分享一個(gè)你不知道的Java異常實(shí)現(xiàn)的缺陷
前言
Java中一個(gè)大家熟知的知識(shí)點(diǎn)就是異常捕獲,try...catch...finally組合,但是很多人不知道這里面有一個(gè)關(guān)于Java的缺陷,或者說(shuō)是異常實(shí)現(xiàn)的一點(diǎn)不足之處。
我這邊就通過(guò)一個(gè)很簡(jiǎn)單的實(shí)驗(yàn)給大家演示下效果玩玩兒,希望大家能覺(jué)得有趣。
模擬
1、自定義異常
這里,我們首先寫(xiě)一個(gè)自定義業(yè)務(wù)異常,專(zhuān)門(mén)用來(lái)拋出。
/**
* <p>
* 自定義業(yè)務(wù)異常
* </p>
*
* @author 程序員濟(jì)癲,公眾號(hào):【Java分享客?!?
* @since 2022/12/6 19:20
*/
public class BusinessException extends RuntimeException {
public BusinessException() {
super();
}
public BusinessException(String errMsg) {
super(errMsg);
}
public BusinessException(String errMsg, Throwable throwable) {
super(errMsg, throwable);
}
}
2、模擬異常
然后,我們寫(xiě)個(gè)測(cè)試方法來(lái)捕獲并拋出空指針異常。
/**
* <p>
* 測(cè)試異常缺陷
* </p>
*
* @author 程序員濟(jì)癲,公眾號(hào):【Java分享客棧】
* @since 2022/12/6 19:22
*/
@Slf4j
public class ExceptionTest {
public static void main(String[] args) {
try {
UserInfo userInfo = null;
System.err.println(userInfo.getName());
} catch (Exception ex) {
throw new BusinessException("捕獲try的異常: " + ex.getMessage());
}
}
}
看下效果,OK沒(méi)問(wèn)題。

接下來(lái),我們加上finally看看。
/**
* <p>
* 測(cè)試異常缺陷
* </p>
*
* @author 程序員濟(jì)癲,公眾號(hào):【Java分享客?!?
* @since 2022/12/6 19:22
*/
@Slf4j
public class ExceptionTest {
public static void main(String[] args) {
try {
UserInfo userInfo = null;
System.err.println(userInfo.getName());
} catch (Exception ex) {
throw new BusinessException("捕獲try的異常: " + ex.getMessage());
} finally {
System.err.println("----------finally最終執(zhí)行---------");
}
}
}
看下效果,OK也沒(méi)問(wèn)題。

接下來(lái)我們這么做,在finally中拋出一個(gè)異常。
/**
* <p>
* 測(cè)試異常缺陷
* </p>
*
* @author 程序員濟(jì)癲,公眾號(hào):【Java分享客?!?
* @since 2022/12/6 19:22
*/
@Slf4j
public class ExceptionTest {
public static void main(String[] args) {
try {
UserInfo userInfo = null;
System.err.println(userInfo.getName());
} catch (Exception ex) {
throw new BusinessException("捕獲try的異常: " + ex.getMessage());
} finally {
System.err.println("----------finally最終執(zhí)行---------");
throw new BusinessException("覆蓋catch的異常");
}
}
}
看下效果,發(fā)現(xiàn)catch的異常竟然被覆蓋了。

雖然這種場(chǎng)景很特殊,但這其實(shí)就是Java在異常實(shí)現(xiàn)上美中不足的地方,因?yàn)楫惓J亲鳛槌绦虺鲥e(cuò)的標(biāo)志絕對(duì)不應(yīng)忽略,可是這種場(chǎng)景下異常的的確確丟失了。
接下來(lái),我們?cè)贉y(cè)試一種情況,在finally中使用return,看看會(huì)發(fā)生什么。
/**
* <p>
* 測(cè)試異常缺陷
* </p>
*
* @author 程序員濟(jì)癲,公眾號(hào):【Java分享客?!?
* @since 2022/12/6 19:22
*/
@Slf4j
public class ExceptionTest {
public static void main(String[] args) {
try {
UserInfo userInfo = null;
System.err.println(userInfo.getName());
} catch (Exception ex) {
throw new BusinessException("捕獲try的異常: " + ex.getMessage());
} finally {
System.err.println("----------finally最終執(zhí)行---------");
return;
}
}
}
看看效果,發(fā)現(xiàn)catch中捕獲的異常干脆直接沒(méi)了,仿佛從沒(méi)來(lái)過(guò)。

