JavaSE異常類

Java中的異常(Exception)又稱為例外,是一個在程序執(zhí)行期間發(fā)生的事件,它中斷正在執(zhí)行程序的正常指令流。為了能夠及時有效地處理程序中的運行錯誤讥蔽,必須使用異常類


一、異常產(chǎn)生的原因和分類

異常產(chǎn)生的原因

在Java中異常產(chǎn)生画机,主要是有三種原因:

  • 編寫程序代碼中的錯誤產(chǎn)生的異常冶伞,比如數(shù)組越界、空指針異常等步氏,這種異常叫做未檢查的異常碰缔,一般需要在類中處理這些異常
  • Java內(nèi)部錯誤發(fā)生的異常,Java虛擬機產(chǎn)生異常
  • 通過throw(拋出異常)語句手動生成的異常戳护,這種異常叫做檢查的異常金抡,一般是用來給方法調(diào)用者一些必要的信息
  • Throwable:是異常體系的頂層類,其派生出兩個重要的子類, Error 和 Exception
而 Error 和 Exception 兩子類分別表示錯誤和異常



區(qū)別就是不檢查異常(Unchecked Exception)和檢查異常(Checked Exception)
  • Exception 類用于用戶程序可能出現(xiàn)的異常情況腌且,它也是用來創(chuàng)建自定義異常類型類的類梗肝。
  • Error 定義了在通常環(huán)境下不希望被程序捕獲的異常。Error 類型的異常用于 Java 運行時由系統(tǒng)顯示與運行時系統(tǒng)本身有關(guān)的錯誤铺董。堆棧溢出是這種錯誤的一例

異澄谆鳎可能在編譯時發(fā)生,也有可能在程序運行時發(fā)生精续,根據(jù)發(fā)生時機不同坝锰,可以分為:

  • 運行時異常: RuntimeException 類及其子類異常,如 NullPointerException重付、IndexOutOfBoundsException 等顷级,這些異常是不檢查異常,程序中可以選擇捕獲處理确垫,也可以不處理弓颈。這些異常一般由程序邏輯錯誤引起帽芽,程序應(yīng)該從邏輯角度盡可能避免這類異常的發(fā)生

  • 編譯時異常: RuntimeException 以外的異常,類型上都屬于 Exception 類及其子類翔冀。從程序語法角度講是必須進行處理的異常导街,如果不處理,程序就不能編譯通過纤子。如 IOException搬瑰、ClassNotFoundException 等以及用戶自定義的 Exception 異常,一般情況下不自定義檢查異常


二控硼、異常的處理

2.1 防御式編程

通知有兩種方式:

  • LBYL 在操作之前就做充分的檢查
private static int divide() {

    int a = 0, b = 0;

    Scanner scanner = new Scanner(System.in);
    a = scanner.nextInt();
    b = scanner.nextInt(); 
    if (b == 0) {
        System.out.println("除數(shù)為0");
        return 0;
    } else {
        return a / b;   
    }

}

缺點:正常流程和錯誤處理流程代碼混在一起, 代碼整體條理不清晰

  • EAFP 先操作遇到問題再處理
private static int divide() {

    int a = 0, b = 0;

    try (Scanner scanner = new Scanner(System.in)) {
        a = scanner.nextInt();
        b = scanner.nextInt();
        return a / b;
    } catch (ArithmeticException exception) {
        System.out.println("除數(shù)為0");
        return 0;
    } 
    
}

優(yōu)點:正常流程和錯誤流程是分離開的, 程序員更關(guān)注正常流程泽论,代碼更清晰,容易理解代碼

處理異常的核心思想就是EAFP


2.2 異常的拋出(throw)

在編寫程序時象颖,如果程序中出現(xiàn)錯誤佩厚,這就需要將錯誤的信息通知給調(diào)用者

這里就可以借助關(guān)鍵字throw姆钉,拋出一個指定的異常對象说订,將錯誤信息告知給調(diào)用者

比如寫一個運行時異常

