internal java compiler error_Java异常处理总结
? ? ? ? ? ? 背景? ? ? ? ? ? ??
最近專門負(fù)責(zé)團(tuán)隊(duì)的項(xiàng)目質(zhì)量。我在治理異常日志過程中,總結(jié)了一下Java的異常處理。上面是我整理的最近自己比較常見的異常知識(shí)地圖。
? 異常知識(shí)地圖概述??
從異常知識(shí)地圖最左邊的根開始看,地圖從左到右的連線連接的類之間有實(shí)實(shí)在在的父子關(guān)系,在java里通過繼承來實(shí)現(xiàn)(除了非RuntimeException是個(gè)虛擬父節(jié)點(diǎn))。
☆Java所有異常的父類是Throwable,它又分為Error和Exception。
☆?Error是程序判定如果執(zhí)行了XX邏輯,則應(yīng)該是至少JVM層面出現(xiàn)了問題。正常情況下不應(yīng)該發(fā)生的。
☆?Exception意思是環(huán)境沒有什么問題,出現(xiàn)Exception請(qǐng)開發(fā)人員自己搞定。
☆?Exception分為RuntimeException運(yùn)行時(shí)異常和非運(yùn)行時(shí)異常。
說到這里,我們從另外一個(gè)維度給異常分類。Java異常又分為檢查異常和非檢查異常。Error和RuntimeException以及RuntimeException的子類是非檢查異常。其他是檢查異常。這個(gè)很好區(qū)分。在寫Java代碼的時(shí)候,編譯器提示需要try catch或者throws的就是檢查異常。其他是非檢查異常。后面在具體代碼實(shí)現(xiàn)里有體現(xiàn)。
☆?異常分為檢查異常和非檢查異常。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ?典型異常發(fā)生場(chǎng)景? ?
典型異常發(fā)生的場(chǎng)景我做了一些demo,上傳到了github,地址:
https://github.com/xiexiaojing/yuna
為了方面展示使用一個(gè)統(tǒng)一的切面來截獲異常:
@RestControllerAdvicepublic class ControllerThrowableAdvice { @ExceptionHandler(Throwable.class) public String handleThrowable(Throwable e) { return "ControllerThrowableAdvice消息:" + e.toString(); }}ErrorError及其子類一般不是用來捕獲的,而用來拋出的。因?yàn)镋rror的發(fā)生意味著環(huán)境有問題,該停下來檢修了。所以一般的處理是一旦發(fā)生Error,會(huì)停止JVM。也就是平時(shí)看到的程序起不來。如下java.awt.image.Kernel的源碼。Error除了手工拋出,在常用的類庫中不用黑科技是不能穩(wěn)定復(fù)現(xiàn)的。所以我測(cè)試類是這么寫的
@GetMapping("/errorThrowable")public String showErrorThrowable(){ Error error = new Error("人工拋出一個(gè)Error"); throw error;}
直接訪問頁面的結(jié)果
ControllerThrowableAdvice消息:?
org.springframework.web.util.NestedServletException:Handler dispatch failed; nested exception is java.lang.Error: 人工拋出一個(gè)Error
上面錯(cuò)誤消息意思是spring mvc通過其核心邏輯DispatcherServlet沒有找到任何一個(gè)可以處理這個(gè)返回model的,因?yàn)橹苯臃祷鼐褪且粋€(gè)Error。最后顯示的消息通過ControllerThrowableAdvice進(jìn)行展示。
注意Error是非檢查異常,不用顯示處理。
NPE
NPE也就是平時(shí)說的空指針異常,它非常常見,很多類都沒有對(duì)null做支持。直到apache提供了common包專門來處理這種情況。防不勝防,時(shí)不時(shí)項(xiàng)目還是需要為了處理這個(gè)異常上線個(gè)bugfix。
@GetMapping("/npe")public String showNullPointerException() { new HashSet<String>(null); return prefix + "異常未拋出";}
直接訪問頁面的結(jié)果
ControllerThrowableAdvice消息:java.lang.NullPointerException
這是因?yàn)閚ew HashSet的時(shí)候傳入null。程序走不到return就拋出異常了。最后顯示的消息通過ControllerThrowableAdvice進(jìn)行展示。
注意NullPointerException是非檢查異常,不用顯示處理。
算數(shù)異常
算數(shù)異常非常常見,比如做除0,會(huì)拋出異常java.lang.ArithmeticException: / by zero,提示我們?cè)撜覕?shù)學(xué)老師幫我們檢查作業(yè)了。值得注意的是如果使用BigDecimal.divide來做除法,請(qǐng)直接使用divide(BigDecimal divisor, int scale, RoundingMode roundingMode)這個(gè)傳3個(gè)參數(shù)的,避免divide(BigDecimal divisor)這個(gè)傳1個(gè)參數(shù)的,因?yàn)槿绻麄鞯闹党槐M會(huì)拋出java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.?帶三個(gè)參數(shù)的方法會(huì)在除不盡的時(shí)候按照傳入的攝入模式和保留小數(shù)點(diǎn)后的位數(shù)對(duì)數(shù)據(jù)做處理。
注意ArithmeticException是非檢查異常,不用顯示處理。未聲明異常
未聲明異常代碼量稍大,想知道測(cè)試源碼的直接去我github里下載。地址:https://github.com/xiexiaojing/yuna拋出異常的原理是使用動(dòng)態(tài)代理時(shí),如果被代理的類拋出了一個(gè)異常。但是卻沒有throws聲明。代理類找不到匹配的異常類型會(huì)拋出InvocationTargetException。從知識(shí)地圖上可以看到它是非檢查異常。最后會(huì)被UndeclaredThrowableException來處理。這是java動(dòng)態(tài)代理不優(yōu)雅的處理方式。建議喜歡看源碼、模仿源碼的朋友對(duì)這一點(diǎn)不要借鑒哦。重點(diǎn)來看一下運(yùn)行拋出異常的打印堆棧java.lang.reflect.UndeclaredThrowableException at com.sun.proxy.$Proxy17.showException(Unknown Source) at com.brmayi.yuna.controller.ExceptionController.showUndeclaredThrowableException(ExceptionController.java:62) at com.brmayi.yuna.controller.IndexControllerTest.showUndeclaredThrowableException(IndexControllerTest.java:34) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:44) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:74) at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:80) at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39) at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.brmayi.yuna.util.ObjProxy.invoke(ObjProxy.java:14) ... 35 moreCaused by: java.lang.ArithmeticException: / by zero at com.brmayi.yuna.service.ShowUndeclaredThrowableExceptionService.showException(ShowUndeclaredThrowableExceptionService.java:5) ... 40 more從打印的堆棧可以看到這個(gè)是最終的ArithmeticException拋出時(shí)被InvocationTargetException捕獲。將原來的參數(shù)傳給了InvocationTargetException后繼續(xù)拋出,最后被UndeclaredThrowableException捕獲。注意UndeclaredThrowableException是非檢查異常,不用顯示處理。不合法參數(shù)異常不合法參數(shù)異常是很多類或者方法自己定義了java基本規(guī)則外的一些規(guī)則,不滿足會(huì)拋出的異常。比如java動(dòng)態(tài)代理的源碼里就寫了被代理的接口不能超過65535個(gè)。否則就拋不合法參數(shù)異常。注意IllegalArgumentException是非檢查異常,不用顯示處理。
以上都說的是非檢查異常。下面開始檢查異常。由于IO異常很常見好構(gòu)造,我們直接來看它的子類。套接字異常套接字異常在通信編程時(shí)非常常見。比如如下代碼 @GetMapping("/socket")public String showSocketException() throws Exception { ServerSocket socket = new ServerSocket(8081); socket.close(); socket.setReuseAddress(true); return prefix + "異常未拋出";}啟動(dòng)了一個(gè)套接口服務(wù)端,馬上關(guān)閉。關(guān)閉后才去調(diào)用setReuseAddress。這時(shí)候就會(huì)拋出java.net.SocketException: Socket is closed。
注意SocketException是檢查異常,需要顯示處理。
綁定異常????套接字異常有一種情況,可以明確的知道是綁定異常,就不用拋出套接字異常這樣模糊的異常了。@GetMapping("/binding")public String showBindingException() throws Exception { ServerSocket socket = new ServerSocket(80); socket.setReuseAddress(true); return prefix + "異常未拋出";}如上,80端口是http默認(rèn)端口,不能在自定義通信程序里使用。這時(shí)候就會(huì)拋出java.net.BindException: Permission denied。
注意BindException是檢查異常,需要顯示處理。
主機(jī)名未知異常主機(jī)名未知異常在比如內(nèi)網(wǎng)DNS出現(xiàn)問題、或者遠(yuǎn)程調(diào)用時(shí)由于機(jī)器下線等原因找不到主機(jī)時(shí)出現(xiàn)。可以人為連接一個(gè)未啟用的端口來構(gòu)造。
@GetMapping("/unknownHost")public String showUnknownHostException() throws Exception { new Socket("ttt", 5300); return prefix + "異常未拋出";}注意UnknownHostException是檢查異常,需要顯示處理。超時(shí)異常超時(shí)異常因?yàn)樵诜植际较到y(tǒng)中涉及程序內(nèi)部線程間、程序之間的通信多,所以非常常見。具體代碼有點(diǎn)長(zhǎng),詳見
https://github.com/xiexiaojing/yuna
拋出java.util.concurrent.TimeoutException。它是concurrent包里的一個(gè)類。
注意TimeoutException是檢查異常,需要顯示處理。
反射操作異常及其子類反射操作異常一般只在啟動(dòng)時(shí)看到,線上程序運(yùn)行中一般不會(huì)發(fā)生。因?yàn)槌R婎惱锼沁@么處理的
上面可以看到在java.net.InetAddress的源碼里,ReflectiveOperationException的處理是直接拋出Error。在程序啟動(dòng)時(shí),經(jīng)常會(huì)由于maven pom里引入的包沖突、版本不合適、或者是缺少包引起「類找不到異常」。例如下面的測(cè)試?yán)?#xff1a;@GetMapping("/classNotFound")public String showClassNotFoundException() throws Exception { Class.forName("com.XXX"); return prefix + "異常未拋出";}由于com.XXX不存在。會(huì)直接拋出java.lang.ClassNotFoundException: com.XXX。它是反射操作異常的子類。平時(shí)反射操作異常及它的子類異常一旦發(fā)生就會(huì)拋出Error,JVM停止。如下面的源碼:
Spring對(duì)于異常的處理
默認(rèn)異常處理Spring的MVC在默認(rèn)情況下對(duì)不能處理的異常如404、500會(huì)拋出白頁。像下面這樣:
這是因?yàn)镾pring MVC的核心處理類DispatcherServlet的doDispatch方法包含代碼片段:mappedHandler = getHandler(processedRequest);// 找到合適的請(qǐng)求處理器if (mappedHandler == null || mappedHandler.getHandler() == null) { // 原則上如果沒有找到則會(huì)進(jìn)入到這里,并且設(shè)置response的狀態(tài)碼為404 // 但是經(jīng)過調(diào)試并沒有進(jìn)入到這里 noHandlerFound(processedRequest, response); return;}它最終處理是返回/error頁。也就是白頁。不知道大家有沒有注意到我前面在介紹Error的時(shí)候,定義了error頁面的url地址為errorThrowable。
這是因?yàn)閑rror是被Spring自身占用了。如果定義為error,我們將看不到預(yù)期的結(jié)果,而是下面的白頁
這里因?yàn)?error是默認(rèn)頁面,返回了999的http錯(cuò)誤碼,意思是請(qǐng)求被拒絕。自定義異常處理在典型異常的發(fā)生場(chǎng)景里一開始就介紹了定義了一個(gè)統(tǒng)一錯(cuò)誤處理如下:
@RestControllerAdvicepublic class ControllerThrowableAdvice { @ExceptionHandler(Throwable.class) public String handleThrowable(Throwable e) { return "ControllerThrowableAdvice消息:" + e.toString(); }}這是使用了spring aop做了統(tǒng)一攔截。Advice在AOP的概念中翻譯成增強(qiáng)。包括Before、After、Around等增強(qiáng)時(shí)機(jī)。這里類名用到了Advice意思是在controller發(fā)生Throwable時(shí)做的增強(qiáng)。看到有的項(xiàng)目喜歡用
@ExceptionHandler(Exception.class)這個(gè)也OK。但是我會(huì)假設(shè)Everything fails! 程序在發(fā)生平時(shí)不會(huì)遇到的問題時(shí)也可控。
??? ? ? ? ? ? 總結(jié)??? ? ? ? ? ???
本文先圍繞著異常知識(shí)地圖介紹了各種異常及出現(xiàn)場(chǎng)景,最后結(jié)合Spring論述了在實(shí)際工作中如何統(tǒng)一處理異常。這里推薦一個(gè)學(xué)習(xí)方法:梳理知識(shí)地圖,給地圖框架填充內(nèi)容,讓自己的知識(shí)體系化。
總結(jié)
以上是生活随笔為你收集整理的internal java compiler error_Java异常处理总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习算法平台alink_Alink漫
- 下一篇: java虚引用作用_深入理解Java中的