最后,我們?cè)傺菔疽环N你可能工作中干過(guò)或者見(jiàn)過(guò)的莫名其妙的事情。
我們修改一下這個(gè)測(cè)試方法,看代碼。
/**
* <p>
* 測(cè)試異常缺陷
* </p>
*
* @author 程序員濟(jì)癲,公眾號(hào):【Java分享客?!?
* @since 2022/12/6 19:22
*/
@Slf4j
public class ExceptionTest {
public static void main(String[] args) {
try {
// 調(diào)用其他類(lèi)的查詢方法
queryData();
} catch (Exception ex) {
// 捕獲這個(gè)查詢方法拋出的異常
throw new BusinessException(ex.getMessage());
}
}
private static void queryData() {
try {
// 這個(gè)方法剛好你也try..catch了,并且在finally中做了一些末尾必須執(zhí)行的業(yè)務(wù)邏輯處理。
UserInfo userInfo = null;
System.err.println(userInfo.getName());
} finally {
doSomething();
}
}
private static void doSomething() {
// 業(yè)務(wù)處理過(guò)程中你同樣習(xí)慣性地拋出了某個(gè)業(yè)務(wù)異常
System.err.println("--------處理末尾業(yè)務(wù)--------");
throw new BusinessException("處理末尾業(yè)務(wù)拋出邏輯異常");
}
}
簡(jiǎn)單描述一下,你調(diào)用其他類(lèi)的一個(gè)查詢方法,那個(gè)方法可能習(xí)慣性的try..catch..finally了,而finally中還做了一些末尾必須要執(zhí)行的操作,這個(gè)業(yè)務(wù)邏輯處理可能有幾十行,你很可能又習(xí)慣性的做了一些判斷以及異常的拋出。
別不相信,當(dāng)一個(gè)項(xiàng)目進(jìn)入中期甚至趕進(jìn)度的時(shí)候,方法套方法,不少人已經(jīng)在機(jī)械的茫然的寫(xiě)代碼,也可能是在別人的基礎(chǔ)上改代碼,你很可能不會(huì)太仔細(xì)地一行一行去看那些代碼里究竟有什么,恰巧測(cè)試的時(shí)候也沒(méi)出什么大問(wèn)題。
那么結(jié)果可能就是下面這樣,你會(huì)發(fā)現(xiàn)自己一開(kāi)始特意拋出的那個(gè)捕獲該查詢方法異常的玩意兒沒(méi)一點(diǎn)效果,也不知道去哪兒了,怎么找也找不到,不知從哪兒冒出來(lái)下面這個(gè)莫名其妙的異常,后來(lái)想不到也就算了。

原因
這其實(shí)就是Java異常實(shí)現(xiàn)的一個(gè)不足,異常是程序出錯(cuò)的標(biāo)志,怎么都不應(yīng)該被忽略掉,更不用說(shuō)是finally這種常用的行為,直接或間接地造成了異常的丟失。
《Thinking In Java》的作者有明確指出這個(gè)異常,認(rèn)為這是相當(dāng)嚴(yán)重的缺陷,是一個(gè)可能造成異常完全丟失的缺陷,而且是以一種更微妙、更難以察覺(jué)的方式在進(jìn)行。
而C++就處理的很好,會(huì)將這種在第一個(gè)異常被處理之前拋出第二個(gè)異常的情況視為嚴(yán)重的編程錯(cuò)誤。
總結(jié)
知道了這個(gè)缺陷后,其實(shí)就很好避免了。
1、避免在finally中拋出異常;
2、避免在finally中使用return;
3、catch中一定要養(yǎng)成log.error記錄異常日志的好習(xí)慣,因?yàn)閘og是一定會(huì)記錄下來(lái)的,至少不會(huì)讓你毫無(wú)線索。
結(jié)尾再演示下加了日志的效果,只要是catch我都加上日志,那么一定不會(huì)錯(cuò)過(guò)。

到此這篇關(guān)于分享一個(gè)你不知道的Java異常實(shí)現(xiàn)的缺陷的文章就介紹到這了,更多相關(guān)Java異常實(shí)現(xiàn)缺陷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java項(xiàng)目Guava包?HashMultimap使用及注意事項(xiàng)
guava基本上可以說(shuō)是java開(kāi)發(fā)項(xiàng)目中,大概率會(huì)引入的包,今天介紹的主角是一個(gè)特殊的容器HashMultmap,可以簡(jiǎn)單的將它的數(shù)據(jù)結(jié)構(gòu)理解為Map<K,?Set<V>>,今天主要介紹下基礎(chǔ)的知識(shí)點(diǎn)?HashMultmap級(jí)使用,感興趣的朋友一起看看吧2022-05-05
Spring ApplicationListener源碼解析
這篇文章主要為大家介紹了Spring ApplicationListener源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
java rocketmq--消息的產(chǎn)生(普通消息)
這篇文章主要介紹了java rocketmq--消息的產(chǎn)生(普通消息),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06
Java基礎(chǔ)知識(shí)之StringWriter流的使用
這篇文章主要介紹了Java基礎(chǔ)知識(shí)之StringWriter流的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
使用Sentinel實(shí)現(xiàn)流控和服務(wù)降級(jí)的代碼示例
Sentinel是面向分布式、多語(yǔ)言異構(gòu)化服務(wù)架構(gòu)的流量治理組件,本文將詳細(xì)為大家介紹如何使用Sentinel實(shí)現(xiàn)流控和服務(wù)降級(jí),文中有相關(guān)的代碼示例,需要的朋友可以參考下2023-05-05
基于SpringBoot實(shí)現(xiàn)郵箱找回密碼的代碼示例
本文主要介紹了如何基于SpringBoot實(shí)現(xiàn)郵箱找回密碼,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02

