異常分類
如果某個(gè)方法不能按照正常的途徑完成任務(wù),就可以通過另一種路徑退出方法表悬。在這種情況下會(huì)拋出一個(gè)封裝了錯(cuò)誤信息的對(duì)象弥锄。此時(shí),這個(gè)方法會(huì)立刻退出同時(shí)不返回任何值蟆沫。另外,調(diào)用這個(gè)方法的其他代碼也無法繼續(xù)執(zhí)行温治,異常處理機(jī)制會(huì)將代碼執(zhí)行交給異常處理器饭庞。
Throwable
Throwable 是 Java 語言中所有錯(cuò)誤或異常的超類。
也就是說熬荆,如果你要拋出或者聲明一個(gè)異常舟山,那么,這個(gè)異常必須繼承Throwable.
public class Main {
public static void main(String[] args) {
}
public void testMyError() throws MyError{
throw new MyError();
}
public void testMyErrorNon() throws MyErrorNon {
throw new MyErrorNon();
}
public void testMyException() throws MyException {
throw new MyException();
}
public void testMyExceptionNon() throws MyExceptionNon{
throw new MyExceptionNon();
}
}
Error
Error 類是指 java 運(yùn)行時(shí)系統(tǒng)的內(nèi)部錯(cuò)誤和資源耗盡錯(cuò)誤。應(yīng)用程序不會(huì)拋出該類對(duì)象累盗。如果出現(xiàn)了這樣的錯(cuò)誤寒矿,除了告知用戶,剩下的就是盡力使程序安全的終止.
Exception
Exception 又 有 兩 個(gè) 分 支 若债, 一 個(gè) 是 運(yùn) 行 時(shí) 異 常 RuntimeException 符相, 一個(gè)是CheckedException.
RuntimeExceptionn
運(yùn)行時(shí)異常
如 : NullPointerException 、 ClassCastException 蠢琳; 一 個(gè) 是 檢 查 異 常CheckedException啊终,如 I/O 錯(cuò)誤導(dǎo)致的 IOException、SQLException傲须。 RuntimeException 是那些可能在 Java 虛擬機(jī)正常運(yùn)行期間拋出的異常的超類蓝牲。 如果出現(xiàn) RuntimeException,那么一定是程序員的錯(cuò)誤.
CheckedException
檢查異常
一般是外部錯(cuò)誤泰讽,這種異常都發(fā)生在編譯階段例衍,Java 編譯器會(huì)強(qiáng)制程序去捕獲此類異常,即會(huì)出現(xiàn)要求你把這段可能出現(xiàn)異常的程序進(jìn)行 try catch已卸,該類異常一
般包括幾個(gè)方面:
- 試圖在文件尾部讀取數(shù)據(jù)
- 試圖打開一個(gè)錯(cuò)誤格式的 URL
- 試圖根據(jù)給定的字符串查找 class 對(duì)象肄渗,而這個(gè)字符串表示的類并不存在
異常處理方式
throw
throws
jvm自動(dòng)拋出
throw和throws的異同
相同點(diǎn):
- 都繼承Throwable
- 程序的執(zhí)行順序收到影響
- 不一定每次都出現(xiàn),概率不確定
- 消極處理異常的方式咬最,只是拋出或者可能拋出異常翎嫡,但是不會(huì)由方法去處理異常,真正的處理異常常有方法的調(diào)用者處理永乌。
不同點(diǎn):
- 位置不同:
- ==throws用在方法上== 惑申,后面跟的是異常類,可以跟多個(gè)翅雏;==而throw用在方法內(nèi)==圈驼,后面跟的是異常對(duì)象。
- 功能不同:
- throws用來聲明異常望几,==讓調(diào)用者知道該方法可能出現(xiàn)的問題==绩脆,可以給出預(yù)先的處理方式;==throw拋出具體的異常問題對(duì)象==橄抹,執(zhí)行到throw靴迫,就無法在繼續(xù)按正常的順序繼續(xù)執(zhí)行了。跳轉(zhuǎn)到調(diào)用者楼誓,并將具體的問題對(duì)象拋給調(diào)用者玉锌。
- throws表示==出現(xiàn)異常的一種可能性==,并不一定會(huì)發(fā)生這些異常疟羹;throw則是==拋出了異常==主守,執(zhí)行throw則一定拋出了某種異常禀倔。
異常的捕獲
public class Main {
public static void main(String[] args) {
try {
testcatch();
} catch (MyException4 myException4) {
myException4.printStackTrace();
} catch (MyException3 myException3) {
myException3.printStackTrace();
} catch (MyException2 myException2) {
myException2.printStackTrace();
} catch (MyException1 myException1) {
myException1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static void testcatch() throws Throwable, MyException4, MyException3, MyException2, MyException1, Exception, Error {
}
}
異常的捕獲,最靠近try的参淫,異常范圍是最小的救湖。異常匹配時(shí),從上到下進(jìn)行匹配涎才,如果將范圍大的異常捕獲放在前面鞋既,那么后面比這個(gè)異常范圍小的異常,永遠(yuǎn)得不到調(diào)用憔维。
異常調(diào)用鏈
每個(gè)方法內(nèi)部出現(xiàn)異常后涛救,要么方法內(nèi)部使用try捕獲異常,方法內(nèi)解決這個(gè)異常业扒。使程序繼續(xù)運(yùn)行下去检吆。
要么將異常拋出,由方法的調(diào)用者處理這個(gè)異常程储。
public class OMain {
public static void main(String[] args) {
try {
new OMain().test4();
} catch (MyException4 myException4) {
myException4.printStackTrace();
}
}
public void test4() throws MyException4 {
try {
test3();
} catch (MyException3 myException3) {
throw new MyException4();
}
}
public void test3() throws MyException3 {
try {
test2();
} catch (MyException2 myException2) {
throw new MyException3();
}
}
public void test2() throws MyException2 {
try {
test1();
} catch (MyException1 myException1) {
throw new MyException2();
}
}
public void test1() throws MyException1 {
try {
test();
} catch (Exception e) {
throw new MyException1();
}
}
public void test() throws Exception {
throw new Exception();
}
}
在這個(gè)例子中蹭沛,真正的異常是在test方法中出現(xiàn)的,但是因?yàn)槊恳粋€(gè)方法都在自己的方法內(nèi)部捕獲了下層異常章鲤,然后拋出自己的異常摊灭。
此時(shí)整個(gè)異常已經(jīng)失真了,從異常堆棧根本無法知道問題出在哪里:
異常調(diào)用鏈的完整败徊,可以幫助我們更快的定位問題帚呼。
public class OMain {
public static void main(String[] args) {
try {
new OMain().test4();
} catch (MyException4 myException4) {
myException4.printStackTrace();
}
}
public void test4() throws MyException4 {
try {
test3();
} catch (MyException3 myException3) {
MyException4 myException4 = new MyException4();
myException4.initCause(myException3);
throw myException4;
}
}
public void test3() throws MyException3 {
try {
test2();
} catch (MyException2 myException2) {
MyException3 myException3 = new MyException3();
myException3.initCause(myException2);
throw myException3;
}
}
public void test2() throws MyException2 {
try {
test1();
} catch (MyException1 myException1) {
MyException2 myException2 = new MyException2();
myException2.initCause(myException1);
throw myException2;
}
}
public void test1() throws MyException1 {
try {
test();
} catch (Exception e) {
MyException1 myException1 = new MyException1();
myException1.initCause(e);
throw myException1;
}
}
public void test() throws Exception {
throw new Exception();
}
}
在Throwable中,有一個(gè)屬性cause,默認(rèn)是自己皱蹦。這個(gè)屬性就是記錄當(dāng)前異常的上一個(gè)異常是誰煤杀。如果cause就是自己,那么沪哺,說明這個(gè)異常就是最先拋出的異常沈自。
這樣整個(gè)異常調(diào)用鏈就完整了:
其調(diào)用關(guān)系,可以完整的從堆棧中看出辜妓。
這是最常見的場(chǎng)景枯途。
我們假設(shè)這個(gè)調(diào)用是上下級(jí)的調(diào)用:
test4->test3->test2->test1->test
其中越是下級(jí),做的工作越簡(jiǎn)單籍滴,也越是底層方法的調(diào)用酪夷。在底層方法得到調(diào)用中,就會(huì)出現(xiàn)各種各樣的異常(什么異常都有可能)
在例子中出現(xiàn)的是Exception异逐。
在test方法中捶索,可能完全不明白為什么出現(xiàn)異常,或者說灰瞻,出現(xiàn)異常的原因是什么腥例,如何解決?這些問題在test方法中都無法解決的酝润。
于是test方法將自己無法解決的異常拋出燎竖。
在test1方法中,捕獲到了異常要销,可能test1交給test是讀取一個(gè)文件构回,結(jié)果出現(xiàn)了文件沒找到的異常。那么疏咐,在test1方法中就知道了纤掸,是文件沒找到,但是浑塞,對(duì)于沒找到的文件借跪,該如何處理呢?test1就不知道了酌壕,于是掏愁,繼續(xù)向上拋出異常,此時(shí)異常被進(jìn)行細(xì)化卵牍,由文件沒找到的異常轉(zhuǎn)換為文件讀取失敗果港。
在test2方法中,捕獲到了異常糊昙,可能test2交給test1是讀取一個(gè)文件夾的文件辛掠,結(jié)果出現(xiàn)了部分文件未找到。那么在test2方法中释牺,我們知道了萝衩,是部分文件沒找到,而不是文件夾內(nèi)全部的文件沒找到船侧。在test2方法中欠气,可以確定,是某一個(gè)文件沒有找到镜撩,那么對(duì)于這個(gè)文件预柒,我們是要忽略,還是重試呢袁梗?test2也不知道宜鸯,此時(shí)只能轉(zhuǎn)換異常,然后繼續(xù)拋出遮怜。轉(zhuǎn)換的異常更加細(xì)化淋袖,部分文件未找到。
锯梁。即碗。焰情。。
以此類推剥懒,最終異常被交給了主線程内舟,當(dāng)主線程也無法確認(rèn)后,將問題拋給用戶初橘,由用戶決定验游。用戶決定如何處理。
這個(gè)過程可以類比我們使用操作系統(tǒng)復(fù)制整個(gè)文件夾的過程保檐。
里面是一層層的劃分耕蝉,當(dāng)出現(xiàn)無法抉擇的異常,最終拋給用戶決定夜只。
遇到可以抉擇的異常垒在,直接處理。(一個(gè)文件失敗自動(dòng)重試3次盐肃。爪膊。)
將異常交由調(diào)用者處理,是非常正確的處理方式砸王。\
==這種方式是金字塔結(jié)構(gòu)推盛,越是底層的方法,異常范圍越大谦铃。==
==還有一種是倒金字塔結(jié)構(gòu)耘成,越是底層的方法,異常越小驹闰。==對(duì)于這種結(jié)構(gòu)瘪菌,無需做任何捕獲,直接拋出即可嘹朗。
public class OMain {
public static void main(String[] args) {
try {
new OMain().test();
} catch (Throwable e) {
e.printStackTrace();
}
}
public void test4() throws MyException4 {
throw new MyException4();
}
public void test3() throws MyException3 {
test4();
}
public void test2() throws MyException2 {
test3();
}
public void test1() throws MyException1 {
test2();
}
public void test() throws Throwable {
test1();
}
}