1、異常概述
- 異常機制已經成爲判斷一門編程語言是否成熟的標準,目前主流的編程語言都提供了成熟的異常機制,增加了異常處理機制後的程序有更好的容錯性,更加健壯
- Java的異常機制主要依賴於:try、catch、finally、throws和throw
- Java7進一步增強了異常處理機制的功能,包括帶資源的try語句、捕獲更多異常的catch
- Java將異常分爲兩種,Checked異常和Runtime異常,Checked異常都是可以在編譯階段被處理的異常,程序強制要求處理;而Runtime異常是運行時產生的異常
2、異常處理機制
- 使用try…catch捕獲異常
try{…}catch(Exception e){…}
如果執行try塊裏的業務邏輯代碼時出現異常,系統自動生成一個異常對象,該異常對象被提交給Java運行時環境,這個過程被稱爲拋出(throw)異常
當Java運行時環境收到異常對象時,會尋找能處理該異常對象的catch塊,如果找到合適的則把該異常對象交給該catch塊處理,這個過程被稱爲捕獲異常,否則運行時環境終止,Java程序也將退出
3、異常的繼承關係
- Java把所有非正常情況分成兩種:異常(Exception)和錯誤(Error),它們都是繼承Throwable父類
- Error錯誤,一般是指與虛擬機相關的問題,如系統崩潰、虛擬機錯誤、動態鏈接失敗等,這種錯誤無法恢復或不可能捕獲,將導致應用程序中斷;通常應用程序無法處理這些錯誤,因此也無法捕獲
- 開發中常遇到的內存溢出是屬於錯誤(Error)
第一種:OutOfMemoryError: PermGen space內存空間不足,如一次性加載太多信息,或加載項目太多
第二種:OutOfMemoryError: Java heap spacejava虛擬機創建的對象太多,在進行垃圾回收之間,虛擬機分配的到堆內存空間已經用滿了
第三種:OutOfMemoryError:unable to create new native thread一般很少出現,可能由於分配給JVM內存與系統本身比例問題引起 - Java7可以提供更多異常捕獲
try{…}catch(IndexOutOfBoundsException|NumberFormatException|ArithmeticException ie){…} - 訪問異常信息
getMessage():返回該異常的詳細描述字符串
printStackTrace():將該異常的跟蹤棧信息輸出到標準錯誤輸出
printStackTrace(PrintStream s):將該異常的跟蹤信息輸出到指定輸出流
getStackTrace():返回該異常的跟蹤棧信息
4、finally塊
try{
...
}catch(Exception e){
...
}finally{
...
}
- 不管try塊中的代碼是否出現異常,也不管哪一個catch塊被執行,甚至在try塊或catch塊中執行了return、throw語句,finally塊總是被執行
- 異常處理語法結構中只有try塊是必須的,catch塊和finally塊都是可選的,但兩者至少出現其中之一;可以有多個catch塊,catch塊必須位於try之後,finally必須位於所有catch塊之後;
- 如果在異常處理代碼中使用System.exit(1)語句來退出虛擬機,則finally塊將失去執行的機會
- 在通常情況下,不要在finally塊中使用如return或throw等導致方法終止的語句,否則會導致try塊、catch塊中的return、throw語句失效
5、Java7自動關閉資源的try語句
try(BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));){
System.out.println(br);
} catch (Exception e) {
e.printStackTrace();
}
- Java7增強了try語句的功能,允許在try關鍵字後緊跟一對圓括號聲明、初始化一個或多個資源,try語句在該語句結束時自動關閉這些資源;而且這些資源實現類必須實現AutoCloseable或Closeable接口,實現這兩個接口就必須實現close()方法
- 以上的BufferedReader都是實現了Closeable接口的,即使沒有finally塊也會自動關閉資源
6、Checked異常和Runtime異常體系
- 所有的RuntimeException類及其子類的實例被稱爲Runtime異常;非RuntimeException類及其子類的異常實例則被稱爲Checked異常
- Checked異常處理方式:
當前方法明確知道如何處理該異常,程序應該使用try…catch塊來捕獲該異常,然後再對應的catch塊中修復該異常
當方法不知道如何處理這種異常,應該在定義該方法時拋出該異常 - Runtime異常則無須顯示聲明拋出,程序也可以通過try…catch塊來捕獲
7、使用throws聲明拋出異常
public class ThrowsTest{
public static main(String[] args)throws IOException{
FileInputStream fis = new FileInputStream("a.txt");
}
}
- 當前方法不知道如何處理這種類型異常時可以使用throws聲明拋出異常
- 使用throws聲明拋出異常時有一個限制:子類方法聲明拋出的異常類型應該是父類方法聲明拋出的異常類型的子類或相同,子類方法聲明拋出異常不允許比父類方法聲明拋出的異常多
8、使用throw拋出異常
- 當程序出現錯誤時,系統會自動拋出異常,除此之外,Java也允許程序自行拋出異常,使用throw語句來完成
9、自定義異常類
用戶自定義異常都應該繼承Exception基類,如果系統自定義Runtime異常,則應該繼承RuntimeException基類
public class MyException extends Exception{
public MyException(){}
public MyException(String msg){
super(msg);
}
publc MyException(Throwable t){
super(t);
}
}
10、catch和throw同時使用
爲了實現通過多方協作處理同一個異常的情形,可以在catch塊中結合throw語句來完成,將異常傳遞給下一個程序
Java7可以直接將捕獲的異常直接傳遞:
try{
new FileOutputStream("a.txt");
}catch(Exception ex){
ex.printStackTrack();
throw ex;
}
11、異常鏈
public calSal() throws MyException{
try{
...
}catch(Exception e){
throw new MyException(e);
}
}
12、異常處理規則
- 成功的異常處理應該實現4個目標:
使程序代碼混亂最小化
捕獲並保留診斷信息
通知合適的人員
採用合適的方式結束異常活動 - 避免過度依賴異常:
把異常和普通錯誤混淆在一起,不再編寫任何錯誤處理代碼
使用異常處理來替代流程控制 - 不要使用過於龐大的try塊
應該把大塊的try塊分割成多個可能出現異常的程序段落,並把它們放在單獨的try塊中