Java自定義異常與異常使用的最佳方式
異常的分類
1. 非運(yùn)行時(shí)異常(Checked Exception)
Java中凡是繼承自Exception但不是繼承自RuntimeException的類都是非運(yùn)行時(shí)異常。
2. 運(yùn)行時(shí)異常(Runtime Exception/Unchecked Exception)
RuntimeException類直接繼承自Exception類,稱為運(yùn)行時(shí)異常。Java中所有的運(yùn)行時(shí)異常都直接或間接的繼承自RuntimeException。
Java中所有的異常類都直接或間接的繼承自Exception。
異常的處理
一、對(duì)應(yīng)非運(yùn)行時(shí)異常,必須對(duì)其進(jìn)行處理
處理方式有兩種:
- 使用try…catch…finally語(yǔ)句塊進(jìn)行捕獲
- 在產(chǎn)生異常的方法所在的方法聲明throws Exception
二、對(duì)于運(yùn)行時(shí)異常,可以不對(duì)其進(jìn)行處理
也可以對(duì)其進(jìn)行處理。一般情況下都不對(duì)其進(jìn)行處理。
在使用Java API的方法時(shí)會(huì)產(chǎn)生異常,由于實(shí)際的需要,我們需要?jiǎng)?chuàng)建和使用自定義異常。使用全新的異常類,應(yīng)用到系統(tǒng)程序中。
在介紹自定義異常時(shí),首要先談?wù)勈裁匆褂米远x異常,使用自定義異常的好處。創(chuàng)建自定義異常是為了表示應(yīng)用程序的一些錯(cuò)誤類型,為代碼可能發(fā)生的一個(gè)或多個(gè)問(wèn)題提供新的含義;可以顯示代碼多個(gè)位置之間的錯(cuò)誤的相似處,也可區(qū)分代碼運(yùn)行時(shí)可能出現(xiàn)的相似問(wèn)題的一個(gè)或多個(gè)錯(cuò)誤,或給出應(yīng)用程序中一組錯(cuò)誤的特殊含義。
應(yīng)用場(chǎng)景
服務(wù)器的基本作用是處理與客戶機(jī)的通信,若使用標(biāo)準(zhǔn)Java API(如java.io和java.net包中的類)來(lái)編寫(xiě)服務(wù)器,則可使編寫(xiě)的代碼在多個(gè)位置拋出IOException。在設(shè)置服務(wù)器、等待客戶機(jī)連接和獲取通訊流時(shí),可拋出IOException,在通信期間及試圖斷開(kāi)連接時(shí),也會(huì)拋出IOException。簡(jiǎn)而言之,服務(wù)器的各個(gè)部分都是引發(fā)IOException。
對(duì)于服務(wù)器而言,這樣IOException意義不盡相同。雖然由同一異常類型表示,但與各個(gè)異常先關(guān)的業(yè)務(wù)含義存在差異,報(bào)告和恢復(fù)操作也不相同。所以,可以將一個(gè)異常集與服務(wù)器配置和啟動(dòng)問(wèn)題關(guān)聯(lián),將另一個(gè)異常集與客戶機(jī)通訊的實(shí)際行動(dòng)關(guān)聯(lián),將第三個(gè)異常集與服務(wù)器關(guān)閉任務(wù)關(guān)聯(lián)。使用自定義異常,可采用對(duì)應(yīng)用程序有意義的方式來(lái)靈活地表示錯(cuò)誤。
為此,我們需要使用自定義異常來(lái)定為問(wèn)題,定位問(wèn)題與異常實(shí)際準(zhǔn)確的位置。
自定義異常類過(guò)程
1. 多數(shù)情況下
只需要繼承異常類Exception, 經(jīng)常需要定義一個(gè)或多個(gè)構(gòu)造函數(shù),以在對(duì)象中存儲(chǔ)錯(cuò)誤消息。
擴(kuò)展
類java.lang.Throwable是所有異常類的基類,它包括兩個(gè)子類:Exception和Error,Exception類用于描述程序能夠捕獲的異常,如ClassNotFoundException。Error類用于指示合理的應(yīng)用程序不應(yīng)該試圖捕獲的嚴(yán)重問(wèn)題,如虛擬機(jī)錯(cuò)誤VirtualMachineError
自定義異常類可以繼承Throwable類或者Exception類,而不要繼承Error類。自定義異常類之間也可以有繼承關(guān)系
需要為自定義異常類設(shè)計(jì)構(gòu)造方法,以方便構(gòu)造自定義異常對(duì)象。
在繼承任何異常時(shí),將自動(dòng)繼承Throwable類的一些標(biāo)準(zhǔn)特性,如:
- 錯(cuò)誤消息
- 棧跟蹤
- 異常包裝
若要在異常中添加附加信息,則可以為類添加一些變量和方法。本例演示的自定義異常沒(méi)有按照業(yè)務(wù)類型來(lái)命名,而是創(chuàng)建一個(gè)通用異常類,以retCd來(lái)區(qū)別發(fā)生異常的業(yè)務(wù)類型與發(fā)生位置,當(dāng)然對(duì)于具體的retCd值,事先必須有具體的規(guī)定或說(shuō)明。
/** * 多數(shù)情況下,創(chuàng)建自定義異常需要繼承Exception,本例繼承Exception的子類RuntimeException * @author Mahc * */ public class CustomerException extends RuntimeException { private String retCd ; //異常對(duì)應(yīng)的返回碼 private String msgDes; //異常對(duì)應(yīng)的描述信息 public CustomerException() { super(); } public CustomerException(String message) { super(message); msgDes = message; } public CustomerException(String retCd, String msgDes) { super(); this.retCd = retCd; this.msgDes = msgDes; } public String getRetCd() { return retCd; } public String getMsgDes() { return msgDes; } }
2. 聲明方法拋出自定義異常
為了使用自定義異常,必須通知調(diào)用代碼的類:要準(zhǔn)備處理這個(gè)異常類型。為此,聲明一個(gè)或多個(gè)方法拋出異常。找到異常發(fā)生點(diǎn),新建異常并加上關(guān)鍵字throw。
public class TestClass { public void testException() throws CustomerException { try { <p> //..some code that throws <span style="font-family:SimSun;">CustomerException</span></p> } catch (Exception e) { throw new CustomerException("14000001", "String[] strs's length < 4"); } } }
3.自定義異常測(cè)試操作
public class TestCustomerException { public static void main(String[] args) { try { TestClass testClass = new TestClass(); testClass.testException(); } catch (CustomerException e) { e.printStackTrace(); System.out.println("MsgDes\t"+e.getMsgDes()); System.out.println("RetCd\t"+e.getRetCd()); } } }
以下的自定義異常的最佳實(shí)踐,摘自網(wǎng)絡(luò),經(jīng)過(guò)參考擴(kuò)展使用。
使用異常的最佳實(shí)踐
下面的部分我們列出了客戶端代碼處理 API 拋出異常的一些最佳實(shí)現(xiàn)方法。
1. 記得釋放資源
如果你正在用數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)連接的資源,要記得釋放它們。如果你使用的 API 僅僅使用 unchecked exception,你應(yīng)該用完后釋放它們,使用 try-final。
public void dataAccessCode (){ Connection conn = null; try{ conn = getConnection (); ..some code that throws SQLException }catch(SQLException ex){ ex.printStacktrace (); } finally{ DBUtil.closeConnection (conn); } } class DBUtil{ public static void closeConnection (Connection conn){ try{ conn.close (); } catch(SQLException ex){ logger.error ("Cannot close connection"); throw new RuntimeException (ex); } } }
DBUtil 是一個(gè)關(guān)閉連接的工具類。最重要的部分在于 finally,無(wú)論異常發(fā)不發(fā)生都會(huì)執(zhí)行。在這個(gè)例子中,finally 關(guān)閉了連接,如果關(guān)閉過(guò)程中有問(wèn)題發(fā)生的話,會(huì)拋出一個(gè) RuntimeException。
2. 不要使用異常作控制流程之用
生成?;厮菔欠浅0嘿F的,棧回溯的價(jià)值是在于調(diào)試。在流程控制中,?;厮菔菓?yīng)該避免的,因?yàn)榭蛻舳藘H僅想知道如何繼續(xù)。
下面的代碼,一個(gè)自定義的異常 MaximumCountReachedException,用來(lái)控制流程。
public void useExceptionsForFlowControl () { try { while (true) { increaseCount (); } } catch (MaximumCountReachedException ex) { } //Continue execution } public void increaseCount () throws MaximumCountReachedException { if (count >= 5000) throw new MaximumCountReachedException (); }
useExceptionsForFlowControl()使用了一個(gè)無(wú)限的循環(huán)來(lái)遞增計(jì)數(shù)器,直至異常被拋出。這樣寫(xiě)不僅降低了代碼的可讀性,也讓代碼變得很慢。記住異常僅用在有異常發(fā)生的情況。
3. 不要忽略異常
當(dāng)一個(gè) API 方法拋出 checked exception 時(shí),它是要試圖告訴你你需要采取某些行動(dòng)處理它。如果它對(duì)你來(lái)說(shuō)沒(méi)什么意義,不要猶豫,直接轉(zhuǎn)換成 unchecked exception 拋出,千萬(wàn)不要僅僅用空的{}catch 它,然后當(dāng)沒(méi)事發(fā)生一樣忽略它。
4. 不要 catch 最高層次的 exception
Unchecked exception 是繼承自 RuntimeException 類的,而 RuntimeException 繼承自 Exception。如果 catch Exception 的話,你也會(huì) catch RuntimeException。
try{ .. }catch(Exception ex){ }
上面的代碼會(huì)忽略掉 unchecked exception。
5. 僅記錄 exception 一次
對(duì)同一個(gè)錯(cuò)誤的棧回溯(stack trace)記錄多次的話,會(huì)讓程序員搞不清楚錯(cuò)誤的原始來(lái)源。所以僅僅記錄一次就夠了。
總結(jié):
這里是我總結(jié)出的一些異常處理最佳實(shí)施方法。我并不想引起關(guān)于 checked exception 和 unchecked exception 的激烈爭(zhēng)論。你可以根據(jù)你的需要來(lái)設(shè)計(jì)代碼。我相信,隨著時(shí)間的推移,我們會(huì)找到些更好的異常處理的方法的。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家.
相關(guān)文章
解析Tomcat 6、7在EL表達(dá)式解析時(shí)存在的一個(gè)Bug
這篇文章主要是對(duì)Tomcat 6、7在EL表達(dá)式解析時(shí)存在的一個(gè)Bug進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(44)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07SpringBoot+Vue+Element-ui實(shí)現(xiàn)前后端分離
使用前后端分離的方式,可以減少代碼耦合,本文主要介紹了SpringBoot+Vue+Element-ui實(shí)現(xiàn)前后端分離,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06詳解在Java程序中運(yùn)用Redis緩存對(duì)象的方法
這篇文章主要介紹了在Java程序中運(yùn)用Redis緩存對(duì)象的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化
這篇文章主要介紹了rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03