▲ 處理錯誤 ▲ 日誌
▲ 捕獲異常 ▲ 調試技巧
▲ 使用異常機制的技巧 ▲ GUI程序排錯技巧
▲ 使用斷言 ▲ 使用調試器
如果一個用戶在運行程序期間,由於程序的錯誤或一些外部環境的影響造成用戶數據的丟失,用戶就有可能不再使用這個程序了。爲了避免這類事情的發生,至少應該做到以下幾點:
- 向用戶通告錯誤;
- 保存所有的工作結果;
- 允許用戶已妥善的形式退出程序。
對於異常情況,Java使用一種稱爲異常處理(exception handing)的錯誤捕獲機制處理。
11.1 處理錯誤
如果由於出現錯誤而使得某些錯做沒有完成,程序應該:
- 返回到一種安全狀態,並能夠讓用戶執行一些其他的命令。
- 允許用戶保存所有操作的結果,並以一種適當的方式終止程序。
11.1.1 異常分類
在Java程序設計語言中,異常對象都是派生於Throwable類的一個實例。
圖1 Java中的異常層次結構
所有的異常都是由Throwable繼承而來,但在下一層立即分解爲兩個分支:Error和
Exception。
Error類層次結構描述了Java運行時系統內部錯誤和資源耗盡錯誤。
Exception層次結構劃分爲兩個分支:由程序錯誤導致的異常屬於RuntimeException;而程序本身沒有問題,但由於像I/O錯誤這類問題導致的異常屬於其他異常。
Java語言規範將派生於Error類或RuntimeException類的所有異常稱爲未檢查(unchecked)異常,將所有其他的異常稱爲已檢查(checked)異常。
11.1.2 聲明已檢查異常
如果遇到無法處理的情況,那麼Java的方法可以拋出一個異常。
方法應該在其首部聲明所有可能拋出的異常。
遇到下面4中情況應該拋出異常:
- 調用一個拋出已檢查異常的方法。
- 程序運行過程中發現錯誤,並利用throw語句拋出一個已檢查異常。
- 程序錯誤,例如啊a[-1]=0會拋出一個ArrayIndexOutOfBoundsException這樣的未檢查異常。
- Java虛擬機和運行時庫出現內部錯誤。
對於那些可能被他人使用的Java方法,應該根據異常規範,在方法的首部聲明這個方法可能拋出的異常。
11.1.3 如何拋出異常
對一個已存在的異常類,將其拋出方法:
1)找到一個合適的異常類。
2)創建這個類的一個對象。
3)將對象拋出。
11.1.4 創建異常類
定義一個派生於Exception的類,或者派生於EXception子類的類。
定義的類應該包含兩個構造器,一個是默認構造器;另一個是帶有詳細描述信息的構造器。
11.2 捕獲異常
想要捕獲異常,必須設置try/catch語句塊。
如果在try語句塊中的任何代碼拋出了一個在catch子句中說明的異常類,那麼
1)程序將跳過try語句塊的其餘代碼。
2)程序將執行catch子句中的處理器代碼。
如果在try語句塊中的代碼沒有拋出任何異常,那麼程序將跳過catch子句。
如果方法中的任何代碼拋出了一個在catch子句中沒有聲明的異常類型,那麼這個方法就會立刻退出。
通常,應該捕獲那些知道如何處理的異常,而將那些不知道怎樣處理的異常繼續進行傳遞。
11.2.1 捕獲多個異常
在一個try語句塊中可以捕獲多個異常類型,並對不同類型的異常做出不同的處理。可以按照下列方式爲每個異常類型使用一個單獨的catch子句:
異常對象可能包含與異常本身有關的信息。想要獲得對象的更多信息,可以試着使用e.getMessage()得到詳細的錯誤信息,或者使用e.getClass().getName()的到異常對象的實際類型。
在Java SE 7 中,同一個catch子句可以捕獲多個異常類型。如果對應異常的動作是一樣的,就可以合併catch子句:
只有當捕獲的異常類型彼此之間不存在子類關係時才需要這個特性。
11.2.2 再次拋出異常與異常鏈
在catch子句中可以拋出一個異常,這樣可以讓用戶拋出子系統中的高級異常,而不會丟失原始異常的細節。
沒弄明白是什麼意思,只知道可以記錄一個異常,然後再把它重新拋出,而不做任何改變。
11.2.3 finally子句
不管異常是否被捕獲,finally子句中的代碼都被執行。
1)代碼沒有拋出異常。程序首先執行try語句塊中的全部代碼,然後執行finally子句中的代碼。隨後執行try語句塊之後的第一條語句。
2)拋出一個在catch子句中捕獲的異常。程序首先執行try語句塊中的所有代碼,直到發生異常爲止。此時,將跳過try語句塊中的剩餘代碼,轉去執行與該異常匹配的catch子句中的代碼,最後執行finally子句中的代碼。
3)代碼拋出了一個異常,但這個異常不是由catch子句捕獲的。程序首先執行try語句塊中的所有代碼,直到異常被拋出爲止。此時,將跳過try語句塊中的剩餘代碼,轉去執行finally子句中的語句,並將異常拋給這個方法的調用者。
try語句可以只有finally子句,而沒有catch子句。
11.2.4 帶資源的try語句
假如資源屬於一個實現了AutoCloseable接口的類,Java SE 7 爲這種代碼模式提供了一個很有用的快捷方式,AutoCloseable接口有一個方法:
void close() throws Exception
try語句塊退出時,會自動調用res.close()。
11.2.5 分析堆棧跟蹤元素
堆棧跟蹤是一個方法調用過程的列表,它包含了程序執行過程中方法調用的特定位置。
可以調用Throwable類的printStackTrace方法訪問堆棧跟蹤的文本描述信息。
11.3 使用異常機制的技巧
- 異常處理不能代替簡單的測試,只在異常情況下使用異常機制。
- 不要過分地細化異常
- 利用異常層次結構
- 不要壓抑異常
- 在檢查錯誤時,苛刻要比放任更好
- 不要羞於傳遞異常
11.4 使用斷言
斷言機制允許在測試期間向代碼中插入一些檢查語句,當代碼發佈時,這些插入的檢測語句將會被移走。
斷言格式:
- assert 條件;
- assert 條件:表達式;
這兩種形式都會對條件進行檢測,如果結果爲false,則拋出一個AssertionError異常。第二種形式中,表達式將被傳入AssertionError的構造器,並轉行成一個消息字符串。
11.4.1 啓用和禁用斷言
在默認情況下,斷言被禁用。
可以在運行程序時使用-enableassertions或-ea選項啓用它:java-enableassertions MyApp
啓用或禁用斷言時不必重新編譯程序。
開發工具中啓用斷言:
1.在eclipse中,Run-> Run Configurations -> Arguments頁籤 -> VMarguments文本框中加上斷言開啓的標誌:-enableassertions 或者-ea
2.在myEclipse中,Windows-> Preferences ->Java ->Installed JREs ->點擊正使用的JDK ->Edit->Default VM Arguments文本框中輸入:-ea
11.4.2 使用斷言完成參數檢查
選擇斷言的條件:
- 斷言失敗是致命的、不可恢復的錯誤。
- 斷言檢查只用於開發和測試階段。
不應該使用斷言向程序的其他部分通告發生了可恢復的錯誤,或者,不應該作爲程序向用戶通告問題的手段。
斷言只應該用於在測試階段確定程序內部的錯誤位置。
11.5 記錄日誌
記錄日誌API就是爲了幫助程序員定位問題而設計的。這些API的優點有:
- 可以很容易地取消全部日誌記錄,或者僅僅取消某個級別的日誌,而且打開和關閉這個操作也很容易。
- 可以簡單地禁止日誌記錄和輸出,因此,這些日誌代碼留在程序中的開銷很小。
- 日誌記錄可以被定向到不同的處理器,用於在控制檯中顯示,用於存儲在文件中等。
- 日誌記錄器和處理器都可以對記錄進行過濾。過濾器可以根據過濾實現器制定的標準丟棄那些無用的記錄。
- 日誌記錄可以採用不同的方式可視化,例如村文本或XML
- 應用程序可以採用多個日誌記錄器,它們使用類似包名的這種具有層次結構的名字。
- 在默認情況下,日誌系統的配置由配置文件控制。如果需要的話,應用程序可以替換這個配置。
Java核心技術介紹的日誌應該是Java自帶的日誌系統,JDBC中好像重新封裝了日誌功能,到時候再來比較。