1.概述
- Java提供異常處理機(jī)制來(lái)處理異常
- 斷言:在測(cè)試中旦棉,不需要寫測(cè)試代碼齿风,測(cè)試完將代碼刪除。我們使用斷言來(lái)有選擇性的進(jìn)行測(cè)試
- 日志:記錄下出現(xiàn)的問(wèn)題绑洛,已備日后分析
2.處理錯(cuò)誤
2.1 異常分類
- Error:描述了運(yùn)行時(shí)系統(tǒng)的我內(nèi)部錯(cuò)誤等救斑。一般除了通知用戶,并盡力使程序安全終止外真屯,無(wú)能為力
- Exception:根據(jù)unchecked分為unchecked Exception和checked Exception
-
RuntimeExcetion:unchecked Exception脸候。
- NullPointerException
- ClassCastException
- ArrayIndexOutOfBoundsException
-
IOException:checked Exception
- 如試圖打開(kāi)一個(gè)不存在的文件
- 試圖在文件尾部后面讀取數(shù)據(jù)
-
RuntimeExcetion:unchecked Exception脸候。
- 編譯器會(huì)檢查是否為所有的checked Exception提供了異常處理器。也就是編譯期間绑蔫,編譯器會(huì)檢查你有沒(méi)有對(duì)IOException進(jìn)行異常處理运沦,但是不會(huì)關(guān)心你否處理了數(shù)組越界異常
2.2 聲明checked異常
- 每個(gè)包含的checked異常的方法都需要通過(guò)
throws
聲明具體可能會(huì)拋出什么異常,包含多個(gè)checked Exception則需要聲明多個(gè)配深。 - 什么時(shí)候需要拋出異常携添?
- 調(diào)用會(huì)拋出checked Exception的方法時(shí)
- 方法本身利用
throw
,拋出異常時(shí) - 程序出現(xiàn)運(yùn)行時(shí)異常
- 系統(tǒng)和內(nèi)部錯(cuò)誤
- 什么時(shí)候需要聲明異常呢篓叶?
- 上述前兩種情況
- 不需要聲明系統(tǒng)錯(cuò)誤以及運(yùn)行時(shí)的unchecked Exception烈掠,也就是RuntimeException羞秤。我們應(yīng)該盡力去修正這些RuntimeException。
- 總之左敌,一個(gè)方法必須聲明所有可能拋出的受查異常瘾蛋,而非受查異常要么不可控制(Error),要么就應(yīng)該避免發(fā)生(RuntimeException)
- 異辰孟蓿可以被拋出哺哼,也可以被捕獲,具體采用什么策略要看具體情況
3.捕獲異常
3.1 捕獲異常
- 捕獲異常叼风,必須設(shè)置
try/catch
語(yǔ)句塊 - 如果調(diào)用了一個(gè)拋出受查異常的方法取董,就必須對(duì)它進(jìn)行處理,或者繼續(xù)傳遞咬扇。
- 通常甲葬,應(yīng)該捕獲那些知道如何處理的異常,而將那些不知道怎樣處理的異常繼續(xù)進(jìn)行傳遞
- 如果想傳遞一個(gè)異常懈贺,就必須在方法的首部添加一個(gè)throws說(shuō)明符经窖,以便告知調(diào)用者這個(gè)方法可能會(huì)拋出異常
- 注意:如果編寫一個(gè)覆蓋超類的方法,而這個(gè)方法又沒(méi)有拋出異常(如JComponent中的paintComponent)梭灿,那么這個(gè)方法就必須捕獲方法代碼中出現(xiàn)的每一個(gè)受查異常画侣。不允許在子類的throws說(shuō)明符中出現(xiàn)超過(guò)超類方法所列出的異常類范圍
3. 2 捕獲多個(gè)異常
- 在一個(gè)
try
語(yǔ)句塊中,可以捕獲多個(gè)異常堡妒,并對(duì)不同類型的異常做出不同的處理
3.3 再次拋出異常和異常鏈
- 在
catch
子句中可以拋出異常配乱,這樣做的目的是改變異常的類型,如:try { } catch(SQLException e) { throw new ServletException(); }
-
另一種更好的辦法:將原始異常作為新異常的原因
獲取到異常后皮迟,使用下面這條語(yǔ)句重新得到異常:try { } catch(SQLException e) { Throwable se = new ServletException(); se.initCause(e); throw se; }
se.getCause()
- 如果在一個(gè)方法中發(fā)生了一個(gè)受查異常搬泥,而不允許拋出它,那么包裝技術(shù)就十分有用伏尼。我們可以捕獲這個(gè)受查異常忿檩,并將它包裝成一個(gè)運(yùn)行時(shí)異常
3.4 finally子句
- 當(dāng)finally子句包含return語(yǔ)句時(shí),將會(huì)出現(xiàn)一種意想不到的結(jié)果爆阶。假設(shè)利用return語(yǔ)句從try語(yǔ)句塊中退出燥透。在方法返回前,finally子句的內(nèi)容將被執(zhí)行辨图。如果finally子句中也有一個(gè)return語(yǔ)句班套,這個(gè)返回值將會(huì)覆蓋原始的返回值。請(qǐng)看一個(gè)復(fù)雜的例子:
public static int f(int n) { try { int r=2; return r; } finally { if (r=2) return 0; } }
3.5 帶資源的try語(yǔ)句
- 帶資源的
try語(yǔ)句
:try塊退出時(shí)或存在一個(gè)異常時(shí)故河,會(huì)自動(dòng)調(diào)用res.close() - 如果try塊拋出一個(gè)異常吱韭,而且close方法也拋出一個(gè)異常,這就會(huì)帶來(lái)一個(gè)難題鱼的。帶資源的try語(yǔ)句可以很好地處理這種情況理盆。原來(lái)的異常會(huì)重新拋出瞻讽,而close方法拋出的異常會(huì)“被抑制”。這些異常將自動(dòng)捕獲熏挎,并由addSuppressed方法增加到原來(lái)的異常。如果對(duì)這些異常感興趣晌砾,可以調(diào)用getSuppressed方法坎拐,它會(huì)得到從close方法拋出并被抑制的異常列表。
3.6 分析堆棧軌跡元素
- 堆棧軌跡(stack trace)是一個(gè)方法調(diào)用過(guò)程的列表养匈,它包含了程序執(zhí)行過(guò)程中方法調(diào)用的特定位置
- 可以調(diào)用Throwable類的printStackTrace方法訪問(wèn)堆棧軌跡的文本描述信息
- 一種更靈活的方法是使用getStackTrace方法哼勇,它會(huì)得到StackTraceElement對(duì)象的一個(gè)數(shù)組,可以在你的程序中分析這個(gè)對(duì)象數(shù)組
- StackTraceElement類含有能夠獲得文件名和當(dāng)前執(zhí)行的代碼行號(hào)的方法呕乎,同時(shí)积担,還含有能夠獲得類名和方法名的方法
4.使用異常機(jī)制的技巧
- 異常處理不能代替簡(jiǎn)單的測(cè)試
對(duì)比下面兩段代碼:
if(!s.empty)s.pop;
與
try
{
}
catch(EmptyStackException e)
捕獲異常花費(fèi)的執(zhí)行時(shí)間相比于前者是巨大的猬仁,因此:只在異常情況下使用異常機(jī)制
- 利用異常層次結(jié)構(gòu)
不要只拋出RuntimeException異常。應(yīng)該尋找更加適當(dāng)?shù)淖宇惢騽?chuàng)建自己的異常類
不要只捕獲Thowable異常,否則朽们,會(huì)使程序代碼更難讀奶镶、更難維護(hù) - 不要壓制異常
- 在檢測(cè)錯(cuò)誤時(shí),“苛刻”要比放任更好
例如诈闺,當(dāng)椏是欤空時(shí),Stack.pop是返回一個(gè)null雅镊,還是拋出一個(gè)異常襟雷?我們認(rèn)為:在出錯(cuò)的地方拋出一個(gè)EmptyStackException異常要比在后面拋出一個(gè)NullPointerException異常更好 - 不要羞于傳遞異常
4-5總結(jié)起來(lái)就是”早拋出,晚捕獲“
5.斷言
5.1 斷言是什么
double y = Math.sqrt(x)
假設(shè)我們編寫了以上代碼仁烹,此時(shí)我們需要檢測(cè)x
是否大于0耸弄,我們可以這樣做:
if (x<0) throw new IllegalArgumentException();
但是這樣這段代碼會(huì)一直保留在程序中,即使測(cè)試完畢也不會(huì)自動(dòng)刪除晃危。如果程序中含有大量這種檢查叙赚,程序運(yùn)行起來(lái)會(huì)相當(dāng)慢
- 斷言機(jī)制允許在測(cè)試期間向代碼中插入一些檢查語(yǔ)句。當(dāng)代碼發(fā)布時(shí)僚饭,這些插入的檢測(cè)語(yǔ)句將會(huì)被自動(dòng)移走震叮。
5.2 斷言的使用
assert 條件;
或
assert 條件:表達(dá)式鳍鸵;
這兩種形式都會(huì)對(duì)條件進(jìn)行檢測(cè)苇瓣,如果結(jié)果為false,則拋出一個(gè)AssertionError異常偿乖。在第二種形式中击罪,表達(dá)式將被傳入AssertionError的構(gòu)造器哲嘲,并轉(zhuǎn)換成一個(gè)消息字符串
5.3 什么時(shí)候使用斷言
java中給出了3種處理系統(tǒng)異常的機(jī)制:
- 拋出一個(gè)異常
- 日志
- 斷言
- 什么時(shí)候使用斷言
錯(cuò)誤1. 斷言失敗是致命的、不可恢復(fù)的錯(cuò)誤
- 斷言檢查只用于開(kāi)發(fā)和測(cè)階段
因此不應(yīng)該使用斷言向程序的其他部分通告發(fā)生了可恢復(fù)性的