深入理解Java中异常体系
任何程序都追求正確有效的運(yùn)行,除了保證我們代碼盡可能的少出錯(cuò)之外,我們還要考慮如何有效的處理異常,一個(gè)良好的異常框架對(duì)于系統(tǒng)來(lái)說(shuō)是至關(guān)重要的。最近在給公司寫采集框架的時(shí)候系統(tǒng)的了解一邊,收獲頗多,特此記錄相關(guān)的理論。
1 .異常體系簡(jiǎn)介:
異常是指由于各種不期而至的情況,導(dǎo)致程序中斷運(yùn)行的一種指令流,如:文件找不到、非法參數(shù)、網(wǎng)絡(luò)超時(shí)等。為了保證正序正常運(yùn)行,在設(shè)計(jì)程序時(shí)必須考慮到各種異常情況,并正確的對(duì)異常進(jìn)行處理。異常也是一種對(duì)象,java當(dāng)中定義了許多異常類,并且定義了基類java.lang.Throwable作為所有異常的超類。Java語(yǔ)言設(shè)計(jì)者將異常劃分為兩類:Error和Exception,其體系結(jié)構(gòu)大致如下圖所示:
Throwable:有兩個(gè)重要的子類:Exception(異常)和Error(錯(cuò)誤),兩者都包含了大量的異常處理類。
Error(錯(cuò)誤):是程序中無(wú)法處理的錯(cuò)誤。
表示運(yùn)行應(yīng)用程序中出現(xiàn)了嚴(yán)重的錯(cuò)誤。此類錯(cuò)誤一般表示代碼運(yùn)行時(shí)JVM出現(xiàn)問(wèn)題。通常有Virtual MachineError(虛擬機(jī)運(yùn)行錯(cuò)誤)、NoClassDefFoundError(類定義錯(cuò)誤)等。比如說(shuō)當(dāng)jvm耗完可用內(nèi)存時(shí),將出現(xiàn)OutOfMemoryError。此類錯(cuò)誤發(fā)生時(shí),JVM將終止線程。這些錯(cuò)誤是不可查的,非代碼性錯(cuò)誤。因此,當(dāng)此類錯(cuò)誤發(fā)生時(shí),應(yīng)用不應(yīng)該去處理此類錯(cuò)誤。
Exception(異常):程序本身可以捕獲并且可以處理的異常。
Exception這種異常又分為兩類:運(yùn)行時(shí)異常和編譯異常。
可查異常與不可查異常:java的所有異??梢苑譃榭刹楫惓?#xff08;checked exception)和不可查異常(unchecked exception)。
2.異常處理流程:
在java應(yīng)用中,異常的處理機(jī)制分為拋出異常和捕獲異常。
當(dāng)一個(gè)方法出現(xiàn)錯(cuò)誤而引發(fā)異常時(shí),該方法會(huì)將該異常類型以及異常出現(xiàn)時(shí)的程序狀態(tài)信息封裝為異常對(duì)象,并交給本應(yīng)用。運(yùn)行時(shí),該應(yīng)用將尋找處理異常的代碼并執(zhí)行。任何代碼都可以通過(guò)throw關(guān)鍵詞拋出異常,比如java源代碼拋出異常、自己編寫的代碼拋出異常等。
一旦方法拋出異常,系統(tǒng)自動(dòng)根據(jù)該異常對(duì)象尋找合適異常處理器(Exception Handler)來(lái)處理該異常。所謂合適類型的異常處理器指的是異常對(duì)象類型和異常處理器類型一致。
對(duì)于不同的異常,java采用不同的異常處理方式:
- 2.1捕獲異常
1、try-catch語(yǔ)句
try {//可能產(chǎn)生的異常的代碼區(qū),也成為監(jiān)控區(qū)}catch (ExceptionType1 e) {//捕獲并處理try拋出異常類型為ExceptionType1的異常}catch(ExceptionType2 e) {//捕獲并處理try拋出異常類型為ExceptionType2的異常}監(jiān)控區(qū)一旦發(fā)生異常,則會(huì)根據(jù)當(dāng)前運(yùn)行時(shí)的信息創(chuàng)建異常對(duì)象,并將該異常對(duì)象拋出監(jiān)控區(qū),同時(shí)系統(tǒng)根據(jù)該異常對(duì)象依次匹配catch子句,若匹配成功(拋出的異常對(duì)象的類型和catch子句的異常類的類型或者是該異常類的子類的類型一致),則運(yùn)行其中catch代碼塊中的異常處理代碼,一旦處理結(jié)束,那就意味著整個(gè)try-catch結(jié)束。含有多個(gè)catch子句,一旦其中一個(gè)catch子句與拋出的異常對(duì)象類型一致時(shí),其他catch子句將不再有匹配異常對(duì)象的機(jī)會(huì)。
2、try-catch-finally
try {//可能產(chǎn)生的異常的代碼區(qū)}catch (ExceptionType1 e) {//捕獲并處理try拋出異常類型為ExceptionType1的異常}catch (ExceptionType2 e){//捕獲并處理try拋出異常類型為ExceptionType2的異常}finally{//無(wú)論是出現(xiàn)異常,finally塊中的代碼都將被執(zhí)行}3、try-catch-finally代碼塊的執(zhí)行順序:
4、總結(jié)
try代碼塊:用于捕獲異常。其后可以接零個(gè)或者多個(gè)catch塊。如果沒(méi)有catch塊,后必須跟finally塊,來(lái)完成資源釋放等操作,另外建議不要在finally中使用return,不用嘗試通過(guò)catch來(lái)控制代碼流程。
catch代碼塊:用于捕獲異常,并在其中處理異常。
finally代碼塊:無(wú)論是否捕獲異常,finally代碼總會(huì)被執(zhí)行。如果try代碼塊或者catch代碼塊中有return語(yǔ)句時(shí),finally代碼塊將在方法返回前被執(zhí)行。注意以下幾種情況,finally代碼塊不會(huì)被執(zhí)行:
在前邊的代碼中使用System.exit()退出應(yīng)用。
程序所在的線程死亡或者cpu關(guān)閉
如果在finally代碼塊中的操作又產(chǎn)生異常,則該finally代碼塊不能完全執(zhí)行結(jié)束,同時(shí)該異常會(huì)覆蓋前邊拋出的異常。
2.2拋出異常
1、throws拋出異常
如果一個(gè)方法可能拋出異常,但是沒(méi)有能力處理該異?;蛘咝枰ㄟ^(guò)該異常向上層匯報(bào)處理結(jié)果,可以在方法聲明時(shí)使用throws來(lái)拋出異常。這就相當(dāng)于計(jì)算機(jī)硬件發(fā)生損壞,但是計(jì)算機(jī)本身無(wú)法處理,就將該異常交給維修人員來(lái)處理。
publicmethodName throws Exception1,Exception2….(params){}其中Exception1,Exception2…為異常列表一旦該方法中某行代碼拋出異常,則該異常將由調(diào)用該方法的上層方法處理。如果上層方法無(wú)法處理,可以繼續(xù)將該異常向上層拋。
2、throw拋出異常
在方法內(nèi),用throw來(lái)拋出一個(gè)Throwable類型的異常。一旦遇到到throw語(yǔ)句,后面的代碼將不被執(zhí)行。然后,便是進(jìn)行異常處理——包含該異常的try-catch最終處理,也可以向上層拋出。注意我們只能拋出Throwable類和其子類的對(duì)象。
throw newExceptionType;比如我們可以拋出:throw new Exception();
也有時(shí)候我們也需要在catch中拋出異常,這也是允許的,比如說(shuō):
Try{ //可能會(huì)發(fā)生異常的代碼}catch(Exceptione){throw newException(e);}3、異常關(guān)系鏈
在實(shí)際開(kāi)發(fā)過(guò)程中經(jīng)常在捕獲一個(gè)異常之后拋出另外一個(gè)異常,并且我們希望在新的異常對(duì)象中保存原始異常對(duì)象的信息,實(shí)際上就是異常傳遞,即把底層的異常對(duì)象傳給上層,一級(jí)一級(jí),逐層拋出。當(dāng)程序捕獲了一個(gè)底層的異常,而在catch處理異常的時(shí)候選擇將該異常拋給上層…這樣異常的原因就會(huì)逐層傳遞,形成一個(gè)由低到高的異常鏈。但是異常鏈在實(shí)際應(yīng)用中一般不建議使用,同時(shí)異常鏈每次都需要就將原始的異常對(duì)象封裝為新的異常對(duì)象,消耗大量資源?,F(xiàn)在(jdk 1.4之后)所有的Throwable的子類構(gòu)造中都可以接受一個(gè)cause對(duì)象,這個(gè)cause也就是原始的異常對(duì)象。
下面是一個(gè)不錯(cuò)的例子:
/**高層異常*/classHighLevelExceptionextends Exception{public HighLevelException(Throwable cause) {super(cause);}}/**中層異常*/classMiddleLevelExceptionextends Exception{public MiddleLevelException(Throwable cause) {super(cause);}}/**底層異常*/classLowLevelExceptionextends Exception{}publicclass TestException {publicvoid highLevelAccess()throws HighLevelException{try {middleLevelAccess();}catch (Exception e) {thrownew HighLevelException(e);}}publicvoid middleLevelAccess()throws MiddleLevelException{try {lowLevelAccess();}catch (Exception e) {thrownew MiddleLevelException(e);}}publicvoid lowLevelAccess()throws LowLevelException {thrownew LowLevelException();}publicstaticvoid main(String[] args) {/** lowlevelAccess()將異常對(duì)象拋給middleLevelAccess(),而* middleLevelAccess()又將異常對(duì)象拋給highLevelAccess(),*也就是底層的異常對(duì)象一層層傳遞給高層。最終在在高層可以獲得底層的異常對(duì)象。*/try {new TestException().highLevelAccess();}catch (HighLevelException e) {e.printStackTrace();System.out.println(e.getCause());}} }4、異常轉(zhuǎn)譯
異常轉(zhuǎn)義就是將一種類型的異常轉(zhuǎn)成另一種類型的異常,然后再拋出異常。之所以要進(jìn)行轉(zhuǎn)譯,是為了更準(zhǔn)確的描述異常。就我個(gè)人而言,我更喜歡稱之為異常類型轉(zhuǎn)換。在實(shí)際應(yīng)用中,為了構(gòu)建自己的日志系統(tǒng),經(jīng)常需要把系統(tǒng)的一些異常信息描述成我們想要的異常信息,就可以使用異常轉(zhuǎn)譯。異常轉(zhuǎn)譯針對(duì)所有Throwable類的子類而言,其子類型都可以相互轉(zhuǎn)換。
通常而言,更為合理的轉(zhuǎn)換方式是:
Error——>Exception
Error——>RuntimeException
Exception——>RuntimeException,
在下面的代碼中,我們自定義了MyException異常類,然后我們將IOException類型的異常轉(zhuǎn)為MyException類型的異常,最后拋出。
class MyExceptionextends Exception {public MyException(String msg, Throwable e) {super(msg, e);} } publicclass Demo {publicstaticvoid main(String[] args)throws MyException {Filefile =new File("H:/test.txt");if (file.exists())try {file.createNewFile();}catch (IOException e) {thrownew MyException("文件創(chuàng)建失敗!", e);}} }5、Throwable類中常用的方法
像catch(Exception e)中的Exception就是異常的變量類型,e則是形參。通常在進(jìn)行異常輸出時(shí)有如下幾個(gè)方法可用:
e.getCause():返回拋出異常的原因。
e.getMessage():返回異常信息。
e.printStackTrace():發(fā)生異常時(shí),跟蹤堆棧信息并輸出。
6、 常異總結(jié)
此部分可以api文檔中進(jìn)行查閱,這里僅做參考。
常見(jiàn)異常:
java.lang.IllegalAccessError:違法訪問(wèn)錯(cuò)誤。當(dāng)一個(gè)應(yīng)用試圖訪問(wèn)、修改某個(gè)類的域(Field)或者調(diào)用其方法,但是又違反域或方法的可見(jiàn)性聲明,則拋出該異常。
java.lang.InstantiationError:實(shí)例化錯(cuò)誤。當(dāng)一個(gè)應(yīng)用試圖通過(guò)Java的new操作符構(gòu)造一個(gè)抽象類或者接口時(shí)拋出該異常.
java.lang.OutOfMemoryError:內(nèi)存不足錯(cuò)誤。當(dāng)可用內(nèi)存不足以讓Java虛擬機(jī)分配給一個(gè)對(duì)象時(shí)拋出該錯(cuò)誤。
java.lang.StackOverflowError:堆棧溢出錯(cuò)誤。當(dāng)一個(gè)應(yīng)用遞歸調(diào)用的層次太深而導(dǎo)致堆棧溢出或者陷入死循環(huán)時(shí)拋出該錯(cuò)誤。
java.lang.ClassCastException:類造型異常。假設(shè)有類A和B(A不是B的父類或子類),O是A的實(shí)例,那么當(dāng)強(qiáng)制將O構(gòu)造為類B的實(shí)例時(shí)拋出該異常。該異常經(jīng)常被稱為強(qiáng)制類型轉(zhuǎn)換異常。
java.lang.ClassNotFoundException:找不到類異常。當(dāng)應(yīng)用試圖根據(jù)字符串形式的類名構(gòu)造類,而在遍歷CLASSPAH之后找不到對(duì)應(yīng)名稱的class文件時(shí),拋出該異常。
java.lang.ArithmeticException:算術(shù)條件異常。譬如:整數(shù)除零等。
java.lang.ArrayIndexOutOfBoundsException:數(shù)組索引越界異常。當(dāng)對(duì)數(shù)組的索引值為負(fù)數(shù)或大于等于數(shù)組大小時(shí)拋出。
java.lang.IndexOutOfBoundsException:索引越界異常。當(dāng)訪問(wèn)某個(gè)序列的索引值小于0或大于等于序列大小時(shí),拋出該異常。
java.lang.InstantiationException:實(shí)例化異常。當(dāng)試圖通過(guò)newInstance()方法創(chuàng)建某個(gè)類的實(shí)例,而該類是一個(gè)抽象類或接口時(shí),拋出該異常。
java.lang.NoSuchFieldException:屬性不存在異常。當(dāng)訪問(wèn)某個(gè)類的不存在的屬性時(shí)拋出該異常。
java.lang.NoSuchMethodException:方法不存在異常。當(dāng)訪問(wèn)某個(gè)類的不存在的方法時(shí)拋出該異常。
java.lang.NullPointerException:空指針異常。當(dāng)應(yīng)用試圖在要求使用對(duì)象的地方使用了null時(shí),拋出該異常。譬如:調(diào)用null對(duì)象的實(shí)例方法、訪問(wèn)null對(duì)象的屬性、計(jì)算null對(duì)象的長(zhǎng)度、使用throw語(yǔ)句拋出null等等。
java.lang.NumberFormatException:數(shù)字格式異常。當(dāng)試圖將一個(gè)String轉(zhuǎn)換為指定的數(shù)字類型,而該字符串確不滿足數(shù)字類型要求的格式時(shí),拋出該異常。
java.lang.StringIndexOutOfBoundsException:字符串索引越界異常。當(dāng)使用索引值訪問(wèn)某個(gè)字符串中的字符,而該索引值小于0或大于等于序列大小時(shí),拋出該異常。
其他異常:
java.lang.AbstractMethodError:抽象方法錯(cuò)誤。當(dāng)應(yīng)用試圖調(diào)用抽象方法時(shí)拋出。
java.lang.AssertionError:斷言錯(cuò)。用來(lái)指示一個(gè)斷言失敗的情況。
java.lang.ClassCircularityError:類循環(huán)依賴錯(cuò)誤。在初始化一個(gè)類時(shí),若檢測(cè)到類之間循環(huán)依賴則拋出該異常。
java.lang.ClassFormatError:類格式錯(cuò)誤。當(dāng)Java虛擬機(jī)試圖從一個(gè)文件中讀取Java類,而檢測(cè)到該文件的內(nèi)容不符合類的有效格式時(shí)拋出。
java.lang.Error:錯(cuò)誤。是所有錯(cuò)誤的基類,用于標(biāo)識(shí)嚴(yán)重的程序運(yùn)行問(wèn)題。這些問(wèn)題通常描述一些不應(yīng)被應(yīng)用程序捕獲的反常情況。
java.lang.ExceptionInInitializerError:初始化程序錯(cuò)誤。當(dāng)執(zhí)行一個(gè)類的靜態(tài)初始化程序的過(guò)程中,發(fā)生了異常時(shí)拋出。靜態(tài)初始化程序是指直接包含于類中的static語(yǔ)句段。
java.lang.IncompatibleClassChangeError:不兼容的類變化錯(cuò)誤。當(dāng)正在執(zhí)行的方法所依賴的類定義發(fā)生了不兼容的改變時(shí),拋出該異常。一般在修改了應(yīng)用中的某些類的聲明定義而沒(méi)有對(duì)整個(gè)應(yīng)用重新編譯而直接運(yùn)行的情況下,容易引發(fā)該錯(cuò)誤。
java.lang.InternalError:內(nèi)部錯(cuò)誤。用于指示Java虛擬機(jī)發(fā)生了內(nèi)部錯(cuò)誤。
java.lang.LinkageError:鏈接錯(cuò)誤。該錯(cuò)誤及其所有子類指示某個(gè)類依賴于另外一些類,在該類編譯之后,被依賴的類改變了其類定義而沒(méi)有重新編譯所有的類,進(jìn)而引發(fā)錯(cuò)誤的情況。
java.lang.NoClassDefFoundError:未找到類定義錯(cuò)誤。當(dāng)Java虛擬機(jī)或者類裝載器試圖實(shí)例化某個(gè)類,而找不到該類的定義時(shí)拋出該錯(cuò)誤。
java.lang.NoSuchFieldError:域不存在錯(cuò)誤。當(dāng)應(yīng)用試圖訪問(wèn)或者修改某類的某個(gè)域,而該類的定義中沒(méi)有該域的定義時(shí)拋出該錯(cuò)誤。
java.lang.NoSuchMethodError:方法不存在錯(cuò)誤。當(dāng)應(yīng)用試圖調(diào)用某類的某個(gè)方法,而該類的定義中沒(méi)有該方法的定義時(shí)拋出該錯(cuò)誤。
java.lang.ThreadDeath:線程結(jié)束。當(dāng)調(diào)用Thread類的stop方法時(shí)拋出該錯(cuò)誤,用于指示線程結(jié)束。
java.lang.UnknownError:未知錯(cuò)誤。用于指示Java虛擬機(jī)發(fā)生了未知嚴(yán)重錯(cuò)誤的情況。
java.lang.UnsatisfiedLinkError:未滿足的鏈接錯(cuò)誤。當(dāng)Java虛擬機(jī)未找到某個(gè)類的聲明為native方法的本機(jī)語(yǔ)言定義時(shí)拋出。
java.lang.UnsupportedClassVersionError:不支持的類版本錯(cuò)誤。當(dāng)Java虛擬機(jī)試圖從讀取某個(gè)類文件,但是發(fā)現(xiàn)該文件的主、次版本號(hào)不被當(dāng)前Java虛擬機(jī)支持的時(shí)候,拋出該錯(cuò)誤。
java.lang.VerifyError:驗(yàn)證錯(cuò)誤。當(dāng)驗(yàn)證器檢測(cè)到某個(gè)類文件中存在內(nèi)部不兼容或者安全問(wèn)題時(shí)拋出該錯(cuò)誤。
java.lang.VirtualMachineError:虛擬機(jī)錯(cuò)誤。用于指示虛擬機(jī)被破壞或者繼續(xù)執(zhí)行操作所需的資源不足的情況。
java.lang.ArrayStoreException:數(shù)組存儲(chǔ)異常。當(dāng)向數(shù)組中存放非數(shù)組聲明類型對(duì)象時(shí)拋出。
java.lang.CloneNotSupportedException:不支持克隆異常。當(dāng)沒(méi)有實(shí)現(xiàn)Cloneable接口或者不支持克隆方法時(shí),調(diào)用其clone()方法則拋出該異常。
java.lang.EnumConstantNotPresentException:枚舉常量不存在異常。當(dāng)應(yīng)用試圖通過(guò)名稱和枚舉類型訪問(wèn)一個(gè)枚舉對(duì)象,但該枚舉對(duì)象并不包含常量時(shí),拋出該異常。
java.lang.Exception:根異常。用以描述應(yīng)用程序希望捕獲的情況。
java.lang.IllegalAccessException:違法的訪問(wèn)異常。當(dāng)應(yīng)用試圖通過(guò)反射方式創(chuàng)建某個(gè)類的實(shí)例、訪問(wèn)該類屬性、調(diào)用該類方法,而當(dāng)時(shí)又無(wú)法訪問(wèn)類的、屬性的、方法的或構(gòu)造方法的定義時(shí)拋出該異常。
java.lang.IllegalMonitorStateException:違法的監(jiān)控狀態(tài)異常。當(dāng)某個(gè)線程試圖等待一個(gè)自己并不擁有的對(duì)象(O)的監(jiān)控器或者通知其他線程等待該對(duì)象(O)的監(jiān)控器時(shí),拋出該異常。
java.lang.IllegalStateException:違法的狀態(tài)異常。當(dāng)在Java環(huán)境和應(yīng)用尚未處于某個(gè)方法的合法調(diào)用狀態(tài),而調(diào)用了該方法時(shí),拋出該異常。
java.lang.IllegalThreadStateException:違法的線程狀態(tài)異常。當(dāng)縣城尚未處于某個(gè)方法的合法調(diào)用狀態(tài),而調(diào)用了該方法時(shí),拋出異常。
java.lang.InterruptedException:被中止異常。當(dāng)某個(gè)線程處于長(zhǎng)時(shí)間的等待、休眠或其他暫停狀態(tài),而此時(shí)其他的線程通過(guò)Thread的interrupt方法終止該線程時(shí)拋出該異常。
java.lang.NegativeArraySizeException:數(shù)組大小為負(fù)值異常。當(dāng)使用負(fù)數(shù)大小值創(chuàng)建數(shù)組時(shí)拋出該異常。
java.lang.SecurityException:安全異常。由安全管理器拋出,用于指示違反安全情況的異常。
java.lang.TypeNotPresentException:類型不存在異常。
文章轉(zhuǎn)自
總結(jié)
以上是生活随笔為你收集整理的深入理解Java中异常体系的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 为什么不推荐使用 select * ?是
- 下一篇: 真正厉害的产品经理,都是“数据思维”的高