Java 基礎:異常

目錄:
一、 異常繼承體系
二、 發生異常到時候,程序的執行特征:
三、 異常與錯誤的區別
四、 拋出異常 throw
五、 聲明異常 throws
六、 捕獲異常 try…catch…finally
七、 try…catch…finally 異常處理的組合方式
八、 異常在方法重寫中細節
九、 Throwable 類中的常用方法
十、 try和finally中都有return語句,執行哪一個 return?
十一、 自定義異常


一、 異常繼承體系

  • 異常是程序運行過程中出現的錯誤。Java 把異常當作對象來處理,把異常信息封裝成了一個,并定義一個基類 java.lang.Throwable 作為所有異常的超類。
  • Throwable : 它是所有錯誤與異常的超類(祖宗類),有兩個子類ErrorException
    • Error : 錯誤。程序無法處理的錯誤,比如OutOfMemoryErrorThreadDeath等。這些異常發生時,Java虛擬機(JVM)一般會選擇線程終止。
    • Exception : 異常。程序本身可以處理的異常,程序中應當盡可能去處理這些異常。
      • RuntimeException : 運行期異常, JAVA程序運行過程中出現的問題
      • CheckableException : 編譯時異常或者可檢查異常, 是除了RuntimeExecption(及其子類)之外的所有 Exception
  • 運行時期異常:
    • 方法中拋出運行時期異常,方法定義中無需throws聲明,調用者也無需處理此異常
    • 運行時期異常一旦發生,需要程序人員修改源代碼.
  • 編譯時異常: 必須在編譯前處理,否則無法通過編譯

二、 發生異常到時候,程序的執行特征:

  • 我們的代碼,從發生異常的地方,開始被一分為二
  • 在異常發生之前的代碼,都可以正常運行,之后的代碼不會執行
  • 當異常發生的時候,這個異常被 jvm 所捕獲,并將這個異常的所有相關信息,創建為一個異常對象,然后將該異常對象的信息輸出到控制臺(執行的是虛擬機默認的異常處理代碼)
  • 終止當前程序

三、 異常與錯誤的區別

  • 異常:指程序在編譯、運行期間發生了某種異常(XxxException),我們可以對異常進行具體的處理。若不處理異常,程序將會結束運行。
  • 錯誤:指程序在運行期間發生了某種錯誤(XxxError),Error 錯誤通常沒有具體的處理方式,程序將會結束運行。Error錯誤的發生往往都是系統級別的問題,都是jvm所在系統發生并反饋給jvm的。我們無法針對處理,只能修正代碼。

四、 拋出異常 throw

  • 在 java 中,提供了一個throw關鍵字,它用來拋出一個指定的(Throwable類型)異常對象.
  • 一般會用于程序出現某種邏輯時,程序員主動拋出某種特定類型的異常
  • 具體步驟:
    • 創建一個異常對象, 封裝一些提示信息(信息可以自己編寫).
    • 通過關鍵字 throw, 將這個異常對象告知給調用者.
    • throw 用在方法內,用來拋出一個異常對象,將這個異常對象傳遞到調用者處,并結束當前方法的執行。
  • 使用格式throw new 異常類名(參數);
throw new NullPointerException("要訪問的arr數組不存在");
throw new ArrayIndexOutOfBoundsException("該索引在數組中不存在,已超出范圍");
//具體范例:
public static void main(String[] args) {
    String s = "abc";
    if(s.equals("abc")) {
        throw new NumberFormatException();
    } else {
        System.out.println(s);
    }
    //function();
}
  • 注意事項
    • 如果拋出的異常對象屬于可檢查的異常,必須在該方法頭部,聲明拋出此異常, 即: throws 要拋出的異常類型
    • 其次,對于拋出可檢查的異常,還必須與方法的異常列表中的異常兼容
    • 如果父類方法聲明了異常列表
      • 子類可以不聲明異常列表
      • 子類方法有自己異常列表時,必須保證,子類的異常列表所包含的異常類型,與父類中所包含的異常類型兼容

五、 聲明異常 throws

  • 聲明:將問題標識出來,報告給調用者
  • throws 是方法可能拋出異常的聲明, 如果定義功能時有問題發生需要報告給調用者。可以通過在方法上使用 throws 關鍵字進行聲明。
  • 對于聲明了會拋出可檢查異常的方法, 就意味著這個方法會產生可檢查異常,所以,一旦調用該方法就必須對該方法做異常處理
  • throws 用于進行異常類的聲明,若該方法可能有多種異常情況產生,那么在 throws 后面可以寫多個異常類用逗號隔開
  • 聲明異常格式修飾符 返回值類型 方法名(參數) throws <異常列表> { }
