java基礎中異常處理機制

一、首先體驗程序中的異常?

1.什麼是異常?

  • 程序在運行中發生了意外的情況,稱爲異常(Exception),程序一旦出現異常,後面的代碼將無法執行,程序終止
  • 爲了保證後面的代碼正常執行,需要對異常進行處理
  • java中採用“類”去模擬異常
  • 類是可以創建對象的
    • 比如NullPointerException e = 0x1234;表示e是引用類型,e中保存的內存地址指向堆中的“對象”,這個對象一定是NullPointerException類型
    • 這個對象就表示真實存在的異常事件
    • NullPointerException是一類異常(不同的異常對應不同的類)

2.異常小案例

package 異常機制;

public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;

        int c = a/b;
        //第8行的代碼出現了異常,“沒有處理”,下面的代碼不會執行,直接退出了JVM
        System.out.println("Hello World");
    }
}

  • 控制檯輸出結果
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at 異常機制.Test.main(Test.java:8)
  • 上面代碼,可以看出,以上程序編譯通過了,但是運行時出現了異常,表示發生某個異常事件
  • JVM向控制檯輸出如下的信息,本質是:程序執行過程中發生了算數異常這個事件(因爲被除數不能爲0),JVM爲我們創建了一個ArithmeticException類型的對象。並且這個對象中包含了詳細的異常信息,並且JVM將這個對象中的信息輸出到控制檯
  • 由於出現算數異常,又沒有對其進行處理,所以Hello World不會輸出
  • 如果不懂上面異常的本質,可以先學習理解一下類與對象的創建與使用]

3.異常機制的作用?

  • java語言爲我們提供一種完善的異常處理機制
  • 程序發生異常事件之後,爲我們輸出詳細的信息,程序員通過這個信息,可以對程序進行一些處理,使程序更加健壯

二、異常的層次結構

在這裏插入圖片描述

  • 上訴異常處理機制圖示結構:
Object
    Throwable 
       Exception   
            編譯時異常
            運行時異常
       Error
  • Exception
    • 編譯時異常:
      • 所有Exception的直接子類都是“編譯時異常”
      • 該異常是可預期,很有可能發生的,發生的機率比較高
      • 所有的編譯時異常,要求程序員在編寫程序階段,必須對它進行處理,如果不處理的話,編譯無法通過。處理異常有兩種方式:捕捉和聲明拋出。捕捉:try…catch…,聲明拋出就是在方法聲明的位置上使用throws關鍵字拋出異常
    • 運行時異常:
      • 所有RuntimeException的子類都是運行時異常
      • 該類異常不一定發生,發生的機率比較低,如果代碼沒有出現邏輯錯誤,是不會發生
      • 編譯器不要求去處理,程序員在寫代碼時可以不處理
  • Error
    • java程序運行過程中如果出現了錯誤,屬於JVM和系統發生的錯誤,程序員無法解決的問題。例如:StackOverFlowError (一旦線程棧的大小增長超過了允許的內存限制,就會拋出 java.lang.StackOverflowError 錯誤)

二、處理異常的兩種方式

  • 使用throws 拋出異常
  • 使用try…catch處理異常

1.使用throws 拋出異常

1.1 首先看一下要求異常處理的代碼

package 異常機制;

import java.io.FileInputStream;

public class Test02 {

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

    public static void m1() {
        m2();
    }

    private static void m2() {
        m3();
    }

    private static void m3() {
        new FileInputStream("D:/x.txt");
    }
}

  • 以上程序會發現FileInputStream在編譯的時候會報紅色波浪線

  • 看源碼發現FileInputStream這個構造方法在聲明的位置上使用了throws FileNotFoundException(向上拋)
    FileInputStream源碼

  • FileNotFoundException繼承Exception類,所以屬於編譯時異常,要求程序員必須對其進行異常處理(throws拋出或者try…caych)
    層次結構圖

1.2 對上述代碼使用throws拋出異常

在方法聲明的位置上使用throws FileNotFoundException就可以編譯通過了,或者寫IOException,或者寫Exception,寫比FileNotFoundException更寬泛的異常就可以

package 異常機制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest02 {
    public static void main(String[] args) throws FileNotFoundException{
        m1();
        //使用throws處理異常不是真正處理異常而是推卸責任
        //誰調用的就會拋給誰
        //上面的m1方法如果出現了異常,因爲採用的是上拋,給了JVM,JVM遇到這個異常就會退出JVM,下面的這個代碼不會執行
        System.out.println("Hello World!");
    }

    public static void m1() throws FileNotFoundException{
        m2();
    }

    private static void m2() throws FileNotFoundException{
        m3();
    }

    private static void m3() throws FileNotFoundException {
        new FileInputStream("D:/x.txt");
    }
}

  • 上訴程序可以看出,從調用m3方法開始一直向上拋異常,直到m1方法出現了異常,並且採用的是上拋,再拋給main方法,最終拋給了JVM(java虛擬機),JVM遇到這個異常就會退出JVM,下面的"Hello World!"字符串字樣就不會打印輸出
  • 上述程序編譯沒有問題,打印輸出信息如下
    編譯信息如圖
  • 可以看出在程序運行過程中發生了FileNotFoundException類型的異常
  • JVM爲我們創建了一個FileNotFoundException類型的對象
  • 該對象中攜帶以下的信息
  • JVM負責將該對象的信息打印到控制檯
  • 並且JVM停掉了程序的運行

