這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)java中如何實(shí)現(xiàn)異常處理,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)專注于洮北網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供洮北營銷型網(wǎng)站建設(shè),洮北網(wǎng)站制作、洮北網(wǎng)頁設(shè)計(jì)、洮北網(wǎng)站官網(wǎng)定制、微信平臺(tái)小程序開發(fā)服務(wù),打造洮北網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供洮北網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
先上個(gè)圖,看一下常見的幾個(gè)異常類型。
所有的異常都來自于Throwable。Throwable有兩個(gè)子類,Error和Exception。
Error通常表示的是嚴(yán)重錯(cuò)誤,這些錯(cuò)誤是不建議被catch的。
注意這里有一個(gè)例外,比如ThreadDeath也是繼承自Error,但是它表示的是線程的死亡,雖然不是嚴(yán)重的異常,但是因?yàn)閼?yīng)用程序通常不會(huì)對(duì)這種異常進(jìn)行catch,所以也歸類到Error中。
Exception表示的是應(yīng)用程序希望catch住的異常。
在Exception中有一個(gè)很特別的異常叫做RuntimeException。RuntimeException叫做運(yùn)行時(shí)異常,是不需要被顯示catch住的,所以也叫做unchecked Exception。而其他非RuntimeException的Exception則需要顯示try catch,所以也叫做checked Exception。
我們知道checked exceptions是一定要被捕獲的異常,我們?cè)诓东@異常之后通常有兩種處理方式。
第一種就是按照業(yè)務(wù)邏輯處理異常,第二種就是本身并不處理異常,但是將異常再次拋出,由上層代碼來處理。
如果捕獲了,但是不處理,那么就是忽略checked exceptions。
接下來我們來考慮一下java中線程的中斷異常。
java中有三個(gè)非常相似的方法interrupt,interrupted和isInterrupted。
isInterrupted()只會(huì)判斷是否被中斷,而不會(huì)清除中斷狀態(tài)。
interrupted()是一個(gè)類方法,調(diào)用isInterrupted(true)判斷的是當(dāng)前線程是否被中斷。并且會(huì)清除中斷狀態(tài)。
前面兩個(gè)是判斷是否中斷的方法,而interrupt()就是真正觸發(fā)中斷的方法。
它的工作要點(diǎn)有下面4點(diǎn):
如果當(dāng)前線程實(shí)例在調(diào)用Object類的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在該實(shí)例中調(diào)用了Thread.sleep(long)或Thread.sleep(long,int)方法,并且正在阻塞狀態(tài)中時(shí),則其中斷狀態(tài)將被清除,并將收到InterruptedException。
如果此線程在InterruptibleChannel上的I / O操作中處于被阻塞狀態(tài),則該channel將被關(guān)閉,該線程的中斷狀態(tài)將被設(shè)置為true,并且該線程將收到j(luò)ava.nio.channels.ClosedByInterruptException異常。
如果此線程在java.nio.channels.Selector中處于被被阻塞狀態(tài),則將設(shè)置該線程的中斷狀態(tài)為true,并且它將立即從select操作中返回。
如果上面的情況都不成立,則設(shè)置中斷狀態(tài)為true。
看下面的例子:
public void wrongInterrupted(){try{ Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }
上面代碼中我們捕獲了一個(gè)InterruptedException,但是我們僅僅是打印出了異常信息,并沒有做任何操作。這樣程序的表現(xiàn)和沒有發(fā)送一異常一樣,很明顯是有問題的。
根據(jù)上面的介紹,我們知道,interrupted()方法會(huì)清除中斷狀態(tài),所以,如果我們自身處理不了異常的情況下,需要重新調(diào)用Thread.currentThread().interrupt()重新拋出中斷,由上層代碼負(fù)責(zé)處理,如下所示。
public void correctInterrupted(){try{ Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
遇到異常的時(shí)候,通常我們需要進(jìn)行一定程度的日志輸出,從而來定位異常。但是我們?cè)谧鋈罩据敵龅臅r(shí)候,一定要注意不要暴露敏感信息。
下表可以看到異常信息可能會(huì)暴露的敏感信息:
除了敏感信息之外,我們還要做好日志信息的安全保護(hù)。
如果我們?cè)谔幚懋惓5臅r(shí)候,修改了對(duì)象中某些字段的狀態(tài),在捕獲異常的時(shí)候需要怎么處理呢?
private int age=30;public void wrongRestore(){try{ age=20;throw new IllegalStateException("custom exception!"); }catch (IllegalStateException e){ System.out.println("we do nothing"); } }
上面的例子中,我們將age重置為20,然后拋出了異常。雖然拋出了異常,但是我們并沒有重置age,最后導(dǎo)致age最終被修改了。
整個(gè)restore的邏輯沒有處理完畢,但是我們部分修改了對(duì)象的數(shù)據(jù),這是很危險(xiǎn)的。
實(shí)際上,我們需要一個(gè)重置:
public void rightRestore(){try{ age=20;throw new IllegalStateException("custom exception!"); }catch (IllegalStateException e){ System.out.println("we do nothing"); age=30; } }
我們?cè)谑褂胻ry-finally和try-catch-finally語句時(shí),一定不要在finally block中使用return, break, continue或者throw語句。
為什么呢?
根據(jù)Java Language Specification(JLS)的說明,finally block一定會(huì)被執(zhí)行,不管try語句中是否拋出異常。
在try-finally和try-catch-finally語句中,如果try語句中拋出了異常R,然后finally block被執(zhí)行,這時(shí)候有兩種情況:
如果finally block正常執(zhí)行,那么try語句被終止的原因是異常R。
如果在finally block中拋出了異常S,那么try語句被終止的原因?qū)?huì)變成S。
我們舉個(gè)例子:
public class FinallyUsage {public boolean wrongFinally(){try{throw new IllegalStateException("my exception!"); }finally { System.out.println("Code comes to here!");return true; } }public boolean rightFinally(){try{throw new IllegalStateException("my exception!"); }finally { System.out.println("Code comes to here!"); } }public static void main(String[] args) { FinallyUsage finallyUsage=new FinallyUsage(); finallyUsage.wrongFinally(); finallyUsage.rightFinally(); } }
上面的例子中,我們定義了兩個(gè)方法,一個(gè)方法中我們?cè)趂inally中直接return,另一方法中,我們讓finally正常執(zhí)行完畢。
最終,我們可以看到wrongFinally將異常隱藏了,而rightFinally保留了try的異常。
同樣的,如果我們?cè)趂inally block中拋出了異常,我們一定要記得對(duì)其進(jìn)行捕獲,否則將會(huì)隱藏try block中的異常信息。
通常來說NullPointerException表示程序代碼有邏輯錯(cuò)誤,是需要程序員來進(jìn)行代碼邏輯修改,從而進(jìn)行修復(fù)的。
比如說加上一個(gè)null check。
不捕獲NullPointerException的原因有三個(gè)。
使用null check的開銷要遠(yuǎn)遠(yuǎn)小于異常捕獲的開銷。
如果在try block中有多個(gè)可能拋出NullPointerException的語句,我們很難定位到具體的錯(cuò)誤語句。
最后,如果發(fā)生了NullPointerException,程序基本上不可能正常運(yùn)行或者恢復(fù),所以我們需要提前進(jìn)行null check的判斷。
同樣的,程序也不要對(duì)NullPointerException的父類RuntimeException, Exception, or Throwable進(jìn)行捕捉。
我們拋出異常主要是為了能夠找到準(zhǔn)確的處理異常的方法,如果直接拋出RuntimeException, Exception, 或者 Throwable就會(huì)導(dǎo)致程序無法準(zhǔn)確處理特定的異常。
通常來說我們需要自定義RuntimeException, Exception, 或者 Throwable的子類,通過具體的子類來區(qū)分具體的異常類型。
一般來說checked Exception是需要顯示catch住,或者在調(diào)用方法上使用throws做申明的。
但是我們可以通過某些手段來繞過這種限制,從而在使用checked Exception的時(shí)候不需要遵守上述規(guī)則。
當(dāng)然這樣做是需要避免的。我們看一個(gè)例子:
private static Throwable throwable;private ThrowException() throws Throwable {throw throwable; }public static synchronized void undeclaredThrow(Throwable throwable) { ThrowException.throwable = throwable;try { ThrowException.class.newInstance(); } catch (InstantiationException e) { } catch (IllegalAccessException e) { } finally { ThrowException.throwable = null; } }
上面的例子中,我們定義了一個(gè)ThrowException的private構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)會(huì)throw一個(gè)throwable,這個(gè)throwable是從方法傳入的。
在undeclaredThrow方法中,我們調(diào)用了ThrowException.class.newInstance()實(shí)例化一個(gè)ThrowException實(shí)例,因?yàn)樾枰{(diào)用構(gòu)造函數(shù),所以會(huì)拋出傳入的throwable。
因?yàn)镋xception是throwable的子類,如果我們?cè)谡{(diào)用的時(shí)候傳入一個(gè)checked Exception,很明顯,我們的代碼并沒有對(duì)其進(jìn)行捕獲:
public static void main(String[] args) { ThrowException.undeclaredThrow(new Exception("Any checked exception")); }
怎么解決這個(gè)問題呢?換個(gè)思路,我們可以使用Constructor.newInstance()來替代class.newInstance()。
try { Constructor constructor = ThrowException.class.getConstructor(new Class>[0]); constructor.newInstance(); } catch (InstantiationException e) { } catch (InvocationTargetException e) { System.out.println("catch exception!"); } catch (NoSuchMethodException e) { } catch (IllegalAccessException e) { } finally { ThrowException.throwable = null; }
上述就是小編為大家分享的java中如何實(shí)現(xiàn)異常處理了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。