public static void function() throws NumberFormatException{
        String s = "abc";
        System.out.println(Double.parseDouble(s));
    }

    public static void main(String[] args) {
        try {
            function();
        } catch (NumberFormatException e) {
            System.err.println("非數據類型不能轉換。");
        }
}

六、 捕獲異常 try…catch…finally

1. 概述
  • 捕獲:Java中對異常有針對性的語句進行捕獲,可以對出現的異常進行指定方式的處理
  • 捕獲異常格式
try {
    //需要被檢測的語句。
}
catch(異常類 e) { //try中拋出的是什么異常,在括號中就定義什么異常類型。
    //異常的處理語句。
}
finally {
    //一定會被執行的語句。
}
//try:該代碼塊中編寫可能產生異常的代碼。
//catch:用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。
//finally:有一些特定的代碼無論異常是否發生,都需要執行。另外,因為異常會引發程序跳轉,導致有些語句執行不到。而finally就是解決這個問題的,在finally代碼塊中存放的代碼都是一定會被執行的。
  • 某個函數或某段程序塊不管會不會,有沒可能拋出異常,都可以加try{...}catch{...}去捕捉它。
2. 異常處理流程:
  1. 首先,當異常在 try 代碼塊中發生的時候,虛擬機首先捕獲這個異常,創建一個異常對象(包含本次異常的所有詳細信息)
  2. 虛擬機會把這個異常,拋出給catch代碼塊(類似于方法調用,虛擬機會調用catch代碼塊中,處理異常的代碼)
  3. 執行catch代碼塊,中的處理異常的代碼
  4. 沒有終止我們應用程序,而是從catch語句之后的代碼開始,繼續執行我們的應用程序

在try語句塊中,一旦發生異常,我們的代碼仍然被一分為二

  1. 在異常發生之前帶代碼都正常運行
  2. 在異常發生之后的代碼,不會執行,而是直接跳轉到我們自己處理異常的catch代碼塊中

七、 try…catch…finally 異常處理的組合方式

  • try catch finally組合: 檢測異常,并傳遞給 catch 處理,并在 finally 中進行資源釋放。

即使在finally代碼塊執行了return語句,finally代碼塊中的代碼,仍然會執行
特殊情況: 走到finally之前 JVM 退出了,就不會執行finally了
finally在開發中的應用:用于釋放資源

  • try catch組合 : 對代碼進行異常檢測,并對檢測的異常傳遞給catch處理。對異常進行捕獲處理
  • 多個try catch組合 當可能有多種類型的異常發生的時候,我把可能產生某異常類型的代碼分開,分別放在不同的try-catch代碼塊中
  • 執行特征是:
    • 每個try-catch中是否拋出異常,相互獨立的
    • 在實際執行的時候,每一個try-catch代碼塊可能都產生異常。
  • 實際開發的時候,一般來講:
    • 效率角度,try 塊中方的代碼越少越好
    • 開發的時候,會將相關的異常,放在同一個 try 塊
      • 相關異常: 一個連續的流程中,可能發生的一系列異常
      • 所以一系列相關的流程中,一旦前一步出現了異常,就會導致,即使后面的流程正常執行,其實也已經沒有意義了
  • 一個try 多個catch組合 : 對代碼進行異常檢測,并對檢測的異常傳遞給catch處理。對每種異常信息進行不同的捕獲處理

這種異常處理方式,要求多個catch中的異常不能相同
catch中的多個異常之間有子父類異常的關系,那么先寫子類異常類型,再寫父類異常類型
當發生異常的時候, 最多執行一個catch分支的代碼

public class Demo {
    public static void main(String[] args) {
        try{
            int i = 10;
            //可能發生異常的語句
            int j = i / 0;
            System.out.println("try after exception");
            //空指針異常
            int[] a = null;
            System.out.println(a[0]);
            //數組越界異常
            int[] b = {1, 2, 3};
            System.out.println(b[3]);
        }catch (ArithmeticException e) {
            System.out.println("發生了除0異常");
        } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { //一個catch分支處理多種類型的異常
            System.out.println("發生了數組異常");
        }
    }
}
  • try finally 組合: 對代碼進行異常檢測,檢測到異常后因為沒有catch,所以一樣會被默認 jvm 拋出。異常是沒有捕獲處理的。但是功能所開啟資源需要進行關閉,所以 finally 只為關閉資源

八、 異常在方法重寫中細節

  • 子類覆蓋父類方法時,如果父類的方法聲明異常,子類只能聲明父類異常或者該異常的子類,或者不聲明