public static void func2(int a) {
    
    if(a == 0) {
        //拋出的是一個指定的異常,最多的使用方式是潮瓶,拋出一個自定義的異常
        throw new RuntimeException("a==0");
    }   
}

public static void main(String[] args) {
    
    func2(0);
    
}

注意:

  • throw必須寫在方法體內(nèi)部
  • 如果拋出的是編譯時異常陶冷,用戶就必須要處理,否則無法通過編譯
  • 如果拋出的運行時異常毯辅,則可以不用處理埂伦,直接交給JVM處理
  • JVM捕獲異常后,后面代碼不會執(zhí)行

2.3 異常的捕獲

2.3.1 throws異常聲明

throws處在方法聲明時參數(shù)列表之后思恐,當(dāng)方法中拋出編譯時異常沾谜,

用戶不想處理該異常,此時就可以借助throws將異常拋 給方法的調(diào)用者來處理胀莹。

格式:

修飾符 返回值類型 方法名(參數(shù)列表) throws 異常類型 {

}

public static void func2(int a) throws CloneNotSupportedException {
    
    if(a == 0) {
        
        throw new CloneNotSupportedException("a==0");
        
    }
    
}

如果說方法內(nèi)部拋出了多個異常基跑,throws之后就必須跟多個異常類型,用逗號進行分隔

public static void func2(int a) throws CloneNotSupportedException, FileNotFoundException {

    if(a == 0) {

        throw new CloneNotSupportedException("a==0");

    }

    if(a == 1) {

        throw new FileNotFoundException();

    }

}

如果拋出多個異常類型有父子關(guān)系描焰,直接聲明父類

public static void func2(int a) throws Exception {

    if(a == 0) {     
        
        throw new CloneNotSupportedException("a==0"); 
        
    }

    if(a == 1) {       
        
       throw new FileNotFoundException();
        
    }

}

調(diào)用聲明拋出異常的方法時媳否,調(diào)用者必須對該異常進行處理,或者繼續(xù)使用throws拋出

public static void main(String[] args) throws FileNotFoundException, CloneNotSupportedException {
    
    func2(0);
    
}

2.3.2 try-catch捕獲異常并處理

如果程序拋出異常荆秦,不處理異常篱竭,那就會交給JVM處理,JVM處理就會把程序立即終止
并且步绸,即使用了 try-catch 也必須捕獲一個對應(yīng)的異常掺逼,如果不是對應(yīng)異常,也會讓JVM進行處理

public static void main(String[] args) {

    try {
        
        int[] array = null;
        System.out.println(array.length);
        
    }catch (NullPointerException e) {

        System.out.println("捕獲到了一個空指針異常瓤介!");

    }

    System.out.println("其他程序坪圾!");

}

如果try拋出多個異常晓折,就必須用多個catch進行捕獲
這里注意,用多個catch進行捕獲兽泄,不是同時進行捕獲的漓概,因為不可能同時拋不同的異常:

public static void main(String[] args) {

    try {
        int[] array = null;
        System.out.println(array.length);
    }catch (NullPointerException e) {
        System.out.println("捕獲到了一個空指針異常!");
    }catch (ArithmeticException e) {
        System.out.println("捕獲到了一個算術(shù)異常病梢!");
    }

    System.out.println("其它代碼邏輯胃珍!");

}

如果異常之間具有父子關(guān)系,那就必須子類異常在前蜓陌,父類異常在后catch觅彰,不然會報錯:

public static void main(String[] args) {
    
    try {
        int[] array = null;
        System.out.println(array.length);
    }catch (NullPointerException e) {
        System.out.println("捕獲到了一個空指針異常!");
    }catch (Exception) {
        System.out.println("捕獲到了一個算術(shù)異常钮热!");
    }
    
    System.out.println("其它代碼邏輯填抬!");
    
}

2.3.3 finally

finally用來進行資源回收,不論程序正常運行還是退出隧期,都需要回收資源

并且異常會引發(fā)程序的跳轉(zhuǎn)飒责,可能會導(dǎo)致有些語句執(zhí)行不到

