溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java常見(jiàn)異常處理

發(fā)布時(shí)間:2020-07-15 14:48:16 來(lái)源:網(wǎng)絡(luò) 閱讀:403 作者:專注地一哥 欄目:編程語(yǔ)言

異常是Java程序中經(jīng)常遇到的問(wèn)題,一個(gè)異常就是一個(gè)Bug,就要花很多時(shí)間來(lái)定位異常。
Java異常
(1)Throwable是Java異常的頂級(jí)類,所有的異常都繼承于這個(gè)類。
(2)Error,Exception是異常類的兩個(gè)大分類。
(3)Error是非程序異常,即程序不能捕獲的異常,一般是編譯或者系統(tǒng)性的錯(cuò)誤,如OutOfMemorry內(nèi)存溢出異常等。
(4)Exception是程序異常類,由程序內(nèi)部產(chǎn)生。Exception又分為運(yùn)行時(shí)異常、非運(yùn)行時(shí)異常。
(5)運(yùn)行時(shí)異常的特點(diǎn)是Java編譯器不會(huì)檢查它,也就是說(shuō),當(dāng)程序中可能出現(xiàn)這類異常,即使沒(méi)有用try-catch語(yǔ)句捕獲它,也沒(méi)有用throws子句聲明拋出它,也會(huì)編譯通過(guò),運(yùn)行時(shí)異??商幚砘蛘卟惶幚?。運(yùn)行時(shí)異常一般常出來(lái)定義系統(tǒng)的自定義異常,業(yè)務(wù)根據(jù)自定義異常做出不同的處理。常見(jiàn)的運(yùn)行時(shí)異常如NullPointException、ArrayIndexOutOfBoundsException等。
(6)非運(yùn)行時(shí)異常是程序必須進(jìn)行處理的異常,捕獲或者拋出,如果不處理程序就不能編譯通過(guò)。如常見(jiàn)的IOException、ClassNotFoundException等。
常見(jiàn)的Java異常坑
最頑固的坑--NullPointerException
空指針異常應(yīng)該是每一個(gè)程序員必須要踩得的坑,而且應(yīng)該是經(jīng)常踩不斷踩的坑,極其普遍且難以根除。但是這個(gè)坑可以通過(guò)某些方法有效的避免。
(1)什么是空指針?
當(dāng)一個(gè)變量的值為 null 時(shí),在 Java 里面表示一個(gè)不存在的空對(duì)象,沒(méi)有實(shí)際內(nèi)容,沒(méi)有給它分配內(nèi)存,null 也是對(duì)象成員變量的默認(rèn)值。
所以,一個(gè)對(duì)象如果沒(méi)有進(jìn)行初始化操作,這時(shí)候,如果你調(diào)用這個(gè)對(duì)象的方法或者變量,就會(huì)出現(xiàn)空指針異常。
如下面示例會(huì)發(fā)生空指針異常:
Object?object?=?null;
String?string?=?object.toString();
空指針?biāo)菍儆谶\(yùn)行時(shí)異常?RuntimeException?的子類,它不是捕獲型的,只有在程序運(yùn)行時(shí)才可能報(bào)出來(lái),而且會(huì)造成程序中斷。
如何有效避免空指針?
以下是我在編碼過(guò)程中遇到的問(wèn)題及解決辦法。
字符串比較,常量放前面---總是從已知的非空String對(duì)象中調(diào)用equals()方法
if(status.equals(SUCCESS)){
}
這個(gè)時(shí)候 status 可能為 null 造成空指針異常,應(yīng)該把常量放前面,就能避免空指針異常。這個(gè)應(yīng)該在各種開(kāi)發(fā)規(guī)范里面都會(huì)提到,也是最基礎(chǔ)的。
if(SUCCESS.equals(status)){
}
當(dāng)valueOf()和toString()返回相同的結(jié)果時(shí),寧愿使用前者。
因?yàn)檎{(diào)用null對(duì)象的toString()會(huì)拋出空指針異常,如果我們能夠使用valueOf()獲得相同的值,那寧愿使用valueOf(),傳遞一個(gè)null給valueOf()將會(huì)返回“null”,尤其是在那些包裝類,像Integer、Float、Double和BigDecimal。
BigDecimal bd = getPrice();
System.out.println(String.valueOf(bd));?//不會(huì)拋出空指針異常
System.out.println(bd.toString());?//拋出 NullPointerException
初始化默認(rèn)值
在對(duì)象初始化的時(shí)候給它一個(gè)默認(rèn)值或者默認(rèn)構(gòu)造實(shí)現(xiàn),如:
User?user?=?new?User();
String?name?=?StringUtils.EMPTY;
避免返回空集合
在返回一個(gè)集合的話,默認(rèn)會(huì)是 null,統(tǒng)一規(guī)范返回一個(gè)空集合。
舉個(gè) List 例子,如:
public?List?getUserList(){
????List?list?=?userMapper.gerUserList();
????return?list?==?null???new?ArrayList()?:?list;
}
這樣接收方就不用擔(dān)心空指針異常了,也不會(huì)影響業(yè)務(wù)。
采用JDK8 Optional?新特性
?Optional是Jdk8提供的一個(gè)可以包含null值的容器對(duì)象,可以用來(lái)代替xx != null的判斷。(這個(gè)暫時(shí)因?yàn)槲覀冘?chē)道系統(tǒng)統(tǒng)一適用的是Java7,這個(gè)方案還沒(méi)有在車(chē)到系統(tǒng)代碼里使用過(guò)。)
OutOfMemoryError
內(nèi)存異常異常也是一個(gè)經(jīng)常出現(xiàn)的Bug,但是這個(gè)不是程序能控制的,這個(gè)問(wèn)題是指要分配的對(duì)象的內(nèi)存超出了當(dāng)前最大的堆內(nèi)存,需要調(diào)整堆內(nèi)存大小(-Xmx)以及優(yōu)化程序。
(1)常見(jiàn)的有以下幾種原因:
1.內(nèi)存中加載的數(shù)據(jù)量過(guò)于龐大,如一次從數(shù)據(jù)庫(kù)取出過(guò)多數(shù)據(jù);
2.集合類中有對(duì)對(duì)象的引用使用完后未清空,使得JVM不能回收;
3.代碼中存在死循環(huán)或循環(huán)產(chǎn)生過(guò)多重復(fù)的對(duì)象實(shí)體;
4.使用的第三方軟件中的BUG;
5.啟動(dòng)參數(shù)內(nèi)存值設(shè)定的過(guò)小
(2)常見(jiàn)解決方法:
1.應(yīng)用服務(wù)器提示錯(cuò)誤的解決:
??把啟動(dòng)參數(shù)內(nèi)存值設(shè)置足夠大。
2.Java代碼導(dǎo)致錯(cuò)誤的解決:
1)檢查代碼中是否有死循環(huán)或遞歸調(diào)用。
2)檢查是否有大循環(huán)重復(fù)產(chǎn)生新對(duì)象實(shí)體。
3)檢查對(duì)數(shù)據(jù)庫(kù)查詢中,是否有一次獲得全部數(shù)據(jù)的查詢。一般來(lái)說(shuō),如果一次取十萬(wàn)條記錄到內(nèi)存,就可能引起內(nèi)存溢出。這個(gè)問(wèn)題比較隱蔽,在上線前,數(shù)據(jù)庫(kù)中數(shù)據(jù)較少,不容易出問(wèn)題,上線后,數(shù)據(jù)庫(kù)中數(shù)據(jù)多了,一次查詢就有可能引起內(nèi)存溢出。因此對(duì)于數(shù)據(jù)庫(kù)查詢盡量采用分頁(yè)的方式查詢。
4 )檢查L(zhǎng)ist、MAP等集合對(duì)象是否有使用完后,未清除的問(wèn)題。List、MAP等集合對(duì)象會(huì)始終存有對(duì)對(duì)象的引用,使得這些對(duì)象不能被GC回收。
IOException
IO,即:input, output,我們?cè)谧x寫(xiě)磁盤(pán)文件、網(wǎng)絡(luò)內(nèi)容的時(shí)候經(jīng)常會(huì)生的一種異常,這種異常是受檢查異常,需要進(jìn)行手工捕獲。常用的一種異常處理方式有兩種,一種是:使用throws拋出可能發(fā)生的異常,另一種是:直接try-catch。
ClassNotFoundException
類找不到異常,Java開(kāi)發(fā)中經(jīng)常遇到,是不是很絕望?這是在加載類的時(shí)候拋出來(lái)的,即在類路徑下不能加載指定的類。
注意:在車(chē)道程序中不推薦使用throws把異常拋給系統(tǒng)處理,車(chē)道系統(tǒng)要捕獲所有能捕獲的異常并進(jìn)行處理,目的是當(dāng)發(fā)生程序級(jí)異常時(shí)要保證車(chē)道程序可以正常收費(fèi),不可因?yàn)槌绦虍惓S绊懙秸谶M(jìn)行的收費(fèi)處理。
異常處理的一般原則
1、 能處理就早處理,拋出不去還不能處理的異常就要想辦法消化掉,或者轉(zhuǎn)換為RuntimeException處理。因?yàn)閷?duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),拋出大量異常是有問(wèn)題的,應(yīng)該從程序開(kāi)發(fā)角度盡可能的控制異常發(fā)生的可能。
2、 對(duì)于檢查異常,如果不能行之有效的處理,還不如轉(zhuǎn)換為RuntimeException拋出。這樣也讓上層的代碼有選擇的余地――可處理也可不處理。
3、 對(duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),應(yīng)該有自己的一套異常處理框架,這樣當(dāng)異常發(fā)生時(shí),也能得到統(tǒng)一的處理風(fēng)格,將優(yōu)雅的異常信息反饋給用戶。
異常的轉(zhuǎn)譯與異常鏈?
?1、異常轉(zhuǎn)譯的原理?
所謂的異常轉(zhuǎn)譯就是將一種異常轉(zhuǎn)換另一種新的異常,也許這種新的異常更能準(zhǔn)確表達(dá)程序發(fā)生異常。
在Java中有個(gè)概念就是異常原因,異常原因?qū)е庐?dāng)前拋出異常的那個(gè)異常對(duì)象,幾乎所有帶異常原因的異常構(gòu)造方法都使用Throwable類型做參數(shù),這也就為異常的轉(zhuǎn)譯提供了直接的支持,因?yàn)槿魏涡问降漠惓:湾e(cuò)誤都是Throwable的子類。比如將SQLException轉(zhuǎn)換為另外一個(gè)新的異常DAOException,可以這么寫(xiě):
??? 先自定義一個(gè)異常DAOException:
???比如有一個(gè)SQLException類型的異常對(duì)象e,要轉(zhuǎn)換為DAOException,可以這么寫(xiě):??
public class DAOException extends RuntimeException {
//(省略了部分代碼)
public DAOException(String message, Throwable cause) {
super(message, cause);
}
}
DAOException daoEx = new DAOException ( "SQL異常", e);
異常轉(zhuǎn)譯是針對(duì)所有繼承Throwable超類的類而言的,從編程的語(yǔ)法角度講,其子類之間都可以相互轉(zhuǎn)換。但是,從合理性和系統(tǒng)設(shè)計(jì)角度考慮,可將異常分為三類:Error、Exception、RuntimeException。
?異常的處理存在著一套哲學(xué)思想:對(duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),系統(tǒng)所發(fā)生的任何異?;蛘咤e(cuò)誤對(duì)操作用戶來(lái)說(shuō)都是系統(tǒng)"運(yùn)行時(shí)"異常,都是這個(gè)應(yīng)用系統(tǒng)內(nèi)部的異常。這也是異常轉(zhuǎn)譯和應(yīng)用系統(tǒng)異??蚣茉O(shè)計(jì)的指導(dǎo)原則。在系統(tǒng)中大量處理非檢查異常的AxiTrader返傭www.fx61.com/brokerlist/axitrader.html負(fù)面影響很多,最重要的一個(gè)方面就是代碼可讀性降低,程序編寫(xiě)復(fù)雜,異常處理的代碼也很蒼白無(wú)力。因此,很有必要將這些檢查異常Exception和錯(cuò)誤Error轉(zhuǎn)換為RuntimeException異常,讓程序員根據(jù)情況來(lái)決定是否捕獲和處理所發(fā)生的異常。
①:Error到Exception:將錯(cuò)誤轉(zhuǎn)換為異常,并繼續(xù)拋出。例如Spring WEB框架中,將org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中,將捕獲的錯(cuò)誤轉(zhuǎn)譯為一個(gè)NestedServletException異常。這樣做的目的是為了最大限度挽回因錯(cuò)誤發(fā)生帶來(lái)的負(fù)面影響。因?yàn)橐粋€(gè)Error常常是很?chē)?yán)重的錯(cuò)誤,可能會(huì)引起系統(tǒng)掛起。
②:Exception到RuntimeException:將檢查異常轉(zhuǎn)換為RuntimeException可以讓程序代碼變得更優(yōu)雅,讓開(kāi)發(fā)人員集中精力設(shè)計(jì)更合理的程序代碼,反過(guò)來(lái)也增加了系統(tǒng)發(fā)生異常的可能性。
③:Error到RuntimeException:目的還是一樣的。把所有的異常和錯(cuò)誤轉(zhuǎn)譯為不檢查異常,這樣可以讓代碼更為簡(jiǎn)潔,還有利于對(duì)錯(cuò)誤和異常信息的統(tǒng)一處理。
1、 異常鏈
異常鏈顧名思義就是將異常發(fā)生的原因一個(gè)傳一個(gè)串起來(lái),即把底層的異常信息傳給上層,這樣逐層拋出。Java API文檔中給出了一個(gè)簡(jiǎn)單的模型:?
try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException)
new HighLevelException().initCause(le);
}
當(dāng)程序捕獲到了一個(gè)底層異常le,在處理部分選擇了繼續(xù)拋出一個(gè)更高級(jí)別的新異常給此方法的調(diào)用者。這樣異常的原因就會(huì)逐層傳遞。這樣,位于高層的異常遞歸調(diào)用getCause()方法,就可以遍歷各層的異常原因。這就是Java異常鏈的原理。異常鏈的實(shí)際應(yīng)用很少,發(fā)生異常時(shí)候逐層上拋不是個(gè)好注意,上層拿到這些異常又能奈之何?而且異常逐層上拋會(huì)消耗大量資源,因?yàn)橐4嬉粋€(gè)完整的異常鏈信息。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI