一、首先體驗程序中的異常?
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(向上拋)
-
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進行捕捉
-
打印輸出:
-
可以看出,如果對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
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();
}
}
}
}
}
- 流是個資源,爲了保證資源一定會釋放,用完我們要關閉