Java提高篇——Java 异常处理
異常的概念
異常是程序中的一些錯誤,但并不是所有的錯誤都是異常,并且錯誤有時候是可以避免的。
比如說,你的代碼少了一個分號,那么運行出來結(jié)果是提示是錯誤java.lang.Error;如果你用System.out.println(11/0),那么你是因為你用0做了除數(shù),會拋出java.lang.ArithmeticException的異常。
異常發(fā)生的原因有很多,通常包含以下幾大類:
- 用戶輸入了非法數(shù)據(jù)。
- 要打開的文件不存在。
- 網(wǎng)絡(luò)通信時連接中斷,或者JVM內(nèi)存溢出。
這些異常有的是因為用戶錯誤引起,有的是程序錯誤引起的,還有其它一些是因為物理錯誤引起的。-
要理解Java異常處理是如何工作的,你需要掌握以下三種類型的異常:
- 檢查性異常:最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程序員無法預見的。例如要打開一個不存在文件時,一個異常就發(fā)生了,這些異常在編譯時不能被簡單地忽略。
- 運行時異常:?運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異??梢栽诰幾g時被忽略。
- 錯誤:?錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中通常被忽略。例如,當棧溢出時,一個錯誤就發(fā)生了,它們在編譯也檢查不到的。
?
異常指不期而至的各種狀況,如:文件找不到、網(wǎng)絡(luò)連接失敗、除0操作、非法參數(shù)等。異常是一個事件,它發(fā)生在程序運行期間,干擾了正常的指令流程。
Java語言在設(shè)計的當初就考慮到這些問題,提出異常處理的框架的方案,所有的異常都可以用一個異常類來表示,不同類型的異常對應不同的子類異常(目前我們所說的異常包括錯誤概念),定義異常處理的規(guī)范,在JDK1.4版本以后增加了異常鏈機制,從而便于跟蹤異常。
Java異常是一個描述在代碼段中發(fā)生異常的對象,當發(fā)生異常情況時,一個代表該異常的對象被創(chuàng)建并且在導致該異常的方法中被拋出,而該方法可以選擇自己處理異常或者傳遞該異常。
回到頂部異常的體系結(jié)構(gòu)
Java把異常當作對象來處理,并定義一個基類java.lang.Throwable作為所有異常的超類。
在Java API中已經(jīng)定義了許多異常類,這些異常類分為兩大類,錯誤Error和異常Exception。
Java異常層次結(jié)構(gòu)圖如下圖所示:
從圖中可以看出所有異常類型都是內(nèi)置類Throwable的子類,因而Throwable在異常類的層次結(jié)構(gòu)的頂層。
接下來Throwable分成了兩個不同的分支,一個分支是Error,它表示不希望被程序捕獲或者是程序無法處理的錯誤。另一個分支是Exception,它表示用戶程序可能捕捉的異常情況或者說是程序可以處理的異常。其中異常類Exception又分為運行時異常(RuntimeException)和非運行時異常。
Java異常又可以分為不受檢查異常(Unchecked Exception)和檢查異常(Checked Exception)。
?
下面將詳細講述這些異常之間的區(qū)別與聯(lián)系:
- Error:Error類對象由 Java 虛擬機生成并拋出,大多數(shù)錯誤與代碼編寫者所執(zhí)行的操作無關(guān)。例如,Java虛擬機運行錯誤(Virtual MachineError),當JVM不再有繼續(xù)執(zhí)行操作所需的內(nèi)存資源時,將出現(xiàn)?OutOfMemoryError。這些異常發(fā)生時,Java虛擬機(JVM)一般會選擇線程終止;還有發(fā)生在虛擬機試圖執(zhí)行應用時,如類定義錯誤(NoClassDefFoundError)、鏈接錯誤(LinkageError)。這些錯誤是不可查的,因為它們在應用程序的控制和處理能力之 外,而且絕大多數(shù)是程序運行時不允許出現(xiàn)的狀況。對于設(shè)計合理的應用程序來說,即使確實發(fā)生了錯誤,本質(zhì)上也不應該試圖去處理它所引起的異常狀況。在Java中,錯誤通常是使用Error的子類描述。
- Exception:在Exception分支中有一個重要的子類RuntimeException(運行時異常),該類型的異常自動為你所編寫的程序定義ArrayIndexOutOfBoundsException(數(shù)組下標越界)、NullPointerException(空指針異常)、ArithmeticException(算術(shù)異常)、MissingResourceException(丟失資源)、ClassNotFoundException(找不到類)等異常,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯誤引起的,程序應該從邏輯角度盡可能避免這類異常的發(fā)生;而RuntimeException之外的異常我們統(tǒng)稱為非運行時異常,類型上屬于Exception類及其子類,從程序語法角度講是必須進行處理的異常,如果不處理,程序就不能編譯通過。如IOException、SQLException等以及用戶自定義的Exception異常,一般情況下不自定義檢查異常。
?注意
Error和Exception的區(qū)別:Error通常是災難性的致命的錯誤,是程序無法控制和處理的,當出現(xiàn)這些異常時,Java虛擬機(JVM)一般會選擇終止線程;Exception通常情況下是可以被程序處理的,并且在程序中應該盡可能的去處理這些異常。
- 檢查異常:在正確的程序運行過程中,很容易出現(xiàn)的、情理可容的異常狀況,在一定程度上這種異常的發(fā)生是可以預測的,并且一旦發(fā)生該種異常,就必須采取某種方式進行處理。
?提示
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于檢查異常,當程序中可能出現(xiàn)這類異常,要么使用try-catch語句進行捕獲,要么用throws子句拋出,否則編譯無法通過。
- 不受檢查異常:包括RuntimeException及其子類和Error。
?提示
不受檢查異常為編譯器不要求強制處理的異常,檢查異常則是編譯器要求必須處置的異常。
回到頂部Java 異常的處理機制
Java的異常處理本質(zhì)上是拋出異常和捕獲異常。
- 拋出異常:要理解拋出異常,首先要明白什么是異常情形(exception condition),它是指阻止當前方法或作用域繼續(xù)執(zhí)行的問題。其次把異常情形和普通問題相區(qū)分,普通問題是指在當前環(huán)境下能得到足夠的信息,總能處理這個錯誤。對于異常情形,已經(jīng)無法繼續(xù)下去了,因為在當前環(huán)境下無法獲得必要的信息來解決問題,你所能做的就是從當前環(huán)境中跳出,并把問題提交給上一級環(huán)境,這就是拋出異常時所發(fā)生的事情。拋出異常后,會有幾件事隨之發(fā)生。首先,是像創(chuàng)建普通的java對象一樣將使用new在堆上創(chuàng)建一個異常對象;然后,當前的執(zhí)行路徑(已經(jīng)無法繼續(xù)下去了)被終止,并且從當前環(huán)境中彈出對異常對象的引用。此時,異常處理機制接管程序,并開始尋找一個恰當?shù)牡胤嚼^續(xù)執(zhí)行程序,這個恰當?shù)牡胤骄褪钱惓L幚沓绦蚧蛘弋惓L幚砥?#xff0c;它的任務是將程序從錯誤狀態(tài)中恢復,以使程序要么換一種方式運行,要么繼續(xù)運行下去。
舉個簡單的例子,假使我們創(chuàng)建了一個學生對象Student的一個引用stu,在調(diào)用的時候可能還沒有初始化。所以在使用這個對象引用調(diào)用其他方法之前,要先對它進行檢查,可以創(chuàng)建一個代表錯誤信息的對象,并且將它從當前環(huán)境中拋出,這樣就把錯誤信息傳播到更大的環(huán)境中。
if(stu == null){throw new NullPointerException(); }這就拋出了異常,它將在其他的地方得到執(zhí)行或者處理,具體是哪個地方后面將很快介紹,代碼中出現(xiàn)的?throw?是一個關(guān)鍵字,暫時先不做過多講解,后面會詳細講解。
- 捕獲異常:在方法拋出異常之后,運行時系統(tǒng)將轉(zhuǎn)為尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發(fā)生時依次存留在調(diào)用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即為合適的異常處理器。運行時系統(tǒng)從發(fā)生異常的方法開始,依次回查調(diào)用棧中的方法,直至找到含有合適異常處理器的方法并執(zhí)行。當運行時系統(tǒng)遍歷調(diào)用棧而未找到合適的異常處理器,則運行時系統(tǒng)終止。同時,意味著Java程序的終止。
?提示
對于運行時異常、錯誤和檢查異常,Java技術(shù)所要求的異常處理方式有所不同。
由于運行時異常及其子類的不可查性,為了更合理、更容易地實現(xiàn)應用程序,Java規(guī)定,運行時異常將由Java運行時系統(tǒng)自動拋出,允許應用程序忽略運行時異常。
對于方法運行中可能出現(xiàn)的Error,當運行方法不欲捕捉時,Java允許該方法不做任何拋出聲明。因為,大多數(shù)Error異常屬于永遠不能被允許發(fā)生的狀況,也屬于合理的應用程序不該捕捉的異常。
對于所有的檢查異常,Java規(guī)定:一個方法必須捕捉,或者聲明拋出方法之外。也就是說,當一個方法選擇不捕捉檢查異常時,它必須聲明將拋出異常。
Java異常處理涉及到五個關(guān)鍵字,分別是:try、catch、finally、throw、throws。下面將驟一介紹,通過認識這五個關(guān)鍵字,掌握基本異常處理知識。
??try?? ? ? ?-- 用于監(jiān)聽。將要被監(jiān)聽的代碼(可能拋出異常的代碼)放在try語句塊之內(nèi),當try語句塊內(nèi)發(fā)生異常時,異常就被拋出。
??catch?? -- 用于捕獲異常。catch用來捕獲try語句塊中發(fā)生的異常。
??finally??-- finally語句塊總是會被執(zhí)行。它主要用于回收在try塊里打開的物力資源(如數(shù)據(jù)庫連接、網(wǎng)絡(luò)連接和磁盤文件)。只有finally塊,執(zhí)行完成之后,才會回來執(zhí)行try或者catch塊中的return或者throw語句,如果finally中使用了return或者throw等終止方法的語句,則就不會跳回執(zhí)行,直接停止。
??throw?? -- 用于拋出異常。
??throws?-- 用在方法簽名中,用于聲明該方法可能拋出的異常。
異常處理的基本語法
1. try-catch
try{//code that might generate exceptions }catch(Exception e){//the code of handling exception1 }catch(Exception e){//the code of handling exception2 }要明白異常捕獲,還要理解監(jiān)控區(qū)域(guarded region)的概念。它是一段可能產(chǎn)生異常的代碼,并且后面跟著處理這些異常的代碼。
因而可知,上述try-catch所描述的即是監(jiān)控區(qū)域,關(guān)鍵詞try后的一對大括號將一塊可能發(fā)生異常的代碼包起來,即為監(jiān)控區(qū)域。Java方法在運行過程中發(fā)生了異常,則創(chuàng)建異常對象。將異常拋出監(jiān)控區(qū)域之外,由Java運行時系統(tǒng)負責尋找匹配的catch子句來捕獲異常。若有一個catch語句匹配到了,則執(zhí)行該catch塊中的異常處理代碼,就不再嘗試匹配別的catch塊了。
匹配的原則是:如果拋出的異常對象屬于catch子句的異常類,或者屬于該異常類的子類,則認為生成的異常對象與catch塊捕獲的異常類型相匹配。
舉個例子算術(shù)異常:
public class TestException { public static void main(String[] args) { int a = 1; int b = 0; try { // try監(jiān)控區(qū)域 if (b == 0) throw new ArithmeticException(); // 通過throw語句拋出異常 System.out.println("a/b的值是:" + a / b); System.out.println("this will not be printed!");} catch (ArithmeticException e) { // catch捕捉異常 System.out.println("程序出現(xiàn)異常,變量b不能為0!"); } System.out.println("程序正常結(jié)束。"); } }運行結(jié)果:
D:\java>java TestException程序出現(xiàn)異常,變量b不能為0!程序正常結(jié)束。顯示一個異常的描述,Throwable重載了toString()方法(由Object定義),所以它將返回一個包含異常描述的字符串。例如,將前面的catch塊重寫成:
catch (ArithmeticException e) { // catch捕捉異常 System.out.println("程序出現(xiàn)異常"+e); }結(jié)果:
D:\java>java TestException程序出現(xiàn)異常java.lang.ArithmeticException程序正常結(jié)束。根據(jù)前面講述的,算術(shù)異常屬于運行時異常,因而實際上該異常不需要程序拋出,運行時系統(tǒng)自動拋出,將例子改為如下:
public class TestException { public static void main(String[] args) { int a = 1; int b = 0; System.out.println("a/b的值是:" + a / b);System.out.println("this will not be printed!");} }結(jié)果:
D:\java>java TestExceptionException in thread "main" java.lang.ArithmeticException: / by zeroat TestException.main(TestException.java:7)使用多重的catch語句:很多情況下,由單個的代碼段可能引起多個異常。處理這種情況,我們需要定義兩個或者更多的catch子句,每個子句捕獲一種類型的異常,當異常被引發(fā)時,每個catch子句被依次檢查,第一個匹配異常類型的子句執(zhí)行,當一個catch子句執(zhí)行以后,其他的子句將被旁路。
編寫多重catch語句塊注意事項:
順序問題:先小后大,即先子類后父類
?
Java通過異常類描述異常類型。對于有多個catch子句的異常程序而言,應該盡量將捕獲底層異常類的catch子句放在前面,同時盡量將捕獲相對高層的異常類的catch子句放在后面。否則,捕獲底層異常類的catch子句將可能會被屏蔽。
RuntimeException異常類包括運行時各種常見的異常,ArithmeticException類和ArrayIndexOutOfBoundsException類都是它的子類。因此,RuntimeException異常類的catch子句應該放在最后面,否則可能會屏蔽其后的特定異常處理或引起編譯錯誤。
嵌套try語句:try語句可以被嵌套。也就是說,一個try語句可以在另一個try塊的內(nèi)部。每次進入try語句,異常的前后關(guān)系都會被推入堆棧。如果一個內(nèi)部的try語句不含特殊異常的catch處理程序,堆棧將彈出,下一個try語句的catch處理程序?qū)z查是否與之匹配。這個過程將繼續(xù)直到一個catch語句被匹配成功,或者是直到所有的嵌套try語句被檢查完畢。如果沒有catch語句匹配,Java運行時系統(tǒng)將處理這個異常。
例如:
class NestTry{public static void main(String[] args){try{int a = args.length;int b = 42 / a;System.out.println("a = "+ a);try{if(a == 1){a = a/(a-a);}if(a == 2){int c[] = {1};c[42] =99;}}catch(ArrayIndexOutOfBoundsException e){System.out.println("ArrayIndexOutOfBounds :"+e);} }catch(ArithmeticException e){System.out.println("Divide by 0"+ e);}} }正如程序中所顯示的,該程序在一個try塊中嵌套了另一個try塊。程序工作如下:當你在沒有命令行參數(shù)的情況下執(zhí)行該程序,外面的try塊將產(chǎn)生一個被0除的異常。程序在有一個命令行參數(shù)條件下執(zhí)行,由嵌套的try塊產(chǎn)生一個被0除的異常,由于內(nèi)部的catch塊不匹配這個異常,它將把異常傳給外部的try塊,在外部異常被處理。如果你在具有兩個命令行參數(shù)的條件下執(zhí)行該程序,將由內(nèi)部try塊產(chǎn)生一個數(shù)組邊界異常。
結(jié)果:
D:\java>javac estTry.javaD:\java>>java NestTryDivide by 0 java.lang.ArithmeticExceptio: / by zeroD:\java>java NestTry onea = 1Divide by 0java.lang.ArithmeticException: / by zeroD:\java>java NestTry one twoa = 2ArrayIndexOutOfBounds :java.lang.ArrayIndexOutOfBoundsException: 42注意:當有方法調(diào)用時,try語句的嵌套可以很隱蔽的發(fā)生。例如,我們可以將對方法的調(diào)用放在一個try塊中。在該方法的內(nèi)部,有另一個try語句。在這種情況下,方法內(nèi)部的try仍然是嵌套在外部調(diào)用該方法的try塊中的。下面我們將對上述例子進行修改,嵌套的try塊移到方法nesttry()的內(nèi)部:
class NestTry{static void nesttry(int a){try{if(a == 1){a = a/(a-a);}if(a == 2){int c[] = {1};c[42] =99;}}catch(ArrayIndexOutOfBoundsException e){System.out.println("ArrayIndexOutOfBounds :"+e);} }public static void main(String[] args){try{int a = args.length;int b = 42 / a;System.out.println("a = "+ a);nesttry(a);}catch(ArithmeticException e){System.out.println("Divide by 0"+ e);}} }結(jié)果輸出與前面例子一致:
D:\java>javac NestTry.javaD:\java>java NestTryDivide by 0java.lang.ArithmeticException: / by zeroD:\java>java NestTry onea = 1Divide by 0java.lang.ArithmeticException: / by zeroD:\java>java NestTry one twoa = 2ArrayIndexOutOfBounds :java.lang.ArrayIndexOutOfBoundsException: 422. throw
到目前為止,我們只是獲取了被Java運行時系統(tǒng)引發(fā)的異常。然而,我們還可以用throw語句拋出明確的異常。Throw的語法形式如下:
throw ThrowableInstance;這里的ThrowableInstance一定是Throwable類類型或者Throwable子類類型的一個對象。簡單的數(shù)據(jù)類型,例如int,char,以及非Throwable類,例如String或Object,不能用作異常。有兩種方法可以獲取Throwable對象:在catch子句中使用參數(shù)或者使用new操作符創(chuàng)建。
程序執(zhí)行完throw語句之后立即停止;throw后面的任何語句不被執(zhí)行,最鄰近的try塊用來檢查它是否含有一個與異常類型匹配的catch語句。如果發(fā)現(xiàn)了匹配的塊,控制轉(zhuǎn)向該語句;如果沒有發(fā)現(xiàn),次包圍的try塊來檢查,以此類推。如果沒有發(fā)現(xiàn)匹配的catch塊,默認異常處理程序中斷程序的執(zhí)行并且打印堆棧軌跡。
例如:
class TestThrow{static void proc(){try{throw new NullPointerException("demo");}catch(NullPointerException e){System.out.println("Caught inside proc");throw e;}}public static void main(String [] args){try{proc();}catch(NullPointerException e){System.out.println("Recaught: "+e);}} }結(jié)果:
D:\java>java TestThrowCaught inside procRecaught: java.lang.NullPointerException: demo該程序兩次處理相同的錯誤,首先,main()方法設(shè)立了一個異常關(guān)系然后調(diào)用proc()。proc()方法設(shè)立了另一個異常處理關(guān)系并且立即拋出一個NullPointerException實例,NullPointerException在main()中被再次捕獲。
該程序闡述了怎樣創(chuàng)建Java的標準異常對象,特別注意這一行:
| 1 | throw?new?NullPointerException("demo"); |
此處new用來構(gòu)造一個NullPointerException實例,所有的Java內(nèi)置的運行時異常有兩個構(gòu)造方法:一個沒有參數(shù),一個帶有一個字符串參數(shù)。當用第二種形式時,參數(shù)指定描述異常的字符串。如果對象用作print()或者println()的參數(shù)時,該字符串被顯示。這同樣可以通過調(diào)用getMessage()來實現(xiàn),getMessage()是由Throwable定義的。
3. throws
如果一個方法可以導致一個異常但不處理它,它必須指定這種行為以使方法的調(diào)用者可以保護它們自己而不發(fā)生異常。要做到這點,我們可以在方法聲明中包含一個throws子句。一個throws子句列舉了一個方法可能引發(fā)的所有異常類型。這對于除了Error或RuntimeException及它們子類以外類型的所有異常是必要的。一個方法可以引發(fā)的所有其他類型的異常必須在throws子句中聲明,否則會導致編譯錯誤。
下面是throws子句的方法聲明的通用形式:
public void info() throws Exception {//body of method }Exception 是該方法可能引發(fā)的所有的異常,也可以是異常列表,中間以逗號隔開。
例如:
class TestThrows{static void throw1(){System.out.println("Inside throw1 . ");throw new IllegalAccessException("demo");}public static void main(String[] args){throw1();} }上述例子中有兩個地方存在錯誤,你能看出來嗎?
該例子中存在兩個錯誤,首先,throw1()方法不想處理所導致的異常,因而它必須聲明throws子句來列舉可能引發(fā)的異常即IllegalAccessException;其次,main()方法必須定義try/catch語句來捕獲該異常。
正確例子如下:
class TestThrows{static void throw1() throws IllegalAccessException {System.out.println("Inside throw1 . ");throw new IllegalAccessException("demo");}public static void main(String[] args){try {throw1();}catch(IllegalAccessException e ){System.out.println("Caught " + e);}} }Throws拋出異常的規(guī)則:
- 如果是不受檢查異常(unchecked exception),即Error、RuntimeException或它們的子類,那么可以不使用throws關(guān)鍵字來聲明要拋出的異常,編譯仍能順利通過,但在運行時會被系統(tǒng)拋出。
- 必須聲明方法可拋出的任何檢查異常(checked exception)。即如果一個方法可能出現(xiàn)受可查異常,要么用try-catch語句捕獲,要么用throws子句聲明將它拋出,否則會導致編譯錯誤
- 僅當拋出了異常,該方法的調(diào)用者才必須處理或者重新拋出該異常。當方法的調(diào)用者無力處理該異常的時候,應該繼續(xù)拋出,而不是囫圇吞棗。
- 調(diào)用方法必須遵循任何可查異常的處理和聲明規(guī)則。若覆蓋一個方法,則不能聲明與覆蓋方法不同的異常。聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。
4. finally
當異常發(fā)生時,通常方法的執(zhí)行將做一個陡峭的非線性的轉(zhuǎn)向,它甚至會過早的導致方法返回。例如,如果一個方法打開了一個文件并關(guān)閉,然后退出,你不希望關(guān)閉文件的代碼被異常處理機制旁路。finally關(guān)鍵字為處理這種意外而設(shè)計。
finally創(chuàng)建的代碼塊在try/catch塊完成之后另一個try/catch出現(xiàn)之前執(zhí)行。finally塊無論有沒有異常拋出都會執(zhí)行。如果拋出異常,即使沒有catch子句匹配,finally也會執(zhí)行。一個方法將從一個try/catch塊返回到調(diào)用程序的任何時候,經(jīng)過一個未捕獲的異?;蛘呤且粋€明確的返回語句,finally子句在方法返回之前仍將執(zhí)行。這在關(guān)閉文件句柄和釋放任何在方法開始時被分配的其他資源是很有用。
finally子句是可選項,可以有也可以無,但是每個try語句至少需要一個catch或者finally子句。
class TestFinally{static void proc1(){try{System.out.println("inside proc1");throw new RuntimeException("demo");}finally{System.out.println("proc1's finally");}}static void proc2(){try{System.out.println("inside proc2");return ;} finally{System.out.println("proc2's finally");}} static void proc3(){try{System.out.println("inside proc3");}finally{System.out.println("proc3's finally");}}public static void main(String [] args){try{proc1();}catch(Exception e){System.out.println("Exception caught");}proc2();proc3();} }該例子中,proc1()拋出了異常中斷了try,它的finally子句在退出時執(zhí)行。proc2的try語句通過return語句返回,但在返回之前finally語句執(zhí)行。在proc3()中try語句正常執(zhí)行,沒有錯誤,finally語句也被執(zhí)行。
輸出結(jié)果:
D:\java>java TestFinallyinside proc1proc1's finally Exception caughtinside proc2proc2's finally inside proc3proc3's finally注:如果finally塊與一個try聯(lián)合使用,finally塊將在try結(jié)束之前執(zhí)行。
問題擴展(面試題):
1、try{} 里有一個 return 語句,那么緊跟在這個 try 后的 finally{} 里的 code 會不會被執(zhí)行,什么時候被執(zhí)行,在 return 前還是后?
答案:會執(zhí)行,在方法返回調(diào)用者前執(zhí)行。
注意:在finally中改變返回值的做法是不好的,因為如果存在finally代碼塊,try中的return語句不會立馬返回調(diào)用者,而是記錄下返回值待finally代碼塊執(zhí)行完畢之后再向調(diào)用者返回其值,然后如果在finally中修改了返回值,就會返回修改后的值。顯然,在finally中返回或者修改返回值會對程序造成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員干這種齷齪的事情,Java中也可以通過提升編譯器的語法檢查級別來產(chǎn)生警告或錯誤,Eclipse中可以在如圖所示的地方進行設(shè)置,強烈建議將此項設(shè)置為編譯錯誤。
?
2、Java語言如何進行異常處理,關(guān)鍵字:throws、throw、try、catch、finally分別如何使用?
答:Java通過面向?qū)ο蟮姆椒ㄟM行異常處理,把各種不同的異常進行分類,并提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable類或其子類的實例。當一個方法出現(xiàn)異常后便拋出一個異常對象,該對象中包含有異常信息,調(diào)用這個對象的方法可以捕獲到這個異常并可以對其進行處理。Java的異常處理是通過5個關(guān)鍵詞來實現(xiàn)的:try、catch、throw、throws和finally。一般情況下是用try來執(zhí)行一段程序,如果系統(tǒng)會拋出(throw)一個異常對象,可以通過它的類型來捕獲(catch)它,或通過總是執(zhí)行代碼塊(finally)來處理;try用來指定一塊預防所有異常的程序;catch子句緊跟在try塊后面,用來指定你想要捕獲的異常的類型;throw語句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各種異常(當然聲明異常時允許無病呻吟);finally為確保一段代碼不管發(fā)生什么異常狀況都要被執(zhí)行;try語句可以嵌套,每當遇到一個try語句,異常的結(jié)構(gòu)就會被放入異常棧中,直到所有的try語句都完成。如果下一級的try語句沒有對某種異常進行處理,異常棧就會執(zhí)行出棧操作,直到遇到有處理這種異常的try語句或者最終將異常拋給JVM。
3、運行時異常與受檢異常有何異同??
答:異常表示程序運行過程中可能出現(xiàn)的非正常狀態(tài),運行時異常表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設(shè)計得沒有問題通常就不會發(fā)生。受檢異常跟程序運行的上下文環(huán)境有關(guān),即使程序設(shè)計無誤,仍然可能因使用的問題而引發(fā)。Java編譯器要求方法必須聲明拋出可能發(fā)生的受檢異常,但是并不要求必須聲明拋出未被捕獲的運行時異常。異常和繼承一樣,是面向?qū)ο蟪绦蛟O(shè)計中經(jīng)常被濫用的東西,在Effective Java中對異常的使用給出了以下指導原則:?
- 不要將異常處理用于正常的控制流(設(shè)計良好的API不應該強迫它的調(diào)用者為了正常的控制流而使用異常)?
- 對可以恢復的情況使用受檢異常,對編程錯誤使用運行時異常?
- 避免不必要的使用受檢異常(可以通過一些狀態(tài)檢測手段來避免異常的發(fā)生)?
- 優(yōu)先使用標準的異常?
- 每個方法拋出的異常都要有文檔?
- 保持異常的原子性?
- 不要在catch中忽略掉捕獲到的異常
4、列出一些你常見的運行時異常??
答:?
- ArithmeticException(算術(shù)異常)?
- ClassCastException (類轉(zhuǎn)換異常)?
- IllegalArgumentException (非法參數(shù)異常)?
- IndexOutOfBoundsException (下標越界異常)?
- NullPointerException (空指針異常)?
- SecurityException (安全異常)
?
回到頂部異常鏈
異常鏈顧名思義就是將異常發(fā)生的原因一個傳一個串起來,即把底層的異常信息傳給上層,這樣逐層拋出。 Java API文檔中給出了一個簡單的模型:
try { lowLevelOp(); } catch (LowLevelException le) { throw (HighLevelException) new HighLevelException().initCause(le); }當程序捕獲到了一個底層異常,在處理部分選擇了繼續(xù)拋出一個更高級別的新異常給此方法的調(diào)用者。 這樣異常的原因就會逐層傳遞。這樣,位于高層的異常遞歸調(diào)用getCause()方法,就可以遍歷各層的異常原因。 這就是Java異常鏈的原理。異常鏈的實際應用很少,發(fā)生異常時候逐層上拋不是個好注意, 上層拿到這些異常又能奈之何?而且異常逐層上拋會消耗大量資源, 因為要保存一個完整的異常鏈信息.
回到頂部自定義異常
使用Java內(nèi)置的異常類可以描述在編程時出現(xiàn)的大部分異常情況。除此之外,用戶還可以自定義異常。用戶自定義異常類,只需繼承Exception類即可。
在程序中使用自定義異常類,大體可分為以下幾個步驟:
- 創(chuàng)建自定義異常類。
- 在方法中通過throw關(guān)鍵字拋出異常對象。
- 如果在當前拋出異常的方法中處理異常,可以使用try-catch語句捕獲并處理;否則在方法的聲明處通過throws關(guān)鍵字指明要拋出給方法調(diào)用者的異常,繼續(xù)進行下一步操作。
- 在出現(xiàn)異常方法的調(diào)用者中捕獲并處理異常。
舉例自定義異常:
class MyException extends Exception {private int detail;MyException(int a){detail = a;}public String toString(){return "MyException ["+ detail + "]";} } public class TestMyException{static void compute(int a) throws MyException{System.out.println("Called compute(" + a + ")");if(a > 10){throw new MyException(a);}System.out.println("Normal exit!");}public static void main(String [] args){try{compute(1);compute(20);}catch(MyException me){System.out.println("Caught " + me);}} }該例子完全按照上述步驟。
運行結(jié)果如下: D:\java>java TestMyExceptionCalled compute(1)Normal exit!Called compute(20)Caught MyException [20] 回到頂部總結(jié)
?
參考文檔:
Java入門之異常處理
from:?http://www.cnblogs.com/Qian123/p/5715402.html
總結(jié)
以上是生活随笔為你收集整理的Java提高篇——Java 异常处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java提高篇——单例模式
- 下一篇: 通俗大白话来理解TCP协议的三次握手和四