class Fu {
    public void method () throws RuntimeException {
    }
}

class Zi extends Fu {
    public void method() throws RuntimeException { }  //拋出父類一樣的異常
    //public void method() throws NullPointerException{ } //拋出父類子異常
}
  • 當父類方法聲明多個異常時,子類覆蓋時只能聲明多個異常的子集
class Fu {
    public void method () throws NullPointerException, ClassCastException{
    }
}
class Zi extends Fu {
    public void method()throws NullPointerException, ClassCastException { }
    public void method() throws NullPointerException{ } //拋出父類異常中的一部分
    public void method() throws ClassCastException { } //拋出父類異常中的一部分
}
  • 當被覆蓋的方法沒有異常聲明時,子類覆蓋時無法聲明異常的。

九、 Throwable類中的常用方法

  • getCause():返回拋出異常的原因,即異常提示信息。如果 cause 不存在或未知,則返回 null。
  • getMessage():返回異常的消息信息,即該異常的名稱與詳細信息字符串
  • printStackTrace():在控制臺輸出該異常的名稱與詳細信息字符串、異常出現的代碼位置

十、 try和finally中都有return語句,執行哪一個return?

  • 首先要確定的一點是,不管有木有出現異常,finally塊中代碼都會執行
  • 當try和catch中有return時,finally仍然會執行;
  • finally是在return后面的表達式運算后執行的(此時并沒有返回運算后的值,而是先把要返回的值保存起來,不管finally中的代碼怎么樣,返回的值都不會改變,任然是之前保存的值),所以函數返回值是在finally執行前確定的;
  • finally中最好不要包含return,否則程序會提前退出,返回值不是try或catch中保存的返回值。

問:如果try和finally語句里面都有return,會執行哪一個呢?
首先,在程序沒有異常的情況下,首先執行到try里面的語句,但是只執行到了return里面的****expression,expression首先存放在操作數棧頂,然后復制到局部變量區,并沒有執行返回語句return(執行返回語句通常意味著程序執行結束)。然后執行finally,當執行到finally里面的return時候,會將return語句執行完整,此時程序已經有了返回值,因為,執行結束。

  • 總結:執行try塊,執行到return語句時,先執行return的語句,但是不返回到main 方法,接下來執行finally塊,遇到finally塊中的return語句,執行,并將值返回到main方法,這里就不會再回去返回try塊中計算得到的值

十一、 自定義異常

1. 概述
  • 如果Java沒有提供你需要的異常,則可以自定義異常類。
  • 編譯時異常繼承 Exception,運行時異常繼承 RuntimeException
  • 格式:
Class 異常名 extends Exception{ //或繼承RuntimeException
    public 異常名(){
    }
    public 異常名(String s){
        super(s);
    }
}
2. 示例

需求描述:
定義Person類,包含name與age兩個成員變量。
在Person類的有參數構造方法中,進行年齡范圍的判斷,若年齡為負數或大于200歲,則拋出NoAgeException異常,異常提示信息“年齡數值非法”。
要求:在測試類中,調用有參數構造方法,完成Person對象創建,并進行異常的處理。

//自定義異常類
class NoAgeException extends Exception{
    NoAgeException() {
        super();
    }

    NoAgeException(String message)  {
        super(message);
    }
}

//Person類
class Person{
    private String name;
    private int age;
    Person(String name,int age) throws NoAgeException   {
        //加入邏輯判斷
        if(age<0 || age>200)        {
            throw new NoAgeException(age+",年齡數值非法");
        }
        this.name = name;
        this.age = age;
    }
    //定義Person對象對應的字符串表現形式。覆蓋Object中的toString方法。
    public String toString()    {
        return "Person[name="+name+",age="+age+"]";
    }
}

//測試類
class ExceptionDemo{
    public static void main(String[] args) {
        try {
            Person p = new Person("xiaoming",20);
            System.out.println(p);
        }
        catch (NoAgeException ex){
            System.out.println("年齡異常啦");
        }
        System.out.println("over");
    }
}

總結一下,構造函數到底拋出這個NoAgeException是繼承Exception呢?還是繼承RuntimeException呢?
繼承Exception,必須要throws聲明,一聲明就告知調用者進行捕獲,一旦問題處理了調用者的程序會繼續執行。
繼承RuntimeExcpetion,不需要throws聲明的,這時調用是不需要編寫捕獲代碼的,因為調用根本就不知道有問題。一旦發生NoAgeException,調用者程序會停掉,并有jvm將信息顯示到屏幕,讓調用者看到問題,修正代碼。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容