【JavaSE學習筆記04】異常
Chapter 9 異常
異常:指程式在執行過程中,出現的非正常的情況,最終導致JVM非正常停止。
在Java等面向物件的程式語言中,異常是一個類,所有異常都是發生在執行階段的(因為也只有程式執行階段方可new
物件),產生異常其實就是建立異常物件。而Java處理異常的方式為中斷處理。
9.1 異常體系
如下的UML圖關於異常的繼承結構,異常的根類為java.lang.Throwable
(顧名思義,所有異常均可丟擲),其下有兩個子類:java.lang.Error
與 java.lang.Exception
。而我們平常所說的異常指的是後者。
- 錯誤(Error):Java程式執行過程中若發生錯誤,則無法恢復,只能夠退出。
- 編譯時異常(CheckException,或稱受控異常、檢查異常):出現了這種型別的異常必須顯式地處理,若不處理則Java程式將無法編譯通過。
- 執行時異常(RuntimeException,或稱UncheckedException,未檢查異常,非受控異常):此種異常發生機率低,可以不用顯式地處理(eg: 算術異常),也能夠編譯通過。
異常資訊的獲取:
Throwable
類中定義了一些檢視方法:
-
public String getMessage()
:獲取異常的描述資訊與原因,多用於向用戶提示錯誤的原因。 -
public void printStackTrace()
:列印異常的跟蹤棧資訊並輸出至控制檯。(開發與除錯階段常用)
9.2 異常處理機制
Java異常的作用是增強程式健壯性。
9.2.1 宣告異常throws
Java的一個方法不僅需要告訴編譯器將要返回什麼值,還需要告訴編譯器有可能發生什麼錯誤。
一個方法必須宣告所有可能丟擲的已檢查異常(CheckedException),而未檢查異常要麼是不可控制的Error,要麼是應該避免發生的未檢查異常(RuntimeException)。
故,方法應在其首部用throws
宣告可能發生的異常(但不一定會發生),其格式為:
修飾符 返回值型別 方法名(引數) throws 異常類名1, 異常類名2 ...{ }
-
例如:
public FileInputStream(String name) throws FileNotFoundException
這個宣告表示該構造器由
String
引數產生一個FileInputStream
物件,但也有可能丟擲一個FileNotFoundException
類物件。若該方法真的丟擲了這樣一個異常物件,執行時系統就會開始搜尋異常處理器,以便知道如何處理FileNotFoundException
物件。
在Java中,沒有throws
說明符的方法將不能丟擲任何已檢查異常,它是可以單獨使用的。一旦對該方法進行throws
宣告,必須交由呼叫該方法的上一級方法的語句來處理(捕獲 或 繼續宣告),若呼叫者不進行處理,編譯一般不能通過!
執行時異常被丟擲可以不處理,即允許不捕獲也不宣告丟擲。
注意:Java中異常發生後若一直往上拋,最終拋給
main
方法,main
方法繼續向上拋,拋給呼叫者JVM,JVM知道這個異常發生後會終止Java程式執行。
9.2.2 丟擲異常throw
在Java中,提供throw
關鍵字,用在方法內,丟擲一個異常物件,將該異常物件傳遞到呼叫者處,並直接結束當前方法的執行。
格式為throw new 異常類名(引數);
或者 異常類 異常物件名 = new 異常類名(引數); throw 異常物件名
。
String readData(Scanner in) throws EOFException{ //宣告可能出現的異常
//...
while(...){
if(!in.hasNext()){
...
if(n < len) throw new EOFException(); //建立異常物件,並將其丟擲
}
}
return s;
}
丟擲異常的4種情況:
- 呼叫一個丟擲已檢查異常的方法,例如,
FileInputStream
構造器。 - 程式執行過程中發現錯誤,並且利用
throw
語句丟擲一個已檢查異常。 - 程式出現錯誤。例如,
a[-1] = 0;
。 - Java虛擬機器和執行時庫出現的內部異常。
一般而言,
throw
語句不能單獨使用,它應與throws
宣告配套使用。
我們一般是使用一次捕獲 多次處理方式,格式如下:
try{
//編寫可能會出現異常的程式碼
} catch(異常型別A e){
//處理A型別異常
} catch(異常型別B e){
//處理B型別異常
}
注意,這種異常處理方式,要求多個catch
中異常不能相同,且catch
中的多個異常之間有子父類異常的關係,那麼子類異常要求在上面的catch
處理,父類異常在下面的catch
處理。
9.2.3 捕獲異常try-catch
捕獲異常:Java中對異常有針對性的語句進行捕獲,可以對出現的異常進行制定方式的處理。要想捕獲一個異常,必須設定try...catch
語句塊。語法格式如下:
try{
//編寫可能產生異常的程式碼。(一般呼叫方法,該方法聲明瞭異常並能夠對異常丟擲)
} catch(異常型別 異常名){
//處理異常的程式碼:記錄日誌/列印異常資訊/繼續丟擲異常
}
如果在
try
語句塊中任何程式碼丟擲一個在catch
子句中說明的異常類,那麼:
- 程式將跳過
try
語句塊的其餘程式碼;- 程式將執行
catch
子句中的處理器程式碼。
舉例如下:
public class JustTest {
public static void main(String[] args) {
try{
read(b.txt);
} catch(FileNotFoundException e){
System.out.println(e); //try中丟擲的是什麼異常,在括號中就定義什麼異常型別
}
System.out.println("Over!");;
}
public static void read(String path) throws FileNotFoundException{
if(!path.equals("a.txt")){
throw new FileNotFoundException("檔案不存在");
}
}
}
9.2.4 finally 程式碼塊
當代碼丟擲一個異常時,就會終止方法中剩餘程式碼的處理,並退出這個方法的執行。當我們在try
語句塊中開啟一些物理資源(磁碟檔案/網路連線/資料庫連線等),我們需要在使用完之後關閉已開啟的資源。注意,finally
不能單獨使用。
public class JustTest {
public static void main(String[] args) {
try{
read(b.txt);
} catch(FileNotFoundException e){
System.out.println(e); //try中丟擲的是什麼異常,在括號中就定義什麼異常型別
} finally {
System.out.println("不管程式如何,我會被執行的~");
}
System.out.println("Over!");;
}
public static void read(String path) throws FileNotFoundException{
if(!path.equals("a.txt")){
throw new FileNotFoundException("檔案不存在");
}
}
}
finally
子句中的程式碼是一定會執行的!如果try
語塊有返回值,finally
子句也會執行完再最後返回(除非使用System.exit(0);
則會中斷finally
子句的執行)
public class JustTest {
public static void main(String[] args) {
int f = Judge();
System.out.println(f);
}
public static int Judge() {
try{
System.out.println("11111");
return 0;
} finally {
System.out.println("233333");
}
}
}
finally
語句塊設計的目的僅是為了讓方法執行一些重要的收尾工作,而不是用來計算返回值的,故在finally
語句塊中修改返回值是無效的!同時不建議在finally
語句塊中返回一些值。
關於final
、finally
、finalize
的區別
final
屬於關鍵字,其修飾的類無法繼承、修飾的方法無法覆蓋、修飾的變數不能重新賦值。finally
屬於關鍵字,與try...catch
聯合使用,finally
語句塊中的程式碼是必須執行的。finalize
識別符號,是一個Object
類中的方法名,該方法是由垃圾回收器GC負責呼叫。
9.3 自定義異常
在實際開發中總是有些異常情況是SUN沒有定義的,根據自己業務的異常情況來定義異常類。
自定義異常類的方式:
- 自定義一個編譯時異常類:自定義類,並繼承於
java.lang.Exception
- 自定義一個執行時期的異常類:自定義類,並繼承於
java.lang.RuntimeException
習慣上,定義的類應包含兩個構造器,一個是預設無參構造器,另一個是帶有詳細描述資訊的構造器。
public class RegisterException extends Exception{
public RegisterException(){ //無參構造
}
public RegisterException(String message){
super(message); //超類Throwable的toString方法將會打印出這些詳細資訊,除錯中有用。
}
}