JavaCoreNote–異常
如果由於出現錯誤而使得某些操作沒有完成,程序應該:
• 返回到一種安全狀態,並能夠讓用戶執行一些其他的命令;或者
• 允許用戶保存所有操作的結果,並以妥善的方式終止程序
異常
for:將控制權從錯誤產生的地方轉移給能夠處理這種情況的錯誤處理器
程序錯誤分類:
- 用戶輸入
- 物理限制
- 設備
- 代碼
處理方式:返回一個特殊的錯誤碼;不返回任何值,而是拋出一個封裝了異常信息的對象,尋找對應的錯誤處理器。
語法
分類
-
非受查異常(unchecked)
Error、RuntimeException
Error類層次結構描述了 Java 運行時系統的內部錯誤和資源耗盡錯誤;
RuntimeException:程序錯誤導致的異常,包括情況:- 錯誤的類型轉換
- 數組訪問越界
- 訪問null指針
如果出現RuntimeException異常,一定是程序員的問題
-
受查異常(checked)
非RuntimeException表示程序本身沒有問題,但由於像IO錯誤這類的異常
何時聲明
- 調用一個拋出受査異常的方法
- 程序運行過程中發現錯誤,並且利用throw語句拋出一個受查異常
- 程序出現錯誤拋出非受查異常
- 虛擬機和運行時庫出現的內部錯誤
注:不應聲明從Error繼承來的錯誤、從RuntimeException繼承來的異常
一個方法必須聲明所有可能拋出的受查異常,而非受查異常要麼不可控制(Error),要麼就應該避免發生(RuntimeException)
拋出異常
- 找到合適異常類
- 創建異常類對象
- 將對象拋出
定義異常類:派生於Exception或其子類的類,提供兩個構造器
注:不允許子類的throws說明符中出現超過超類方法所列出的異常類範圍
捕獲異常
- try/catch語句塊
- 何時捕獲,何時拋出?——捕獲哪些知道如何處理的異常
- 捕獲多個異常時,異常變量隱含爲final變量
- catch語句中可再次拋出異常,爲了轉換異常的類型,可設置異常原因initCause,getCause可重新獲得
finally子句
for:資源回收問題,如果沒有finally子句,一樣的代碼將在兩個地方出現。
finally子句中包含return語句將覆蓋try中的return
解耦合try/catch和try/finally語句塊,內層負責關閉資源,外層負責確保報告出現的異常
InputStrean in = . . .;
try
{
try
{
code that might throwexceptions
}
finally
{
in.cose();
}
}
catch (IOException e) {
show error message
}
finally子句可能產生異常,覆蓋原始的異常信息。
帶資源的try語句
- 資源屬於實現AutoClosable接口的類,會自動調用close方法。
- 帶資源的try語句也可以有自己的catch、finally子句,這些子句會在調用了關閉資源之後執行。實際中,避免加入過多內容,很少這樣使用這種情況。
對於finally子句產生異常覆蓋原始異常的問題,會自動調用addSuppressd方法添加到原始異常的抑制異常中,拋出原始異常,處理器可調用getSuppressed獲得異常列表。
堆棧軌跡
- Throwable.printStackTrace、getStackTrace
- Thread.getAllStackTrace
使用技巧
- 異常處理不能代替簡單測試
- 不要過分細化異常,使代碼膨脹
- 利用異常層次結構
- 不要壓制異常
- 檢測錯誤時,苛刻比放任更好(早拋出)
- 不要羞於傳遞異常(晚捕獲)
斷言
for:有選擇的啓用檢測
斷言機制允許在測試期間向代碼中插入一些檢査語句。當代碼發佈時,這些插人的檢測語句將會被自動地移走
使用
-
assert 條件;
assert 條件:表達式;
條件不成立時拋出AssertionError異常,第二種形式中表達式生成一個說明字符串。 -
啓用禁用
不需要重新編譯,是類加載器的功能。
啓用:-enableassertions 或 -ea
可在某個類或包中啓用:
java -ea:MyClass -ea:com.mycompany.mylib… MyApp
禁用:-disableassertions 或 -da
對於系統類(沒有類加載器),使用:enablesystemassertions/-esa
程序中也可以控制,參看API
- 何時使用
- 斷言失敗是致命的、不可恢復的錯誤;
- 斷言檢查只用於開發和測階段
日誌
日誌API優勢:
- 很容易開啓、取消全部或某個級別日誌記錄;
- 可以被定向到不同處理器;
- 記錄器和處理器可以對記錄過濾;
- 不同方式格式化;
- 可以用多個日誌記錄器;
- 默認用配置文件控制,也可用程序替換配置。
基本日誌
- 調用全局記錄器(global logger)
- 取消:Logger.getGlobal().setLevel(Off)
高級日誌
- 記錄器具有層次結構,子繼承父的屬性
- 7個級別:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST,默認前三個
默認記錄類名、方法名信息,但VM可能進行優化得不到準確調用信息,可使用logp方法獲得調用類和方法的確切信息
作用:
- 用來跟蹤執行流的方法:entring、exiting
- 記錄不可預料的異常:throwing、log
修改日誌管理器配置
默認:jre/lib/1ogging.properties
修改:java -Djava.util.logging.config.file=configFile MainClass
修改日誌級別 名稱.level=FINE
默認日誌管理器:java.util.logging.LogManager
可通過系統屬性修改:java.util.logging.manager
處理器
- 默認使用ConsoleHandler
- 默認INFO級java.uti1.1ogging.ConsoleHandler.level
- 默認發送到處理器及父處理器
其他處理器:FileHandler、SocketHandler
可修改文件處理器的默認行爲
過濾器
實現Filter,記錄器或處理器中setFilter方法使用
格式化
實現Formatter,處理器中setFormatter方法使用
常用操作
- 日誌記錄器命名爲主程序包名
- 程序中安裝默認配置
- 只將對用戶有用的日誌設置爲前3個級別,顯示在控制檯。
調試技巧
- 打印變量;
- 在類中放置main方法,對每個類做單元測試;
- 使用JUnit組織測試用例
- 使用日誌代理(logging proxy)截獲方法調用,記錄日誌
- 打印堆棧軌跡Thread.dumpStack()
- 捕獲堆棧軌跡到字符串
- 捕獲錯誤信息到文件java MyProgram 2> errors.txt
java MyProgram 1> errors.txt 2>&1 - 非捕獲異常的堆棧軌跡保存到文件中,可以調用靜態的 Thread.setDefaultUncaughtExceptionHandler方法改變非捕獲異常的處理器
- 觀察類的加載過程,可以用-verbose標誌啓動Java虛擬機
- -Xlint選項告訴編譯器對一些普遍容易出現的代碼問題進行檢査
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Vg4mJ7vS-1582984683851)(evernotecid://1520493E-927F-420A-8EE1-BA6F74088A9D/appyinxiangcom/11767354/ENResource/p3065)] - 利用JVM對Java應用程序進行監控(monitoring)和管理 (management)的支持
jconsole processID - jmap實用工具獲得一個堆的轉儲
jmap -dump:format=b,file=dumpFileName processID
jhat dumpFileName
進人localhost:7000 - 使用 -Xprof 標誌運行 Java 虛擬機, 就會運行一個基本的剖析器來跟蹤那些代
碼中經常被調用的方法
小結
本文整理了程序錯誤的分類,除了返回特殊的返回值外,Java語言中可通過拋出異常對象、捕獲異常的方式處理程序中出現的問題。通過不同日誌級別記錄程序運行中的業務、程序信息,通過斷言實現程序開發、測試階段的程序檢測。