溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java異常處理機制的示例分析

發(fā)布時間:2022-01-19 09:10:05 來源:億速云 閱讀:145 作者:小新 欄目:開發(fā)技術

這篇文章給大家分享的是有關Java異常處理機制的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

    1.初識異常

    我們在寫代碼的時候都或多或少碰到了大大小小的異常,例如:

    public class Test {
        public static void main(String[] args) {
            int[] arr = {1,2,3};
            System.out.println(arr[5]);
        }
    }

    當我們數(shù)組越界時,編譯器會給我們報數(shù)組越界,并提示哪行出了錯。

    Java異常處理機制的示例分析

     再比如:

    class Test{    
        int num = 10;
        public static void main(String[] args) {
            Test test = null;
            System.out.println(test.num);
        }
    }

    當我們嘗試用使用空對象時,編譯器也會報空指針異常:

    Java異常處理機制的示例分析

    那么究竟什么是異常?

    所謂異常指的就是程序在 運行時 出現(xiàn)錯誤時通知調(diào)用者的一種機制 .

    關鍵字 "運行時" ,有些錯誤是這樣的, 例如將 System.out.println 拼寫錯了, 寫成了

    system.out.println. 此時編譯過程中就會出 錯, 這是 "編譯期" 出錯.

    而運行時指的是程序已經(jīng)編譯通過得到 class 文件了 , 再由 JVM 執(zhí)行過程中出現(xiàn)的錯誤 .  

    2.異常的基本用法

    Java異常處理依賴于5個關鍵字:try、catch、finally、throws、throw。下面來逐一介紹下。

    ①try:try塊中主要放置可能會產(chǎn)生異常的代碼塊。如果執(zhí)行try塊里的業(yè)務邏輯代碼時出現(xiàn)異

    常,系統(tǒng)會自動生成一個異常對象,該異常對象被提交給運行環(huán)境,這個過程被稱為拋出

    (throw)異常。Java環(huán)境收到異常對象時,會尋找合適的catch塊(在本方法或是調(diào)用方

    法)。

    ②catch: catch 代碼塊中放的是出現(xiàn)異常后的處理行為,也可以寫此異常出錯的原因或者打

    印棧上的錯誤信息。但catch語句不能為空,因為一旦將catch語句寫為空,就代表忽略了此

    異常。如:

    Java異常處理機制的示例分析

    空的catch塊會使異常達不到應有的目的,即強迫你處理異常的情況。忽略異常就如同忽略

    火警信號一樣——若把火警信號關掉了,當真正的火災發(fā)生時,就沒有人能看到火警信號

    了?;蛟S你會僥幸逃過一劫,或許結(jié)果將是災難性的。每當見到空的catch塊時,我們都應該

    警鐘長鳴。

    當然也有一種情況可以忽略異常,即關閉fileinputstream(讀寫本地文件)的時候。因為你還

    沒有改變文件的狀態(tài),因此不必執(zhí)行任何恢復動作,并且已經(jīng)從文件中讀取到所需要的信

    息,因此不必終止正在進行的操作。

    ③finally:finally 代碼塊中的代碼用于處理善后工作, 會在最后執(zhí)行,也一定會被執(zhí)行。當遇

    到try或catch中return或throw之類可以終止當前方法的代碼時,jvm會先去執(zhí)行finally中的語

    句,當finally中的語句執(zhí)行完畢后才會返回來執(zhí)行try/catch中的return,throw語句。如果

    finally中有return或throw,那么將執(zhí)行這些語句,不會在執(zhí)行try/catch中的return或throw語

    句。finally塊中一般寫的是關閉資源之類的代碼。但是我們一般不在finally語句中加入return

    語句,因為他會覆蓋掉try中執(zhí)行的return語句。例如:

    Java異常處理機制的示例分析

    finally將最后try執(zhí)行的return 10覆蓋了,最后結(jié)果返回了20.

    ④throws:在方法的簽名中,用于拋出此方法中的異常給調(diào)用者,調(diào)用者可以選擇捕獲或者

    拋出,如果所有方法(包括main)都選擇拋出(或者沒有合適的處理異常的方式,即異常類

    型不匹配)那么最終將會拋給JVM,就會像我們之前沒使用try、catch語句一樣。JVM打印出

    棧軌跡(異常鏈)。

    ⑤throw:用于拋出一個具體的異常對象。常用于自定義異常類中。

    ps:

    關于 "調(diào)用棧",方法之間是存在相互調(diào)用關系的, 這種調(diào)用關系我們可以用 "調(diào)用棧" 來描述.

    在 JVM 中有一塊內(nèi)存空間稱為 "虛擬機棧" 專門存儲方法之間的調(diào)用關系. 當代碼中出現(xiàn)異常

    的時候, 我們就可以使用 e.printStackTrace() 的方式查看出現(xiàn)異常代碼的調(diào)用棧,一般寫在catch語句中。

    異常處理流程

    • 程序先執(zhí)行 try 中的代碼

    • 如果 try 中的代碼出現(xiàn)異常, 就會結(jié)束 try 中的代碼, 看和 catch 中的異常類型是否匹配.

    • 如果找到匹配的異常類型, 就會執(zhí)行 catch 中的代碼

    • 如果沒有找到匹配的異常類型, 就會將異常向上傳遞到上層調(diào)用者.

    • 無論是否找到匹配的異常類型, finally 中的代碼都會被執(zhí)行到(在該方法結(jié)束之前執(zhí)行).

    • 如果上層調(diào)用者也沒有處理的了異常, 就繼續(xù)向上傳遞.

    • 一直到 main 方法也沒有合適的代碼處理異常, 就會交給 JVM 來進行處理, 此時程序就會異常終止.

    3.為什么要使用異常?

    存在即合理,舉個例子

     //不使用異常
            int[] arr = {1, 2, 3};
     
            System.out.println("before");
     
            System.out.println(arr[100]);
     
            System.out.println("after");

    當我們不使用異常時,發(fā)現(xiàn)出現(xiàn)異常程序直接崩潰,后面的after也沒有打印。

    Java異常處理機制的示例分析

       //使用異常
            int[] arr = {1, 2, 3};
     
            try {
     
                System.out.println("before");
     
                System.out.println(arr[100]);
     
                System.out.println("after");
     
            } catch (ArrayIndexOutOfBoundsException e) {
                //	打印出現(xiàn)異常的調(diào)用棧
     
                e.printStackTrace();
     
            }
     
            System.out.println("after try catch");

    當我們使用了異常,雖然after也沒有執(zhí)行,但程序并沒有直接崩潰,后面的sout語句還是執(zhí)行了

    Java異常處理機制的示例分析

    這不就是異常的作用所在嗎?

    再舉個例子,當玩王者榮耀時,突然斷網(wǎng),他不會讓你直接程序崩潰吧,而是給你斷線重連的機會吧:

    Java異常處理機制的示例分析

    我們再用偽代碼演示一把王者榮耀的對局過程:

    不使用異常處理
    boolean ret = false;
     
    ret = 登陸游戲();
     
    if (!ret) {
     
    處理登陸游戲錯誤;
     
    return;
     
    }
     
    ret = 開始匹配();
     
    if (!ret) {
     
    處理匹配錯誤;
     
    return;
     
    }
    ret = 游戲確認();
     
    if (!ret) {
     
    處理游戲確認錯誤;
     
    return;
     
    }
    ret = 選擇英雄();
     
    if (!ret) {
     
    處理選擇英雄錯誤;
     
    return;
     
    }
     
    ret = 載入游戲畫面();
     
    if (!ret) {
     
    處理載入游戲錯誤;
     
    return;
     
    }
     
    ......
    使用異常處理
    try {
     
    登陸游戲();
     
    開始匹配();
     
    游戲確認();
     
    選擇英雄();
     
    載入游戲畫面();
     
    ...
     
    } catch (登陸游戲異常) {
     
    處理登陸游戲異常;
     
    } catch (開始匹配異常) {
     
    處理開始匹配異常;
     
    } catch (游戲確認異常) {
     
    處理游戲確認異常;
     
    } catch (選擇英雄異常) {
     
    處理選擇英雄異常;
     
    } catch (載入游戲畫面異常) {
     
    處理載入游戲畫面異常;
     
    }
    ......

    我們能明顯的看到不使用異常時,正確流程和錯誤處理代碼混在一起,不易于分辨,而用了

    異常后,能更易于理解代碼。

    當然使用異常的好處還遠不止于此,我們可以在try、catch語句中加入信息提醒功能,比如你

    開發(fā)了一個軟件,當那個軟件出現(xiàn)異常時,發(fā)個信息提醒你及時去修復。博主就做了一個小

    小的qq郵箱信息提醒功能,源碼在碼云,有興趣的可以去看看呀!需要配置qq郵箱pop3服

    務,友友們可以去查查怎么開啟呀,我們主旨不是這個所以不教怎么開啟了。演示一下:

    Java異常處理機制的示例分析

    別群發(fā)消息哦,不然可能會被封號???

    異常應只用于異常的情況

    try{
       int i = 0;
       while(true)
           System.out.println(a[i++]);
    }catch(ArrayIndexOutOfBoundsException e){
     }

    這段代碼有什么用?看起來根本不明顯,這正是它沒有真正被使用的原因。事實證明,作為

    一個要對數(shù)組元素進行遍歷的實現(xiàn)方式,它的構(gòu)想是非常拙劣的。當這個循環(huán)企圖訪問數(shù)組

    邊界之外的第一個數(shù)組元素時,用拋出(throw)、捕獲(catch)、

    忽略(ArrayIndexOutOfBoundsException)的手段來達到終止無限循環(huán)的目的。假定它與數(shù)

    組循環(huán)是等價的,對于任何一個Java程序員來講,下面的標準模式一看就會明白:

    for(int m : a)
       System.out.println(m);

    為什么優(yōu)先異常的模式,而不是用行之有效標準模式呢?

    可能是被誤導了,企圖利用異常機制提高性能,因為jvm每次訪問數(shù)組都需要判斷下標是否越

    界,他們認為循環(huán)終止被隱藏了,但是在foreach循環(huán)中仍然可見,這無疑是多余的,應該避

    免。

    上面想法有三個錯誤:

    1.異常機制設計的初衷是用來處理不正常的情況,所以JVM很少對它們進行優(yōu)化。

    2.代碼放在try…catch中反而阻止jvm本身要執(zhí)行的某些特定優(yōu)化。

    3.對數(shù)組進行遍歷的標準模式并不會導致冗余的檢查。

    這個例子的教訓很簡單:顧名思義,異常應只用于異常的情況下,它們永遠不應該用于正常

    的控制流。

    總結(jié):異常是為了在異常情況下使用而設計的,不要用于一般的控制語句。

    4. 異常的種類

    在Java中提供了三種可拋出結(jié)構(gòu):受查異常(checked exception)、運行時異常(run-time exception)和錯誤(error)。

    Java異常處理機制的示例分析

    Java異常處理機制的示例分析

    (補充)

     4.1 受查異常

    什么是受查異常?只要不是派生于error或runtime的異常類都是受查異常。舉個例子:

    我們自定義兩個異常類和一個接口,以及一個測試類

    interface IUser {
        void changePwd() throws SafeException,RejectException;
    }
     
    class SafeException extends Exception {//因為繼承的是execption,所以是受查異常類
     
        public SafeException() {
     
        }
     
        public SafeException(String message) {
            super(message);
        }
     
    }
     
    class RejectException extends Exception {//因為繼承的是execption,所以是受查異常類
     
        public RejectException() {
     
        }
        public RejectException(String message) {
            super(message);
        }
    }
     
    public class Test {
        public static void main(String[] args) {
            IUser user = null;
            user.changePwd();
        }
    }

    Java異常處理機制的示例分析

    我們發(fā)現(xiàn)test測試類中user使用方法報錯了,因為java認為checked異常都是可以再編譯階

    段被處理的異常,所以它強制程序處理所有的checked異常,java程序必須顯式處checked

    異常,如果程序沒有處理,則在編譯時會發(fā)生錯誤,無法通過編譯。

    解決方案:

    ①try、catch包裹

     IUser user = null;
            try {
                user.changePwd();
            }catch (SafeException e){
                e.printStackTrace();
            }
            catch (RejectException e){
                e.printStackTrace();
            }

    Java異常處理機制的示例分析

    ②拋出異常,將處理動作交給上級調(diào)用者,調(diào)用者在調(diào)用這個方法時還是要寫一遍try、catch

    包裹語句的,所以這個其實是相當于聲明,讓調(diào)用者知道這個函數(shù)需要拋出異常

    public static void main(String[] args) throws SafeException, RejectException {
            IUser user = null;
            user.changePwd();
        }

    Java異常處理機制的示例分析

    4.2非受查異常

    派生于error或runtime類的所有異常類就是非受查異常。

    可以這么說,我們現(xiàn)在寫程序遇到的異常大部分都是非受查異常,程序直接崩潰,后面的也

    不執(zhí)行。

    Java異常處理機制的示例分析

    像空指針異常、數(shù)組越界異常、算術異常等,都是非受查異常。由編譯器運行時給你檢查出

    來的,所以也叫作運行時異常。

    5.如何使用異常

    避免不必要的使用受查異常

    如果不能阻止異常條件的產(chǎn)生,并且一旦產(chǎn)生異常,程序員可以立即采取有用的動作,這種

    受查異常才是可取的。否則,更適合用非受查異常。這種例子就是

    CloneNotSuppportedException(受查異常)。它是被Object.clone拋出來的,Object.clone

    只有在實現(xiàn)了Cloneable的對象上才可以被調(diào)用。

    Java異常處理機制的示例分析

    Java異常處理機制的示例分析

     被一個方法單獨拋出的受查異常,會給程序員帶來非常高的額外負擔,如果這個方法還有其

    他的受查異常,那么它被調(diào)用是一定已經(jīng)出現(xiàn)在一個try塊中,所以這個異常只需要另外一個

    catch塊。但當只拋出一個受查異常時,僅僅一個異常就會導致該方法不得不處于try塊中,也

    就導致了使用這個方法的類都不得不使用try、catch語句,使代碼可讀性也變低了。

    受查異常使接口聲明脆弱,比如一開始一個接口只有一個聲明異常

    interfaceUser{  
        //修改用戶名,拋出安全異常  
        publicvoid changePassword() throws MySecurityExcepiton; 
    }

    但隨著系統(tǒng)開發(fā),實現(xiàn)接口的類越來越多,突然發(fā)現(xiàn)changePassword還需要拋出另一個異

    常,那么實現(xiàn)這個接口的所有類也都要追加對這個新異常的處理,這個工程量就很大了。

    總結(jié):如果不是非用不可,盡量使用非受查異常,或?qū)⑹懿楫惓^D(zhuǎn)為非受查異常。

    6.自定義異常

    我們用自定義異常來實現(xiàn)一個登錄報錯的小應用

    class NameException extends RuntimeException{//用戶名錯誤異常
        public NameException(String message){
            super(message);
        }
    }
    class PasswordException extends RuntimeException{//密碼錯誤異常
        public PasswordException(String message){
            super(message);
        }
    }

    test類來測試運行

    public class Test {
        private static final String name = "bit";
        private static final String password ="123";
     
        public static void Login(String name,String password) throws NameException,PasswordException{
            try{
                if(!Test.name.equals(name)){
                    throw new NameException("用戶名錯誤!");
                }
            }catch (NameException e){
                e.printStackTrace();
            }
            try {
                if(!Test.password.equals(password)){
                    throw new PasswordException("密碼錯誤");
                }
            }catch (PasswordException e){
                e.printStackTrace();
            }
        }
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            String name = scanner.nextLine();
            String password = scanner.nextLine();
            Login(name,password);
        }
    }

    Java異常處理機制的示例分析

    Java異常處理機制的示例分析

    Java異常處理機制的示例分析

    關于異常就到此為止了,怎么感覺還有點意猶未盡呢?

    感謝各位的閱讀!關于“Java異常處理機制的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI