異常的定義:在《java編程思想》中這樣定義異常:阻止當(dāng)前方法或作用域繼續(xù)執(zhí)行的問題。
異常類的來源:一是java語言定義的一些基本異常;二是用戶自定義的一些異常,這些異常繼承Exception類或其子類。
異常對象的來源:一是java運(yùn)行時(shí)環(huán)境拋出的異常;二是程序員自己手動拋出的異常,這種異??梢允钩绦騿T自己定義的,也可以是java語言定義的,通過throw關(guān)鍵字來拋出這種指定的異常,這種異常用來向調(diào)用者匯報(bào)異常的一些信息。
Java處理異常的5個(gè)關(guān)鍵字:try catch finally throw throws。在try語句中包含可能拋出異常的代碼,在catch中進(jìn)行匹配并捕獲相應(yīng)的異常對象,finally表示這段代碼必須執(zhí)行,一般用于釋放一些物理資源,throw表示明確的拋出一個(gè)異常,在方法簽名處通過throws來表明可能拋出的各種異常。
在java程序執(zhí)行try塊 catch塊時(shí)遇到了return或throw語句,這兩條并不是立即執(zhí)行,首先看看是否含有finally部分,如果有,先執(zhí)行finally部分,再跳回到try或catch塊里的return或throw語句,如果沒有finally部分,程序立即執(zhí)行try或catch塊里的return或throw語句。若finally里面也有return或throw語句,系統(tǒng)將不會再跳到try或catch塊里面。
一般結(jié)構(gòu):
try{
程序代碼
}catch(異常類型1 異常的變量名1){
程序代碼
}catch(異常類型2 異常的變量名2){
程序代碼
}finally{
程序代碼
}
catch可以有多個(gè),按異常類從小到大進(jìn)行排列,try語句中拋出的異常對象只要在catch中有一個(gè)匹配成功,就不再進(jìn)行往下的匹配。Catch括號中的異常類可以是java語言定義的類也可以是自己定義的類,異常變量名表示拋出異常類對象的引用,可以在catch代碼塊中直接使用這個(gè)變量名,通過這個(gè)引用(變量名)的方法來獲得異常對象的詳細(xì)信息。
常見異常繼承關(guān)系:
Exception表示程序本身可以處理的異常
Error一般是指虛擬機(jī)相關(guān)的問題,如系統(tǒng)失敗,動態(tài)鏈接失敗等,這種錯(cuò)誤無法恢復(fù)或捕獲,程序?qū)⒅袛?。所以不?yīng)該捕獲這種error錯(cuò)誤。
RunttimeExceptionJava 虛擬機(jī)正常運(yùn)行期間拋出的異常的超類。Java編譯器不去檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常時(shí),即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過,這種異??梢酝ㄟ^改進(jìn)代碼實(shí)現(xiàn)來避免。
處理異常一般用兩種方式:
第一:在catch語句中處理
第二:對于處理不了的異常用throw拋出指定異常,并在方法簽名處通過throws拋出,讓上一級的調(diào)用者來處理,如果main方法也不知道如何處理,也使用throws方法拋出異常,該異常將交給虛擬機(jī)來處理。Java虛擬機(jī)會從出現(xiàn)異常的方法代碼 塊中往上找,直到找到處理該異常的代碼塊為止。然后將異常交給相應(yīng)的catch語句處理。如果Java虛擬機(jī)追溯到方法調(diào)用棧底部main()方法時(shí), 如果仍然沒有找到處理異常的代碼塊,將按照下面的步驟處理:
(1) 調(diào)用異常的對象的printStackTrace()方法,打印方法調(diào)用棧的異常信息。
(2) 如果出現(xiàn)異常的線程為主線程,則整個(gè)程序運(yùn)行終止;如果非主線程,則終止該線程,其他線程繼續(xù)運(yùn)行。
通過分析思考可以看出,越早處理異常消耗的資源和時(shí)間越小,產(chǎn)生影響的范圍也越小。因此,不要把自己能處理的異常也拋給調(diào)用者。
并且注意try語句不能單獨(dú)存在,可以和catch、finally組成 try...catch...finally、try...catch、try...finally三種結(jié)構(gòu),catch語句可以有一個(gè)或多 個(gè),finally語句多一個(gè),try、catch、finally這三個(gè)關(guān)鍵字均不能單獨(dú)使用。throw語句后不允許有緊跟其他語句,因?yàn)檫@些沒有機(jī)會執(zhí)行。
一個(gè)方法可能拋出異常的標(biāo)志是:
1 在方法簽名處有throws語句
2 在方法中含有throw語句
自定義異常類
創(chuàng)建Exception或者RuntimeException的子類即可得到一個(gè)自定義的異常類。例如:
public class MyException extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
Exception類可以分為兩種:運(yùn)行時(shí)異常和受檢查異常。
1、運(yùn)行時(shí)異常
RuntimeException 類及其子類都被稱為運(yùn)行時(shí)異常,這種異常的特點(diǎn)是Java編譯器不去檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常時(shí),即使沒有用try...catch語 句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。例如,當(dāng)除數(shù)為零時(shí),就會拋出 java.lang.ArithmeticException異常。
2、受檢查異常
意味著程序出現(xiàn)了bug,如數(shù)組越界,0被除等。除了RuntimeException類及其子類外,其他的Exception類及其子類都屬于受檢查異常,這種異常的特點(diǎn)是要么用try...catch捕獲處理,要么用throws語句聲明拋出,否則編譯不會通過。
3、兩者的區(qū)別
運(yùn)行時(shí)異常表示無法讓程序恢復(fù)運(yùn)行的異常,導(dǎo)致這種異常的原因通常是由于執(zhí)行了錯(cuò)誤的操作。一旦出現(xiàn)錯(cuò)誤,建議讓程序終止。
受檢查異常表示程序可以處理的異常。如果拋出異常的方法本身不處理或者不能處理它,那么方法的調(diào)用者就必須去處理該異常,否則調(diào)用會出錯(cuò),連編譯也無法通過。當(dāng)然,這兩種異常都是可以通過程序來捕獲并處理的。
4、運(yùn)行時(shí)錯(cuò)誤
一般很少見,也很難通過程序解決。它可能源于程序的bug,但一般更可能源于環(huán)境問題,如內(nèi)存耗盡。錯(cuò)誤在程序中無須處理,而有運(yùn)行環(huán)境處理。Error類及其子類表示運(yùn)行時(shí)錯(cuò)誤,通常是由Java虛擬機(jī)拋出的。程序本身無法修復(fù)這些錯(cuò)誤.一般不去擴(kuò)展Error類來創(chuàng)建用戶自定義的錯(cuò)誤類。而RuntimeException類表示程序代碼中的錯(cuò)誤,是可擴(kuò)展的,用戶可以創(chuàng)建特定運(yùn)行時(shí)異常類。
Error(運(yùn)行時(shí)錯(cuò)誤)和運(yùn)行時(shí)異常的相同之處是:Java編譯器都不去檢查它們,當(dāng)程序運(yùn)行時(shí)出現(xiàn)它們,都會終止運(yùn)行。
5、佳解決方案
對于運(yùn)行時(shí)異常,我們不要用try...catch來捕獲處理,而是在程序開發(fā)調(diào)試階段,盡量去避免這種異常,一旦發(fā)現(xiàn)該異常,正確的做法就會改進(jìn)程序設(shè)計(jì) 的代碼和實(shí)現(xiàn)方式,修改程序中的錯(cuò)誤,從而避免這種異常。捕獲并處理運(yùn)行時(shí)異常是好的解決辦法,因?yàn)榭梢酝ㄟ^改進(jìn)代碼實(shí)現(xiàn)來避免該種異常的發(fā)生。
對于受檢查異常,沒說的,老老實(shí)實(shí)去按照異常處理的方法去處理,要么用try...catch捕獲并解決,要么用throws拋出!對于Error(運(yùn)行時(shí)錯(cuò)誤),不需要在程序中做任何處理,出現(xiàn)問題后,應(yīng)該在程序在外的地方找問題,然后解決。
程序發(fā)生異常情況時(shí),首先,同其它java對象的創(chuàng)建一樣,在堆中new出一個(gè)異常對象。然后,當(dāng)前的執(zhí)行路徑被終止,如果出現(xiàn)異常的線程為主線程,則整個(gè)程序運(yùn)行終止;如果非主線程,則終止該線程,其他線程繼續(xù)運(yùn)行。同時(shí)從當(dāng)前環(huán)境中拋出該異常對象的引用。此時(shí),異常處理機(jī)制接管程序,并尋找一個(gè)合適的地方繼續(xù)執(zhí)行程序,即異常處理程序,它的任務(wù)是將程序從錯(cuò)誤中恢復(fù)。
在new出異常對象的時(shí)候,隨之進(jìn)行的是存儲空間的分配和構(gòu)造器的調(diào)用。所有的標(biāo)準(zhǔn)異常類都有兩個(gè)構(gòu)造器:一個(gè)是默認(rèn)的構(gòu)造器;另一個(gè)是接受字符串作為參數(shù),以便將相關(guān)信息放入異常對象的構(gòu)造器。因?yàn)楫惓ο笫窃诙阎衝ew出的,所有垃圾回收器會自動將它們清理到。
對于錯(cuò)誤的信息可以保存在異常對象的內(nèi)部或用異常對象的類名來進(jìn)行暗示。
有時(shí)可能用不到標(biāo)示符,因?yàn)楫惓5念愋鸵呀?jīng)足夠暗示異常的信息,但標(biāo)示符不能省略。
我們平時(shí)所關(guān)心的異常的基類通常是Exception異常。
拋出異常時(shí),異常處理系統(tǒng)會按順序查找“近”的catch,一旦找到匹配的處理catch,就不再繼續(xù)匹配了。對于類型的匹配,派生類的對象也可以匹配其基類的處理程序。
異常處理的優(yōu)點(diǎn)之一就是是得我們可以在某處集中精力處理要解決的問題,而在另一處處理編寫的這段代碼中產(chǎn)生的錯(cuò)誤。