java學習(三)-異常機制

異常

  • 什麼是異常:

    • 程序運行過程中出現的錯誤
  • 異常總覽:
    在這裏插入圖片描述

  • Throwable:

    • java語言中所有異常或錯誤的超類
    • 兩個子類:
      • Error:錯誤類:是指在程序中了嚴重問題,不改代碼,運行不了
      • Exception:異常類:程序一般問題,可以通過處理規避掉
  • 常見的Exception:

    • NullPointerException:空指針異常
    • IndexOutOfBoundsException:越界異常
    • ClassCastException:類型轉換錯誤
    • RuntimeException:運行期錯誤
    • ConcurrentModificationException:併發修改異常
  • 常見的Error:

    • OutOfMemoryError:內存超限:比如:int[] a = new int[1000000000];

Throwable詳解:

  • java語言中所有異常或錯誤的超類
  • 所有繼承自Throwable的子類(java自帶),都默認使用Throwable的方法(子類基本不實現)
  • 其包括如下方法:
方法 描述
public Throwable() 默認構造函數
public Throwable(String message) 指定消息構造函數,後面該message,可通過getMessage獲取
public Throwable(String message,Throwable cause) 可以由message+舊的Throwable對象,構造新的Throwable
public Throwable(Throwable cause) 由老的構造,生成新的Throwable
public String getMessage() 獲取message(簡易的)
public String getLocalizedMessage() 創建本地化描述,如果不做任何子類實現,其等於getMessage
public Throwable getCause() 返回cause
public Throwable initCause(Throwable cause) 初始化cause,只能初始化一次
public String toString() 詳細描述(jdk文檔說是簡單描述)
public void printStackTrace() 將throwable及其追蹤輸出至標準輸出流
public void printStackTrace(PrintStream s) 將此 throwable 及其追蹤輸出到指定的輸出流。
public void printStackTrace(PrintWriter s) 將此 throwable 及其追蹤輸出到指定的 PrintWriter。

JVM對異常的處理:

  • 示例,比如除0異常:
public class Test4 {
    public static void main(String[] args) {
        int a = 1 / 0;
    }
}
  • 輸出:
// 異常的類,以及原因
Exception in thread "main" java.lang.ArithmeticException: / by zero
//出現異常的位置(行)
    at Test3.testZero(Test3.java:27)  
  • 說明:
    • 異常也是對象(萬物皆對象),JVM發現發生了“除法的除0異常”,將異常封裝爲java.lang.ArithmeticException:new ArithmeticException(“/ by zero")
    • 異常向外拋出,拋到main方法的調用者,即:JVM,此時JVM停止程序,並將異常打印到控制檯

異常的詳細程度:

  • e.printStackTrace() > e.toString() > e.getMessage()
public class Test4 {
    public static void main(String[] args) {
        try {
            div(1, 0);
        } catch (Exception e) {
            System.out.println("==================== e.getMessage:簡要 ====================");
            System.out.println(e.getMessage());
            System.out.println("==================== e.toString:較詳細 ====================");
            System.out.println(e.toString());
            System.out.println("==================== e.getMessage:非常詳細 ====================");
            e.printStackTrace();
        }
    }

    private static int div(int a, int b){
        return a / b;
    }
}
  • 輸出:
==================== e.getMessage:簡要 ====================
/ by zero
==================== e.toString:較詳細 ====================
java.lang.ArithmeticException: / by zero
==================== e.getMessage:非常詳細 ====================
java.lang.ArithmeticException: / by zero
    at Test4.div(Test4.java:16)
    at Test4.main(Test4.java:4)

異常的繼承:

  • 一般繼承如下:並且,基本都使用RuntimeException
/**
 * 一般繼承時,都覆寫空構造器、以及帶有message的構造器
 */
class YRuntimeException extends RuntimeException {
    public YRuntimeException() {
        super();
    }

    public YRuntimeException(String message) {
        super(message);
    }
}


異常的捕獲:

  • 多個異常:
private void testExp() {
    // 方式1:
    try {
        method();
    } catch (NullPointerException | ArithmeticException e) {
        System.out.println(e.toString());
    }
    // 方式2:
    try {
        method();
    } catch (ArithmeticException e) {
        System.out.println(e.toString());
    } catch (NullPointerException e) {
        System.out.println(e.toString());
    }
}
  • 如果多個異常,並且這些異常有父子關係,則父類需要寫到最後,否則編譯失敗
class ExceptionParent extends Exception {
}

class ExceptionSon extends  ExceptionParent {
}

class TestException {
    public static void main(String[] args) {
        try {
            throw new ExceptionParent();
        } catch (ExceptionParent e) {
            // 這樣寫,編譯器會報錯,因爲parent異常已經可以捕獲了
        } catch (ExceptionSon e) {

        }
    }
}

Error的捕獲

  • 因爲Error的Exception是兩個不同的實現類,故捕獲的方式不一樣:
class TestY {
    public static void main(String[] args) {
        try {
            int[] a = new int[1000000000];
            // 這裏會報OutOfMemoryError,只能用Error或者Throwable捕獲
        } catch (Error e) {
            System.out.println("捕獲到異常了");
            System.out.println(e.toString());
        }
    }
}

異常拋出:

  • throw:方法內部,手動拋出異常
  • throws:方法上,聲明本方法拋出的異常類型,調用此方法的必須進行處理
  • 運行時異常:RuntimeException或者其子類:
    • 特點:運行時異常,方法上不需要throws聲明,調用者也不需要處理
class XException extends Exception {
}
class YRuntimeException extends RuntimeException {
}

class TestE {
    // 只需要拋出XEception即可,RuntimeException及其子類不需要拋出
    public void show(int a) throws XException{
        if (a == 0) {
            throw new XException();
        } else {
            throw new YRuntimeException();
        }
    }
}

finally

  • finally中代碼快一定會執行,格式:
    • try catch finally
    • try finally
  • 正常場景:try catch finally都有返回
class TestFinally1 {
    public static void main(String[] args) {
        System.out.println(show());  // 輸出爲3
    }

    private static int show() {
        try {
            return 1;
        } catch (Exception e) {
            return 2;
        } finally {
            return 3;   // finally最終返回的是3
        }
    }
}
* 過程分析:執行到return 1;時,jvm先預留,不直接返回。等到finlly執行完成後,再返回。此時發現finally裏有返回,則按照finally返回,故輸出爲3.
  • 正常場景:finally無返回:
class TestFinally1 {
    public static void main(String[] args) {
        System.out.println(show1());   // 返回爲1
    }

    private static int show1() {
        try {
            return 1;              // 最後返回這裏
        } catch (Exception e) {
            return 2;
        } finally {
            System.out.println("no return);   // 首先,會打印這裏
        }
    }
}

輸出:

no return
1
  • 異常場景:無finally,最終按照catch中返回
private static int showException() {
    try {
        int a = 1 / 0;
        return 1;
    } catch (Exception e) {
        return 2;              // 最終方法返回的是這裏
    } 
}
  • 異常場景:有finally:則按照finally中返回
private static int showException() {
    try {
        int a = 1 / 0;
        return 1;
    } catch (Exception e) {
        return 2;
    } finally {
        return 3;           // 最終返回的是這裏:3
    }
}

注意:這裏邏輯比較繞,但最好不要在finally中寫return

繼承關係中方法的異常處理:

  • 父類方法沒有異常,則子類繼承的方法也不能有:
class A {
    public void show() {
    }
}

class B extends A {
    // 正確寫法:
    @Override
    public void show() {
    }
    // 錯誤寫法:
//    @Override
//    public void show() throws ExceptionSon{
//    }
}
  • 父類方法有異常,則子類:
    • 可以不寫
    • 或者聲明爲該異常的子類(異常的父類會報錯)
class AException extends Exception {
}

class BException extends AException {
}

class A {
    public void show() throws AException{
    }
}

class B extends A {
    // 正確寫法1:不拋異常
//    @Override
//    public void show() {
//    }
    // 正確寫法2:拋出爲父類異常的子類
    @Override
    public void show() throws BException{
    }
    // 錯誤寫法:拋出的異常比父類要高
//    @Override
//    public void show() throws Exception{
//    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章