Java-異常機制詳解以及開發時異常設計的原則要求
阿新 • • 發佈:2019-01-10
異常處理的合理性完整性體現了一門語言是否成熟。而Java作為目前最流行的開發語言之一,固然具有一個完善的異常處理機制。本文將對異常處理機制來一次大的總結,後面講解一些原則性問題、開發技巧以及經常遇到的異常。
文章結構:1.異常機制樹的講解; 2.異常處理的五大關鍵字; 3.原則性問題以及開發使用異常技巧;4.異常鏈;5.Java的一些新特性;6.常見的異常類(這個查詢多個文章總結)
一、先來幅異常機制樹圖
在java中,所有的非正常情況都繼承Throwable。而Error和Exception是他的兩個子類,各自又含有一系列的子類,對應一系列的錯誤和異常。這裡可以看出java設計的特性與原則。
Error(錯誤):是程式無法處理的,指與虛擬機器相關的問題(比如:系統崩潰、虛擬機器錯誤)。這些錯誤無法恢復或者捕獲
Exception(異常):是程式本身可處理的。而異常又分為兩大類:Checked異常和Runtime異常(執行時異常)。
Checked異常是指可以在編譯階段內處理的異常,java認為必須顯示處理的異常。處理方式:1.當前方法明確如何處理該異常,就應該捕獲它,在對應的catch中修復 2.當前方法不知道如何處理該異常,應在定義該方法時宣告丟擲該異常。它的缺點:1.Java要求必須顯示捕獲並處理該異常,增加程式設計複雜度 2.在方法顯示宣告丟擲此異常,會導致方法簽名與異常耦合。它的優點 :可在編譯時提醒程式設計師程式碼可能存在的問題,提醒處理該異常。
Runtime異常是指編譯階段無須處理,debug過程處理的異常(因為java編譯器不會檢查它,可以通過編譯)。它的優勢:1.正常程式碼與錯誤處理程式碼的分離;2.保證程式健壯性;3.避免Checked異常的程式設計繁瑣性。
剩餘的異常均是這兩個的子類,這裡不過多描述,每個的異常是不同的,太多了。
二、異常處理的五大關鍵字:
java異常處理機制核心思想:丟擲異常,捕獲異常,處理異常
丟擲異常:當一個方法出現錯誤引發異常時,方法建立異常物件並交付執行時系統,異常物件中包含了異常型別和異常出現時的程式狀態等異常資訊。執行時系統負責尋找處置異常的程式碼並執行。
捕獲異常:在方法丟擲異常之後,執行時系統將轉為尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在呼叫棧中的方法的集合。當異常處理器所能處理的異常型別與方法丟擲的異常型別相符時,即為合適 的異常處理器。執行時系統從發生異常的方法開始,依次回查呼叫棧中的方法,直至找到含有合適異常處理器的方法並執行。當執行時系統遍歷呼叫棧而未找到合適 的異常處理器,則執行時系統終止。同時,意味著Java程式的終止。
處理異常:在異常被捕獲到後,我們需要對可以處理的異常進行處理,在這一級的異常能處理的就在此處理,在這一級處理不了的就讓繼續往上拋異常,交由更上一級去處理。這就是開發的時候的異常處理邏輯樹。
五大關鍵字詳解:
try:作用:裡面放置可能引發異常的程式碼;特點:在try裡面宣告的變數是程式碼塊內的區域性變數,只在try塊內有效,catch塊也不能訪問該變數。變數不是指物理資源
catch:作用:用於處理某一型別的程式碼塊
finally:作用:用於回收在try塊開啟的物理資源,異常機制保證finally會被執行(物理資源:如資料庫連線、網路連線和磁碟檔案等); 特點:1.即使try和catch有return語句也會執行,但是如果try和catch有System.exit(1)語句來退出虛擬機器,就不會執行finally裡面的語句; 2.不要在其中使用return或throw等導致方法終止的語句,否則會導致很多奇怪的情況
Java垃圾回收機制不會回收任何物理資源,回收機制只回收堆記憶體中的物件所佔用的記憶體。所以要在finally去回收
//此處給出一例子
try {
// 可能會發生異常的程式程式碼
} catch (Type1 id1) {
// 捕獲並處理try丟擲的異常型別Type1
} catch (Type2 id2) {
// 捕獲並處理try丟擲的異常型別Type2
} finally {
// 無論是否發生異常,都將執行的語句塊
}
try-catch-finally執行順序:
1)當try沒有捕獲到異常時:try語句塊中的語句逐一被執行,程式將跳過catch語句塊,執行finally語句塊和其後的語句;
2)當try捕獲到異常,catch語句塊裡沒有處理此異常的情況:當try語句塊裡的某條語句出現異常時,而沒有處理此異常的catch語句塊時,此異常將會拋給JVM處理,finally語句塊裡的語句還是會被執行,但finally語句塊後的語句不會被執行;
3)當try捕獲到異常,catch語句塊裡有處理此異常的情況:在try語句塊中是按照順序來執行的,當執行到某一條語句出現異常時,程式將跳到catch語句塊,並與catch語句塊逐一匹配,找到與之對應的處理程式,其他的catch語句塊將不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,執行finally語句塊裡的語句,最後執行finally語句塊後的語句;
throw:作用:用於丟擲一個實際異常,程式自行丟擲異常。可以單獨作為語句使用,丟擲一個具體的異常物件;注意:可以單獨使用,丟擲的不是異常類,而是一個異常例項,且每次只能丟擲一個異常例項!!是一個例項!。
throws:作用:在方法簽名中使用,用於宣告該方法可能丟擲的異常;原理思路:當前方法不知道怎麼處理這一型別的異常,需要交由上一級呼叫者去處理;規則::子類方法宣告丟擲的異常型別應該是父類方法宣告丟擲的異常型別的子類或者與之相同。子類方法宣告丟擲的異常不允許比父類方法宣告丟擲的異常大。
//此處給出的例子包含throws、throw的運用,而且擴充套件了一些東西--自定義異常類(到了專案工程,為了異常層級清晰,層級傳遞,一般需要我們去自定義異常類來實現邏輯層層處理)
//1.分析過程:我們可以先看到我們自定義了一個普通的異常類,然後看到main方法裡面,該類物件使用了一個方法簽名中使用throws的方法,然後他進行了一系列的異常丟擲、捕獲,接著在異常處理丟擲我們的自定義異常類,然後列印異常棧資訊。
//2.一些註解:2.標識可能丟擲的異常: throws 異常類名1,異常類名2
/*.捕獲異常:
try{}
catch(異常類名 y){}
catch(異常類名 y){}
.方法解釋
getMessage() //輸出異常的資訊
printStackTrace() //輸出導致異常更為詳細的資訊
*/
public class AuctionTest
{
private double initPrice = 30.0;
// 因為該方法中顯式丟擲了AuctionException異常,
// 所以此處需要宣告丟擲AuctionException異常
public void bid(String bidPrice)
throws AuctionException
{
double d = 0.0;
try
{
d = Double.parseDouble(bidPrice);
}
catch (Exception e)
{
// 此處完成本方法中可以對異常執行的修復處理,
// 此處僅僅是在控制檯列印異常跟蹤棧資訊。
e.printStackTrace();
// 再次丟擲自定義異常
throw new AuctionException("競拍價必須是數值,"
+ "不能包含其他字元!");
}
if (initPrice > d)
{
throw new AuctionException("競拍價比起拍價低,"
+ "不允許競拍!");
}
initPrice = d;
}
public static void main(String[] args)
{
AuctionTest at = new AuctionTest();
try
{
at.bid("df");
}
catch (AuctionException ae)
{
// 再次捕捉到bid方法中的異常。並對該異常進行處理
System.err.println(ae.getMessage());
}
}
}
class AuctionException extends Exception { // 建立自定義異常類
String message; // 定義String型別變數
public AuctionException (String ErrorMessagr) { // 父類方法
message = ErrorMessagr;
}
補充:自定義異常類的做法:
1.自定義異常:
class 異常類名 extends Exception
{
public 異常類名(String msg)
{
super(msg);
}
}
或者:
class 異常類名 extends Exception
{
String message; // 定義String型別變數
public 異常類名(String ErrorMessagr) { // 父類方法
message = ErrorMessagr;
}
public String getMessage() { // 覆蓋getMessage()方法
return message;
}
}