如果對m1方法進行try…catch進行捕捉

try...catch捕捉

  • 打印輸出:
    打印輸出

  • 可以看出,如果對m1方法進行try…catch處理,捕捉到了這個異常並且處理了這個異常,那麼"Hello World!"就會打印輸出

1.3 總結

  • 使用throws處理異常不是真正處理異常而是推卸責任,如果想要處理該異常,使用try…catch進行捕捉,就不會拋給JVM而使程序異常退出

2.使用try…catch處理異常

2.1 語法

try{
    可能出現異常的代碼;
}catch(異常類型1  變量){
    處理異常的代碼;
}catch(異常類型2  變量){
    處理異常的代碼;
}...
  • catch語句塊可以寫多個
  • 但是從上到下執行catch,必須從小類型異常到大類型異常進行捕捉
  • try…catch…中最多執行1個catch語句塊。執行結束之後try…catch…就結束了

2.2 示例代碼

  • read方法:嘗試將字符讀入指定的字符緩衝區,源碼中看到在read構造方法中看到在聲明的位置上使用了throws IOException
    read源碼
package 異常機制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest03 {
    public static void main(String[] args) {
        try {
            //程序執行到此處發生了FileNotFoundException類型的異常
            //JVM會自動創建一個FileNotFoundException類型的對象,將該對象的內存地址賦值給catch語句塊中的e變量
            FileInputStream fis = new FileInputStream("D:/a.txt");
            //上面的代碼出現了異常,try語句塊的代碼不再繼續執行,直接進入catch語句塊中執行
            System.out.println("gggggggggggggggg");
            fis.read();
        } catch (FileNotFoundException e) { //e內存地址指向堆中的那個對象是“FileNotFoundException類型的”事件
            System.out.println("讀取的文件不存在");
            //FileNotFoundException將Object中的toString方法重寫
            System.out.println(e);  //java.io.FileNotFoundException: D:a.txt (系統找不到指定的文件。)
        } catch (IOException e) {
            System.out.println("其他IO異常");
        }
        System.out.println("aaa");
    }
}

  • 程序執行到讀取特定文件的時候,發生了FileNotFoundException類型的異常
  • JVM會自動創建一個FileNotFoundException類型的對象,將該對象的內存地址賦值給catch語句塊中的e變量
  • e內存地址指向堆中的那個對象是“FileNotFoundException類型的”事件
  • FileNotFoundException將Object中的toString方法重寫
  • 控制檯輸出信息:
讀取的文件不存在
java.io.FileNotFoundException: D:a.txt (系統找不到指定的文件。)
aaa

2.3 總結

  • try{} 不能單獨存在,後面必須跟上 catch 或者 finally , catch可以有多個

三、關於printStackTrace和getMessage方法的應用

  • 如何取得異常對象的具體信息,常用的方法主要有兩種
    • 取得異常的堆棧信息(比較適合於程序調式階段),printStackTrace()
    • 取得異常描述信息:getMessage()

1.printStackTrace方法

package 異常機制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest04 {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("D:a.txt");
        } catch (FileNotFoundException e) {
            //打印異常堆棧信息
            //一般情況下都會使用該方式去調式程序
            e.printStackTrace();

//            String message = e.getMessage();
//            System.out.println(message);    //D:a.txt (系統找不到指定的文件。)
        }
        System.out.println("abc");

    }
}

  • 控制檯輸出
java.io.FileNotFoundException: D:a.txt (系統找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at 異常機制.ExceptionTest04.main(ExceptionTest04.java:9)
abc

2.getMessage方法

  • 將上面13行代碼註釋掉,15、16行代碼打開,運行代碼
  • 控制檯輸出
D:a.txt (系統找不到指定的文件。)
abc

3.總結

  • 從上述兩個打印信息看出,printStackTrace打印異常信息比較詳細,getMessage打印的異常信息較爲簡潔,printStackTrace打印的信息包含了getMessage打印出來的信息,所以一般使用printStackTrace來打印異常信息

四、finally語句塊

  • finally語句塊中可以直接和try語句塊聯用 try…finally…
  • try…catch…finally也可以
  • 在finally語句塊中的代碼是一定會執行的
  • 只要在執行finally語句塊之前退出了JVM,則finally語句塊就不會執行
  • 通常在程序中爲了保證某資源一定會釋放,所以一般在finally語句塊中釋放資源
package 異常機制;

public class ExceptionTest06 {
    public static void main(String[] args) {

        int i = m1();
        System.out.println(i);  //10
    }

    public static int m1(){
        int i = 10;
        try{
            return i;
        }finally {
            i++;
            System.out.println("m1的i="+i);  //m1的i=11
        }
    }
}

  • 輸出結果
m1的i=11
10
  • return i;分開來可以寫成int temp = i; return temp;,操作的是int i = 10;
  • finally語句塊中的i++;操作的是int i = 10;
package 異常機制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest07 {
    public static void main(String[] args) {
        //必須在外邊聲明
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("D:a.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if (fis!=null){
                try{
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

  • 流是個資源,爲了保證資源一定會釋放,用完我們要關閉
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章