編寫程序的時候總會遇到這樣那樣的錯誤赔嚎,為了盡量避免錯誤或者外部環(huán)境造成的數(shù)據(jù)丟失等情況,Java中至少應(yīng)該做到以下幾點:
·向用戶通知錯誤
·保存所有的工作
·允許用戶妥善的退出程序
因此就有了異常處理问麸、斷言和日志這些機制。
異常處理
如果由于出現(xiàn)錯誤而使得操作沒有完成钞翔,程序應(yīng)該:
·返回到一種安全狀態(tài),并能夠讓用戶執(zhí)行其他的命令
·或者允許用戶保存所有工作的結(jié)果席舍,并以妥善的方式種植程序
困難點在于檢測(甚至引發(fā))錯誤條件的代碼通常與那些能夠讓數(shù)據(jù)恢復(fù)到安全狀態(tài)或者能夠保存用戶工作并妥善退出的代碼相聚很遠(yuǎn)布轿。
異常處理的任務(wù)就是將控制權(quán)從產(chǎn)生錯誤的地方轉(zhuǎn)移到能夠處理這種情況的錯誤處理器。
需要考慮以下這些錯誤:
·用戶輸入錯誤? ? 包括鍵盤輸入錯誤来颤,還有一些語法錯誤
·設(shè)備錯誤? ? ? ? ? ? 硬件出現(xiàn)問題
·物理限制? ? ? ? ? ? 磁盤已滿
·代碼錯誤? ? ? ? ? ? 程序方法可能沒有正確的完成工作
在Java中汰扭,如果某個方法不能夠采用正常的途經(jīng)完成它的任務(wù),就會從另一個途徑推出方法福铅,這種情況下萝毛,方法不會返回任何值,而是拋出(throw)一個封裝了錯誤信息的對象滑黔。
這個方法會立刻推出笆包,而且也不會從調(diào)用這個方法的程序代碼繼續(xù)執(zhí)行,而是異常處理機制開始搜索能夠處理這種異常狀況的異常處理器略荡。
異常有自己的語法和特定的繼承層次結(jié)構(gòu)庵佣。
異常分類
異常對象都是派生Trowable類的一個類實例。
有兩個分支汛兜,Error和Exception巴粪。其中Error類層次結(jié)構(gòu)描述了Java運行時系統(tǒng)的內(nèi)部錯誤和資源耗盡錯誤,這種情況很少發(fā)生,但是發(fā)生了就沒辦法肛根。
重點關(guān)注Exception層次結(jié)構(gòu)辫塌,它又分解為兩個分支:一個派生于RuntimeException,另一個是其他異常派哲。
RuntimeException包括:
·錯誤的強制類型轉(zhuǎn)換
·數(shù)組訪問越界
·訪問null指針
不屬于的包括:
·視圖超越文件末尾繼續(xù)讀取數(shù)據(jù)
·試圖打開一個不存在的文件
·視圖根據(jù)給定的字符串查找Class對象臼氨,而這個字符串表示的類并不存在。
“如果出現(xiàn)RuntimeException異常狮辽,那么就一定是你的問題一也。”
非檢查型異常(unchecked):Error類和RuntimeException類派生的異常
檢查型異常:其他異常
編譯器要求為所有檢查型異常提供異常處理器
聲明檢查型異常
遇到下面四種情況時會拋出異常:
·調(diào)用了一個拋出檢查型異常的方法
·檢測到一個錯誤喉脖,并利用throw語句拋出一個檢查型異常
·程序出現(xiàn)錯誤
·Java虛擬機或運行時庫出現(xiàn)內(nèi)部錯誤
為什么要聲明檢查型異常椰苟。例如FileInputStream方法,并不能保證在執(zhí)行這個方法的時候树叽,外部文件沒有被刪除或者更改舆蝴。也就是程序內(nèi)部不能保證的情況,就需要聲明檢查型異常题诵,否則一旦出錯洁仗,所有線程都會終止。
為什么不用聲明非檢查型異常性锭。如果出現(xiàn)Error類赠潦,那么在你的控制之外,無法關(guān)注草冈。如果是RuntimeError她奥,編輯器會自動拋出異常,而且更應(yīng)該關(guān)注如何將這個錯誤避免怎棱,而不是聲明異常哩俭。
如何拋出異常
如下
String readData(Scanner in) throws EOFException{
? ? ...
? ? while(...){
? ????? if ( !in.hasNext()){
? ? ? ? if (n < len)? ? throw new EOFException;
? ? ?????}
? ? }
? ? return s;
}
·找到一個合適的異常類
·創(chuàng)建這個類的一個對象
·將對象拋出
創(chuàng)建異常類
自定義的類應(yīng)該包括兩個構(gòu)造器,一個是默認(rèn)的構(gòu)造器拳恋,另一個是包含詳細(xì)描述信息的構(gòu)造器凡资。例如
class FileFormatException extends IOException{
????public FileFormatException(){}
? ? public? FileFormatException(String gripe){ super(gripe); }
}
然后像上面一樣拋出就可以
捕獲異常
try/catch語句
加入程序拋出了一個異常但是沒有捕獲,那么就會終止程序谬运,然后在控制臺上打印出異常信息隙赁。
使用try/catch語句可以捕獲異常,如果在try語句中拋出了IOException異常梆暖,catch就會捕獲并執(zhí)行catch中的代碼
注意執(zhí)行順序鸳谜,是先執(zhí)行try中的代碼,直到拋出異常式廷,然后跳過剩下的代碼咐扭,去執(zhí)行catch中的代碼。
public?static?void?read(String?filename){
????try?{
????????var?in?=?new?FileInputStream(file);
????????int?b;
????}?catch?(IOException?e)?{
????????e.printStackTrace();
????}
}
注意,這里沒有使用
public static void read(String filename) throws IOException{}
兩者的區(qū)別在于蝗肪,throws給整個方法拋出一個異常袜爪,也就是說,調(diào)用這個方法時可能會拋出這個異常薛闪。這是傳遞異常辛馆。
而try/catch在程序內(nèi)部就解決了異常,調(diào)用這個方法并不會拋出異常豁延。這是處理異常昙篙。
如果拋出了異常,要么傳遞異常要么處理異常诱咏。否則程序就會終止苔可。
捕獲多個異常
try{...}
catch(FileNotFoundException e){...}
catch(UnkonwnHostException e){...}
catch(IOException e){...}
也可以合并
catch(FileNotFoundException | UnkonwnHostException e){...}
再次拋出異常與異常鏈
·如果想改變異常的類型,就可以再次拋出異常
try{...}
catch{SQLException e}{
????throw new ServletException();
}
·或者一個方法不允許拋出檢查型異常但是卻發(fā)生了袋狞,也可以使用這種技術(shù)焚辅。
·或者在拋出異常之前,想要記錄這個異常再拋出苟鸯。
finally子句
try{...}
catch(Exception e){...}
finally{...}
有好幾種情況:try中出現(xiàn)異常同蜻,catch中出現(xiàn)異常,異常有沒有被捕獲等等早处。但是不管如何湾蔓,finally子句中的代碼最后都會執(zhí)行。
try-with-Resources語句
假如要打開的資源屬于一個實現(xiàn)了AutoCloseable接口的類砌梆,就可以用這個語句
try(Resource res = ...){
? ? do something
}
無論有沒有異常默责,最后都會執(zhí)行res.close(),等同于finally么库。
try(var in = new Scanner(new FileInputStream("/usr/share/dict/words"), StandardCharset.UTF_8))
{
? ? while (in.hasNext())
? ? ? ? out.println(in.next());
}
分析堆棧軌跡(stack trace)元素
當(dāng)Java程序因為一個未捕獲的異常而終止時,就會顯示堆棧軌跡甘有。
詳情看《Java核心技術(shù)·卷一》P295頁程序诉儒。