public static void main(String[] args) {
    
    Scanner scanner = new Scanner(System.in);
    
    try {
        int[] array = null;
        System.out.println(array.length);
    }catch (NullPointerException e) {
        System.out.println("捕獲到了一個空指針異常!");
    }catch (ArithmeticException e) {
        System.out.println("捕獲到了一個算術(shù)異常仆潮!");
    }finally {
        scanner.close();
        System.out.println("進行資源關(guān)閉宏蛉!");
    }
    
    System.out.println("其它代碼邏輯!");
    
}

如果不為空性置,那么finally還會被執(zhí)行嗎?

public static void main(String[] args) {
    
    Scanner scanner = new Scanner(System.in);
    
    try {
        int[] array = {1,2,3};
        System.out.println(array.length);
    }catch (NullPointerException e) {
        System.out.println("捕獲到了一個空指針異常拾并!");
    }catch (ArithmeticException e) {
        System.out.println("捕獲到了一個算術(shù)異常!");
    }finally {
        scanner.close();
        System.out.println("進行資源關(guān)閉鹏浅!");
    }
    
    System.out.println("其它代碼邏輯嗅义!");
    
}

不管程序會不會拋出異常,finally都會執(zhí)行

三隐砸、自定義異常類

雖然java中有很多異常類之碗,但是在實際開發(fā)中所遇到的一些異常,不能完全表示 所以這就需要我們自定義異常類

3.1 自定義一個運行時異常

//自定義了一個運行時異常
public class MyException extends RuntimeException{
    
    public MyException() {}
    
    public MyException(String message) {
        
        super(message);
        
    } 
}

3.2 寫一個類來捕獲這個自定義異常

public class Test04 {
    
    public static void func (int a) {        
        throw new MyException("呵呵凰萨!");        
    }

    public static void main(String[] args) {        
        
        try {
            func(20);
        }catch (MyException myException) {
            myException.printStackTrace();
        }finally {
            System.out.println("sadasdasd");
        }
    
    }
}

3.3 下面寫一個用戶登錄的自定義異常類

class UserNameException extends RuntimeException {
    
    public UserNameException() {}
    
    public UserNameException(String message) {
        super(message);
    }
    
}
class PasswordException extends RuntimeException {

    public PasswordException() {}

    public PasswordException(String message) {
        super(message);
    }
    
}
public class LogIn {
    
    private static String uName = "admin";
    private static String pword = "1111";
 
    public static void loginInfo(String userName, String password) {
        if (!uName.equals(userName)) {
            throw new UserNameException("用戶名錯誤继控!");
        }
        if (!pword.equals(password)) {
            throw new RuntimeException("密碼錯誤!");
        }
        System.out.println("登錄成功胖眷!");
    }
 
    public static void main(String[] args) {
        try {
            loginInfo("admin","1111");
        } catch (UserNameException e) {
            e.printStackTrace();
        } catch (PasswordException e) {
            e.printStackTrace();
        }
    }     
}

注意:

自定義異常默認會繼承 Exception 或者 RuntimeException:

  • 繼承于 Exception 的異常默認是受查異常
  • 繼承于 RuntimeException 的異常默認是非受查異常

四武通、try catch finally 執(zhí)行順序詳解

4.1 finally 語句不會執(zhí)行的四種情況

  • 如果在 try 或 catch 語句中執(zhí)行了System.exit(0)
  • 在執(zhí)行 finally 之前 jvm 崩潰了
  • try 語句中執(zhí)行死循環(huán)
  • 電源斷電

除以上四種情況外,finally 語句都會執(zhí)行

