我原本是個(gè)快樂的iOS開發(fā)者, 總是會(huì)看到一張圖片:
我就在想crash就crash了唄, 弄這么多學(xué)名干嘛, 還拋出異常. 后來(lái)經(jīng)過我對(duì)JAVA的研究我發(fā)現(xiàn)我的想法被啪啪打臉, 那叫一個(gè)左右開弓.
( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)
還是我太naive, 而今天, 就要詳述一下異常這個(gè)東西.
我們要確立一個(gè)概念, 那就是: 什么是異常機(jī)制.
異常機(jī)制是指當(dāng)程序出現(xiàn)錯(cuò)誤后,程序如何處理。具體來(lái)說(shuō),異常機(jī)制提供了程序退出的安全通道业簿。當(dāng)出現(xiàn)錯(cuò)誤后剩彬,程序執(zhí)行的流程發(fā)生改變抖锥,程序的控制權(quán)轉(zhuǎn)移到異常處理器漾狼。
當(dāng)你明白異常機(jī)制是怎么回事的時(shí)候,異常處理的流程你也不難接受.
當(dāng)程序中拋出一個(gè)異常后乎婿,程序從程序中導(dǎo)致異常的代碼處跳出肠阱,java虛擬機(jī)檢測(cè)尋找和try關(guān)鍵字匹配的處理該異常的catch塊票唆,如果找到,將控制權(quán)交到catch塊中的代碼屹徘,然后繼續(xù)往下執(zhí)行程序走趋,try塊中發(fā)生異常的代碼不會(huì)被重新執(zhí)行。如果沒有找到處理該異常的catch塊噪伊,在所有的finally塊代碼被執(zhí)行和當(dāng)前線程的所屬的ThreadGroup的uncaughtException方法被調(diào)用后簿煌,遇到異常的當(dāng)前線程被中止氮唯。
用代碼表示就是:
try {
System.out.println("尋找到try關(guān)鍵字");
} catch (Exception e){
e.printStackTrace();
System.out.println("捕獲異常");
} finally {
System.out.println("無(wú)論走try還是走catch, finally中的代碼都會(huì)被執(zhí)行");
}
java.lang.Throwable
既然要說(shuō)明白異常的前世今生就要先說(shuō)一說(shuō)這些異常的前世, java.lang.Throwable是 Java 中所有錯(cuò)誤或異常的超類。只有當(dāng)對(duì)象是此類(或其子類之一)的實(shí)例時(shí)姨伟,才能通過 JVM 或者 Java throw 語(yǔ)句來(lái)拋出, 換言之, 所有的異常都是從他這出來(lái)的, 他是所有異常的祖宗類.
而且, 類似的, 只有此類或其子類之一才可以是 catch 子句中的參數(shù)類型.
由圖可見, 他有兩個(gè)子類, 這兩個(gè)子類就是區(qū)別開了兩種不同的異常.
第一種.java.lang.Error
Error是 Throwable 的子類惩琉,用于指示合理的應(yīng)用程序不應(yīng)該試圖捕獲的嚴(yán)重問題, 這個(gè)問題有多嚴(yán)重? 十分嚴(yán)重, 不可恢復(fù)的錯(cuò)誤, 這種情況下的程序只能終止運(yùn)行, 但是要注意一點(diǎn)的是, 編譯器不會(huì)檢查Error是不是被處理, 程序中也不用捕獲Error類型的異常, 一般情況下, 程序中也是絕對(duì)不應(yīng)該拋出這種異常的, 但是一旦出現(xiàn)Error必須修改源代碼.
第二種.java.lang. Exception
Exception是由JVM發(fā)出的異常, 他的異常類型包括兩種, 一種是RuntimeException, 另外一種是Checked Exception異常或者說(shuō)是(記成)非RuntimeException類型的異常也可.
RuntimeException類型的異常是很特殊的, 他是一種Unchecked Exception, 編譯器不會(huì)檢查程序是不是處理了RuntimeException, 在程序中也不去捕獲RuntimeException類型的異常, 也不會(huì)再方法體中拋出RuntimeException. RuntimeException類型的異常出現(xiàn)的時(shí)候表示程序中出現(xiàn)了邏輯編程錯(cuò)誤, 作為程序員應(yīng)該找出錯(cuò)誤并且修改.
Checked Exception異常, 或者說(shuō)是非RuntimeException類型的異常, 是Exception異常種類中除了RuntimeException異常就是他了, 而JAVA規(guī)定了所有的Checked Exception異常都要處理, 編譯器也會(huì)檢查這種異常是否存在, 要么拋出, 要么try catch捕獲, 處理, 否則將不能通過編譯.
出現(xiàn)異常怎么辦
對(duì)于異常, 真正有用的是意向的對(duì)象類型, 而不是異常對(duì)象本身, 每一種異常對(duì)象都有著自己的含義, 老規(guī)矩, 為了便于理解, 上代碼!
public static double div(int a, int b){
return a / b;
}
// 然后調(diào)用
div(4, 0);
顯而易見的, 我們犯了一個(gè)很基礎(chǔ)的錯(cuò)誤, 那就是拿一個(gè)整數(shù)去除以0, 當(dāng)然, 我們親愛的JAVA不會(huì)慣著我們.
我們來(lái)看一下拋出的異常信息.
在這里我們會(huì)看到幾處有意思的東西.
- java.lang.ArithmeticException 這個(gè)東西好像是類?
- by zero 我好像是除以0了.
- at com.company.Main.div(Main.java:69)
at com.company.Main.main(Main.java:28) 這兩行.
我們首先來(lái)看看java.lang.ArithmeticException, 這個(gè)類的意思是當(dāng)出現(xiàn)異常的運(yùn)算條件時(shí)夺荒,拋出此異常瞒渠。就比如說(shuō)我們所做的,一個(gè)整數(shù)“除以零”時(shí)技扼,拋出此類的一個(gè)實(shí)例.
然后再看 by zero, 的確是除以0了, 沒毛病.
最后再看最后這兩行東西, div和main好像是你的兩個(gè)方法, Main是不是主入口呢? 你看了一下 69 行和 28 行
return a/b; // 69行
div(4, 0); // 28行
大呼神奇.
現(xiàn)在你能看懂異常了.
自定義異常
你以為看懂異常就結(jié)束了嗎? naive, 你不但要會(huì)看, 你還要會(huì)寫, 編程瞬息萬(wàn)變, 你怎么能確定JAVA就考慮到所有你可能出錯(cuò)的情況呢? 所以, 當(dāng)JAVA內(nèi)置的異常都不能明確的說(shuō)明異常情況的時(shí)候伍玖,需要?jiǎng)?chuàng)建自己的異常.
但是你需要注意的是, 唯一有用的就是類型名這個(gè)信息,所以不要在異常類的設(shè)計(jì)上花費(fèi)精力, 他只是去給你提供一個(gè)信息的.
還是上代碼來(lái)的清楚, 請(qǐng)看.
class Computer {
public String getStatus() {
return status;
}
public void setStatus(String status) throws DownException, EnterWaterException {
this.status = status;
if (status.equals("進(jìn)水")){
throw new EnterWaterException("進(jìn)水了, 燒了");
} else if (status.equals("摔了")){
throw new DownException("摔了, 有錢了");
}
}
private String status;
}
class DownException extends RuntimeException{
public DownException(String message){
super(message);
}
}
class EnterWaterException extends RuntimeException{
public EnterWaterException(String message){
super(message);
}
}
你可以清晰的看到我新建了兩個(gè)異常, 并把他放入一個(gè)叫做Computer的類中, 并讓 setStatus方法接著向上拋出異常.
Computer computer = new Computer();
try {
computer.setStatus("進(jìn)水");
} catch (EnterWaterException e){
e.printStackTrace();
} catch (DownException e){
e.printStackTrace();
} finally {
System.out.println("finally");
}
然后我try catch了異常. 來(lái)看一下輸出的結(jié)果.
怎么樣, 是不是結(jié)果顯而易見, 但是這里有幾點(diǎn)需要注意一下, 并且要記住.
- 對(duì)于應(yīng)該在聲明方法拋出異常還是在方法中捕獲異常, 我們應(yīng)該遵守一個(gè)原則, 捕捉并處理哪些知道如何處理的異常剿吻,而傳遞哪些不知道如何處理的異常
- 多個(gè)異常捕獲, 異常對(duì)象存在父子關(guān)系是, 要求父類型的異常, 寫在子類的下面
- 多個(gè)異城瞎浚可以合并在一個(gè)catch中 catch(Exception e), 但是為了看著清晰我沒有這么做.
- throws 是聲明這個(gè)方法的調(diào)用存在異常情況, 調(diào)用者需采取捕獲處理或繼續(xù)向外拋的操作才能通過編譯
- 當(dāng)代碼出現(xiàn)問題時(shí), JVM會(huì)創(chuàng)建一個(gè)異常對(duì)象并拋個(gè)調(diào)用者
- 調(diào)用者接收到異常之后不能處理, 繼續(xù)往上拋直到拋給JVM, 最終將異常信息輸出到控制臺(tái)
這里是一些常見的異常, 方便與在大家在拋出異常的時(shí)候來(lái)對(duì)照:
ArithmeticException——由于除數(shù)為0引起的異常;
ArrayStoreException——由于數(shù)組存儲(chǔ)空間不夠引起的異常丽旅;
ClassCastException—一當(dāng)把一個(gè)對(duì)象歸為某個(gè)類椰棘,但實(shí)際上此對(duì)象并不是由這個(gè)類 創(chuàng)建的,也不是其子類創(chuàng)建的榄笙,則會(huì)引起異常邪狞;
IllegalMonitorStateException——監(jiān)控器狀態(tài)出錯(cuò)引起的異常;
NegativeArraySizeException—一數(shù)組長(zhǎng)度是負(fù)數(shù)办斑,則產(chǎn)生異常外恕;
NullPointerException—一程序試圖訪問一個(gè)空的數(shù)組中的元素或訪問空的對(duì)象中的 方法或變量時(shí)產(chǎn)生異常杆逗;
OutofMemoryException——用new語(yǔ)句創(chuàng)建對(duì)象時(shí)乡翅,如系統(tǒng)無(wú)法為其分配內(nèi)存空 間則產(chǎn)生異常;
SecurityException——由于訪問了不應(yīng)訪問的指針罪郊,使安全性出問題而引起異常蠕蚜;
IndexOutOfBoundsExcention——由于數(shù)組下標(biāo)越界或字符串訪問越界引起異常;
IOException——由于文件未找到悔橄、未打開或者I/O操作不能進(jìn)行而引起異常靶累;
ClassNotFoundException——未找到指定名字的類或接口引起異常;
CloneNotSupportedException——一程序中的一個(gè)對(duì)象引用Object類的clone方法癣疟,但 此對(duì)象并沒有連接Cloneable接口挣柬,從而引起異常;
InterruptedException—一當(dāng)一個(gè)線程處于等待狀態(tài)時(shí)睛挚,另一個(gè)線程中斷此線程邪蛔,從 而引起異常;
NoSuchMethodException一所調(diào)用的方法未找到扎狱,引起異常侧到;
IllegalAccessExcePtion—一試圖訪問一個(gè)非public方法勃教;
StringIndexOutOfBoundsException——訪問字符串序號(hào)越界,引起異常匠抗;
ArrayIdexOutOfBoundsException—一訪問數(shù)組元素下標(biāo)越界故源,引起異常;
NumberFormatException——字符的UTF代碼數(shù)據(jù)格式有錯(cuò)引起異常汞贸;
IllegalThreadException—一線程調(diào)用某個(gè)方法而所處狀態(tài)不適當(dāng)绳军,引起異常;
FileNotFoundException——未找到指定文件引起異常著蛙;
EOFException——未完成輸入操作即遇文件結(jié)束引起異常删铃。
怎么樣, 大家是不是對(duì)于異常都已經(jīng)有了自己的理解了呢, 希望大家要達(dá)道下圖的水平.
我們作為程序員, 每天都在想的無(wú)非就是我怎么把代碼敲得更牛逼, 更美觀, 而我一直認(rèn)為知識(shí), 見解, 經(jīng)驗(yàn)的分享和學(xué)習(xí)是自我進(jìn)行學(xué)習(xí)的一個(gè)重要的途徑, 所以, 知識(shí)的分享總會(huì)增值, 非常期待與各位看官切磋想法, 交流心得.
個(gè)人其他文章:
個(gè)人編程想法心得(不定期更新)
開發(fā)筆記之詳述JAVA構(gòu)造函數(shù)和代碼塊本身及其執(zhí)行細(xì)節(jié)
開發(fā)筆記之冒泡排序, 選擇排序, 折半查找
iOS開發(fā)筆記-基于AFNetworking 3.0的登錄 注冊(cè)
開發(fā)筆記之JAVA String StringBuffer StringBuilder
開發(fā)筆記之JAVA ArrayList 和 LinkedList