是人都會有犯錯的時候,當程式員出現編程錯誤的時候就產生了異常。
編程錯誤分三類:編譯錯誤;邏輯錯誤;執行階段錯誤。
編譯錯誤:
原因:沒有遵循語言的規則 解決方案:由編譯器檢查發現
邏輯錯誤:
原因:程式沒有按照預期的方案執行
解決方案:利用調試技巧可以解決此類錯誤
執行階段錯誤:
原因:在程式運行過程中,出現了不可能執行的操作
解決方案:異常處理
下面主要介紹下執行階段錯誤和解決方案(異常處理)。
執行階段錯誤(runtime error):程式運行時的一些不正常現象
常見的錯誤(Throwable):除數為0、數組下標越界、檔案不存在、記憶體不夠用等等。
這些錯誤可以分為致命性錯誤和非致命性的異常
致命性的錯誤(Error):程式進入了死迴圈、遞迴無法結束、記憶體溢出等。
這類錯誤只能在編程階段解決,運行時程式本身無法解決。只能依靠其他程式幹預,否則會一直處於非正常狀態。
非致命性的異常(Exception):除數為0、運算元超出資料範圍、檔案並不存在、欲裝入的類檔案丟失、網路連接中斷……這類錯誤是可以檢測和處理的,所以產生了相應的異常處理機制。
——java中預定義了很多異常類,每個異常類代表一種運行錯誤。
一些常見的(Exception):
NullPointerException null 指標異常
ArrayIndexOutOfBoundsException 數組越界訪問異常
ClassNotFoundException 試圖訪問一個根本不存在的類
IOException 輸入/輸出異常
NumberFormatException 錯誤的類型轉換異常
ArrayStoreException 當向數組存入錯誤類型資料時
ArithmeticException 除數為0的算術異常
Java提供了異常處理機制,通過物件導向的方法來處理異常。
·在程式啟動並執行過程中,如果發生了異常,則該程式(或Java虛擬機器)產生一個代表該異常類的對象(包含該異常的詳細資料),並把交給運行時系統;
·運行時系統從產生對象的代碼開始,沿方法的調用棧逐層回溯尋找,直到找到包含相應處理代碼的方法,並把異常對象交給該方法,來處理這一異常。
異常處理的機制:使用try-catch-finally語句捕獲和處理異常
1、try語句塊
·將程式中可能出現異常的代碼放入try塊中。
·當try塊中有語句引發異常時,系統將不再執行try塊中未執行的語句,而執行匹配的catch塊。
·如果try塊中沒有語句引發異常,則程式執行完try塊中的語句後不執行catch塊中的語句,
即跳過catch語句,繼續執行後面的程式。
2、catch塊
·每個try語句後面必須伴隨一個或多個catch語句,用於捕捉try語句塊所產生的異常並作相應的處理。
·catch子句的目標是解決“異常”情況,並像沒有出錯一樣繼續運行。
try例可以有多個catch塊,當使用多個catch塊時,需注意catch子句排列
順序--先特殊到一般,也就是子類在父類前面。如果子類在父類後面,子類將永遠不
會到達。 try和它的catch是一個單元,catch不能捕獲其他try聲明所引發的異常。
try不能單獨使用。
一個異常對象能否被一個catch塊接收主要看該異常對象與catch塊中聲明的
異常類的匹配情況,當它們滿足下麵條件中的任一條時,異常對象將被接受:
異常對象是catch塊中聲明的異常類的執行個體;
異常對象是catch塊中聲明的異常類的子類的執行個體;
異常對象實現了catch塊中聲明的異常類的介面。
finally語句塊
某些情況下,不管異常是否發生,都需要處理某些語句,那麼就將這些語句
放到finally語句塊中。finally語句所包含的代碼在任何情況下都會被執行。
一個try語句至少有一個catch語句或finally語句與之匹配,但匹配的
catch可以有多個,而finally語句只能有一個,並且finally語句並非必須有的。
格式:
try{ //產生異常的語句 }
catch(異常類1 變數)
{
//異常處理代碼
}
catch(異常類2 變數)
{
//異常處理代碼
}
[finally{ }]
異常是類所以根據需要可以人工建立並拋出(Throwable)
如果一個方法中的代碼在運行時可能產生某種異常,但是在本方法中不必要,或者不能確定如何處理此類異常時,則可以使用throws聲明拋棄異常;
表明該方法中將不對此類異常進行處理,而由該方法的調用者負責處理;
即系統將在調用該方法的上層方法體內尋找合適的異常處理代碼,而不再繼續執行該方法的正常處理流程。
文法格式:throw 異常類對象;
例:IOException e = new IOException(); throw e;
程式執行throw語句後立即終止,然後在包含它的所有try塊中從裡向外尋找
含有與其類型匹配的catch子句。
聲明拋棄異常的格式
類型 方法名([參數表]) throws 異常類型,…
{ //方法體; }
自訂異常類:雖然Java的內建異常處理能夠處理大多數常見錯誤,但使用者仍需建立
自己的異常類型來處理特殊情況。這時可以通過建立Exception的子類來定義自己的
異常類。
Exception類自己沒有定義任何方法。但它繼承了Throwable提供的一些方法。
格式:class 類名 extends Exception
{ … … }
在Java中,對異常進行處理需要考慮以下因素:
如果例外狀況事件是在運行時產生的,並且在JDK API中沒有與該例外狀況事件相對應的異常對象,則應建立使用者自訂類型的異常對象。
如果可以預測異常對象的類型並且該異常是在程式運行時發生的,則建議應用JDK API中定義的系統異常類型,並且可以拋出這種類型的異常對象由JVM處理。
如果不能確定異常對象的類型以及異常發生的時機,則應該採用系統類別型異常對象並由JVM處理。
對應用程式設計失誤導致的數組越界、非法變數等類型的異常,如果要全部捕獲所有類型的異常對象,會增加系統開銷,導致程式的運行效率降低,建議應用程式可以不對此類異常進行捕獲,而交由JVM進行處理。
對於實現輸入/輸出處理、網路通訊和資料庫訪問功能的代碼,必須進行異常對象的捕獲和處理 。
從JDK1.4版本開始,Java語言引入了斷言(assert)機制。
目的:程式調試
測試代碼或者偵錯工具時,總會做出一些假設,斷言就是用於在代碼中捕捉
這些假設 。
表現形式:斷言就是程式中的一條語句,它對一個boolean運算式進行檢查
一個正確程式必須保證這個boolean運算式的值為true;
如果該值為false,說明程式已經處於不正確的狀態,系統給出警告或退出
如果沒有斷言機制,Java程式通常使用if-else或switch語句進行變數狀態檢查。
缺點:
由於檢查的資料類型不完全相同,這樣的語句形式不會統一。
因為檢查僅僅是應用在測試階段,而if-else或switch語句在發布以後仍然將起作用,
如果消除這些代碼就意味著要注釋或者刪除這些代碼,如果這些代碼量很大就意味著工作很繁重並存在風險。
優點:
Java程式員用統一的方式處理狀態檢查問題;
斷言只需在發行的時候關閉該功能即可。
在預設情況下斷言是關閉的,因此在使用斷言以前,
需要先開啟斷言功能,方法:
java –ea MyClass 或者
java –enableassertions MyClass 關閉斷言功能的方法:
java –da MyClass 或者
java –disableassertions MyClass注意:斷言檢查通常在開發與測試時開啟。為
了提高效能,在軟體發布後,斷言檢查通常是關閉的。
文法格式為:assert Expression1
執行到assert語句時,如果Expression1的值為true,則程式正常執行,如果值為false,該語句建立一個Assertion Error對象,並拋出該對象。
格式:assert Expression1:Expression2當宣告失敗時,系統會自動將Expression2的值傳遞給建立的AssertionError對象,進而將其轉換為一個訊息字串儲存起來,可以獲得更多、更有針對性的檢查失敗細節資訊。
那什麼時候用斷言呢:
通常來說,斷言用於檢查一些關鍵的值,並且這些值對整個程式,或者局部功能的完成有很大的影響。
斷言運算式應該短小、易懂,如果需要評估複雜的運算式,應該使用函數計
算。
使用斷言的情況
·檢查控制流程:在if-else和switch語句中,可以在不應該發生的控制支流上加上
assert語句。如果這種情況發生了,assert能夠檢查出來。
·在私人方法計算前,檢查輸入參數是否有效,對於一些private的方法,要求輸入滿
足一些特定的條件,可以在方法開頭使用assert進行參數檢查;
·對於公用方法,通常不使用斷言檢查
在方法計算後,檢查方法結果是否有效
·檢查程式不變數