4.2 finally語句執(zhí)行原則

  1. 不管有沒有出現(xiàn)異常珊搀,finally 語句塊中代碼都會執(zhí)行

    public void demo1(){
        try {
            System.out.println(result);
        } catch (Exception e) {                     
            System.out.println(e.getMessage());
        }finally {            
            System.out.println("finally trumps. ");
        }
    }
    
    //輸出結(jié)果為:
    result
    finally trumps.
    

    上面代碼可知如果未出現(xiàn)異常時是順序執(zhí)行 try 和 finally 代碼塊


  2. 當(dāng) try 和 catch 中有 return 時冶忱,finally 仍然會執(zhí)行

    public static int demo2() {
        try {
            return0;
        }
        finally {
            System.out.println("finally trumps return.");
        }
    }
    
    //輸出結(jié)果
    finally trumps return.
    0
    

    上面代碼可知當(dāng) finally 里面沒有 return 語句時,執(zhí)行 try 和 finally 語句之后最后再執(zhí)行 return


  3. finally 是在 return 后面的表達式運算后執(zhí)行的(此時并沒有返回運算后的值境析,而是先把要返回的值保存起來囚枪,管 finally 中的代碼怎么樣派诬,返回的值都不會改變,任然是之前保存的值)链沼,所以函數(shù)返回值是在 finally 執(zhí)行前確定的

    public static int demo3()
    {
        int i = 0;
        try {
            i = 2;
            return i;
        } finally {
            i = 12;
            System.out.println("finally trumps return.");
        }       
    }
    
    //輸出結(jié)果
    finally trumps return.
    2
    

    此處中 finally 中對 i 賦值12但是 demo3 的返回值仍然是2默赂,也就是在 finally 中對i賦值并未改變i的返回值,這里需要詳細的講一下括勺,此處涉及到了jvm機制:


    image.png
  4. 在variable內(nèi)存中有兩個變量區(qū)域一個是用來存放i的值缆八,對應(yīng)最上面的那個,另一個用于存放返回值疾捍。在上面代碼執(zhí)行到 i = 2; return i ;先對 i 賦值2奈辰,然后執(zhí)行 return 語句此時并不是將結(jié)果返回,而是將 i = 2 的值保存到返回值變量區(qū)域乱豆,在執(zhí)行完 i = 12 時奖恰,再返回 variable 中返回值地址變量區(qū)域的2


  5. finally中最好不要包含return,否則程序會提前退出宛裕,返回值不是try或catch中保存的返回值

    public static int demo4() {
        int i = 0;
        try {
            return i;
        } finally {
            i = 12;
            System.out.println("finally trumps return.");
            return i;
        }
    }
    
    //輸出結(jié)果
    finally trumps return.
    12
    

    在程序還未執(zhí)行try中的 return 語句時就先執(zhí)行了 finally 里面的 return 語句瑟啃,所以返回結(jié)果為12

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市续滋,隨后出現(xiàn)的幾起案子翰守,更是在濱河造成了極大的恐慌孵奶,老刑警劉巖疲酌,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異了袁,居然都是意外死亡朗恳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門载绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粥诫,“玉大人,你說我怎么就攤上這事崭庸』辰” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵怕享,是天一觀的道長执赡。 經(jīng)常有香客問我,道長函筋,這世上最難降的妖魔是什么沙合? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮跌帐,結(jié)果婚禮上首懈,老公的妹妹穿的比我還像新娘绊率。我一直安慰自己,他們只是感情好究履,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布滤否。 她就那樣靜靜地躺著,像睡著了一般最仑。 火紅的嫁衣襯著肌膚如雪顽聂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天盯仪,我揣著相機與錄音紊搪,去河邊找鬼。 笑死全景,一個胖子當(dāng)著我的面吹牛耀石,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爸黄,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼滞伟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炕贵?” 一聲冷哼從身側(cè)響起梆奈,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎称开,沒想到半個月后亩钟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡鳖轰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年清酥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕴侣。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡焰轻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昆雀,到底是詐尸還是另有隱情辱志,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布狞膘,位于F島的核電站揩懒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏客冈。R本人自食惡果不足惜旭从,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧和悦,春花似錦退疫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馍忽,卻和暖如春棒坏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遭笋。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工坝冕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓦呼。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓喂窟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親央串。 傳聞我的和親對象是個殘疾皇子磨澡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容