Markdown版本笔记 | 个人GitHub首页 | 个人博客 | 个人微信 | 个人邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
问:在 try catch 中开启新的线程,能捕获线程里面的异常吗?java
例如:git
try { new Thread(() -> System.out.println(1 / 0)).start(); //Runnable的run()方法抛出了 unchecked exception //new Thread(() -> throw new RuntimeException("抛出了 unchecked exception")).start(); } catch (Exception e) { e.printStackTrace(); System.out.println("这里能执行到吗?"); //不能够 }
上面是捕获不到异常的,而若是改成下面这种形式,则能够捕获到异常:github
new Thread(() -> { try { System.out.println(1 / 0); //Runnable的run()方法并无抛出异常,而是本身捕获了异常 } catch (Exception e) { e.printStackTrace(); System.out.println("这里能执行到吗?"); //能够 } }).start();
其实使用 try catch 捕获异常时有一个规范,那就是尽可能用 try catch 包住最少的代码,有些同窗一上来就用 try catch 把整个方法的逻辑包住,这样很是不合适,好比就会致使上述 try catch 失效。面试
在java多线程程序中,全部线程都不容许抛出checked exception
,也就是说各个线程的checked exception
必须由本身捕获
。这一点是经过java.lang.Runnable.run()
方法声明进行的约束,由于此方法声明上没有throws
部分。微信
可是线程依然有可能抛出一些运行时的异常(即unchecked exception
,RuntimeException),当此类异常跑抛出时,此线程就会终结
,而对于其余线程彻底不受影响,且彻底感知不到某个线程抛出的异常。多线程
JVM的这种设计源自于这样一种理念:线程是独立执行的代码片段
,线程的问题应该由线程本身来解决,而不要委托到外部。less
在Java中,线程方法的异常(不管是checked仍是unchecked exception),都应该在线程代码边界以内(run方法内)进行try catch并处理掉。换句话说,咱们不能捕获从线程中逃逸的异常。dom
查看 Thread 的源码能够帮忙分析当线程出现未捕获异常时的处理逻辑。测试
首先看Thread.dispatchUncaughtException()
方法:this
//Dispatch an uncaught exception to the handler. This method is intended to be called only by the JVM. private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); }
这个方法仅仅被 JVM 调用,用来将 uncaught exception 分发到 handler 去处理,这个 handler 是哪来的呢?
public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; }
Returns the handler invoked when this thread abruptly terminates due to an uncaught exception.
返回此线程因为未捕获的异常而忽然终止时调用的handler。
If this thread has not had an uncaught exception handler explicitly set then this thread's ThreadGroup object is returned, unless this thread has terminated, in which case null is returned.
若是此线程没有显式设置未捕获的异常 handler,则返回此线程的 ThreadGroup 对象,除非此线程已终止,在这种状况下返回 null。
这里的uncaughtExceptionHandler
只有一个地方初始化:
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;// null unless explicitly set public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { checkAccess(); uncaughtExceptionHandler = eh; }
若是返回的是 ThreadGroup 的话,默认会一直找到顶层 ThreadGroup(相似双亲委派模型),而后会找 Thread 类共用的 defaultUncaughtExceptionHandler
,若是存在则调用,若是不存在,则打印线程名字和异常:
public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { //不是ThreadDeath System.err.print("Exception in thread \"" + t.getName() + "\" "); //打印线程名字 e.printStackTrace(System.err); //打印异常 } } }
而defaultUncaughtExceptionHandler
也只有一个地方初始化:
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;// null unless explicitly set public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { //检查权限 defaultUncaughtExceptionHandler = eh; }
也就是说,当一个线程中有未捕获的异常时,JVM 会经过调用 UncaughtExceptionHandler 的 uncaughtException 方法处理异常,若是没有设置,则会直接打印线程名字和异常。
问:Java异常捕获机制try...catch...finally
块中的finally
语句是否是必定会被执行?
答:不必定,至少有两种状况下finally语句是不会被执行的:
System.exit(0)
这样的语句,System.exit(0)
是终止Java虚拟机JVM的,连JVM都中止了,全部都结束了,固然finally语句也不会被执行到。finally语句是在try的return语句执行以后,return返回以前
执行的
测试案例:
System.out.println(test()); public String test() { try { System.out.println("try block"); if (new Random().nextBoolean()) return "直接返回"; else return test2(); } finally { System.out.println("finally block"); } } public String test2() { System.out.println("return statement"); //return语句执行以后才执行finally语句 return "调用方法返回"; }
运行结果:
try block finally block 直接返回
或
try block return statement finally block 调用方法返回
说明try中的return语句先执行了,但并无当即返回,而是等到finally执行结束后再返回
。
这里你们可能会想:若是finally里也有return语句,那么是否是就直接返回了,try中的return就不能返回了?看下面。
finally块中的return语句会覆盖try块中的return返回
System.out.println(test()); public static String test() { try { System.out.println("try block"); return "在try中返回"; } finally { System.out.println("finally block"); return "在finally中返回"; } // return "finally外面的return就变成不可到达语句,须要注释掉不然编译器报错"; }
运行结果:
try block finally block 在finally中返回
这说明finally里的return直接返回了
,就无论try中是否还有返回语句。
这里还有个小细节须要注意,finally里加上return事后,finally外面的return b就变成不可到达语句了
,也就是永远不能被执行到,因此须要注释掉不然编译器报错。
若是finally语句中没有return语句覆盖返回值,那么原来的返回值可能由于finally里的修改而改变,也可能不变
测试用例:
System.out.println(test()); public static int test() { int b = 20; try { System.out.println("try block"); return b += 80; } finally { b += 10; System.out.println("finally block"); } }
运行结果:
try block finally block 100 //这是关键
测试用例2:
System.out.println(test()); public static List<Integer> test() { List<Integer> list = new ArrayList<Integer>(); list.add(10086); try { System.out.println("try block"); return list; } finally { list.add(10088); System.out.println("finally block"); } }
运行结果:
try block finally block [10086, 10088] //这是关键
这其实就是Java究竟是传值仍是传址的问题了
,简单来讲就是:Java中只有传值没有传址
。
这里你们可能又要想:是否是每次返回的必定是try中的return语句呢?那么finally外的return不是一点做用没吗?请看下面
try块里抛异常的return语句在异常的状况下不会被执行,这样具体返回哪一个看状况
public class TestFinally { public static void main(String[] args) { System.out.println(test()); } public static int test() { int b = 0; try { System.out.println("try block"); b = b / 0; return b += 1; } catch (Exception e) { b += 10; System.out.println("catch block"); } finally { b += 100; System.out.println("finally block"); } return b; } }
运行结果是:
try block catch block finally block 110
这里因 为在return以前发生了异常,因此try中的return不会被执行到
,而是接着执行捕获异常的 catch 语句和最终的 finally 语句,此时二者对b的修改都影响了最终的返回值,这时最后的 return b 就起到做用了。
这里你们可能又有疑问:若是catch中有return语句呢?固然只有在异常的状况下才有可能会执行,那么是在 finally 以前就返回吗?看下面。
当发生异常后,catch中的return执行状况与未发生异常时try中return的执行状况彻底同样
public class TestFinally { public static void main(String[] args) { System.out.println(test()); } public static int test() { int b = 0; try { System.out.println("try block"); b = b / 0; return b += 1; } catch (Exception e) { b += 10; System.out.println("catch block"); return 10086; } finally { b += 100; System.out.println("finally block"); } //return b; } }
运行结果:
try block catch block finally block 10086
说明了发生异常后,catch中的return语句先执行,肯定了返回值后再去执行finally块,执行完了catch再返回,也就是说状况与try中的return语句执行彻底同样。
Java包含两种异常:checked异常和unchecked异常:
java.lang.Exception
类,Checked异常必须经过try-catch被显式地捕获
或者经过throws
子句进行传递。java.lang.RuntimeException
类,是那些可能在 Java 虚拟机正常运行期间抛出的异常
,unchecked异常能够即没必要捕获也不抛出。运行时异常
运行时异常咱们通常不处理,当出现这类异常的时候程序会由虚拟机接管。好比,咱们历来没有去处理过NullPointerException,并且这个异常仍是最多见的异常之一。
出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇处处理代码,若是没有catch块进行处理,到了最上层,若是是多线程就有Thread.run()抛出,若是不是多线程那么就由main.run()抛出。抛出以后,若是是线程,那么该线程也就终止了,若是是主程序,那么该程序也就终止了。
其实运行时异常的也是继承自Exception,也能够用catch块对其处理,只是咱们通常不处理罢了,也就是说,若是不对运行时异常进行catch处理,那么结果不是线程退出就是主程序终止。若是不想终止,那么咱们就必须捕获全部可能出现的运行时异常。
两种异常的使用场景
功能
的角度来说是等价的,能够用checked异常实现的功能必然也能够用unchecked异常实现,反之亦然。public class Test { public static void main(String[] args) { try { new Test().testException(); } catch (MyException e) { e.printStackTrace(); System.out.println("调用抛出checked异常的方法时,一样必须经过try-catch显式地捕获或者经过throws子句进行传递"); } } void testException() throws MyException { throw new MyException("抛出checked异常时,必须经过try-catch显式地捕获或者经过throws子句进行传递"); } class MyException extends Exception { MyException(String s) { super(s); } } }
和上面相比,只需把自定义的异常由继承自Exception改成继承自RuntimeException便可。
因为RuntimeException继承自Exception,因此修改后上面其余代码都不须要改变就能够正常使用。但RuntimeException能够即没必要捕获也不抛出:
public class Test { public static void main(String[] args) { new Test().testException(); } void testException() { throw new MyException("unchecked异常能够即没必要捕获也不抛出"); } class MyException extends RuntimeException { MyException(String s) { super(s); } } }
public class Test { public static void main(String[] args) { try { start(); } catch (IOException e) { e.printStackTrace(); } } public static void start() { System.out.println("这个方法并无声明会抛出checked 异常,例如IOException"); } }
上面的代码编译是通不过的:
由于IOException是checked异常,而start方法并无抛出IOException,编译器将在处理IOException时报错。
可是若是你将IOException改成Exception,编译器报错将消失,由于Exception能够用来捕捉全部运行时异常(包括unchecked异常),这样就不须要声明抛出语句。
将上例中的 IOException 改成 unchecked 异常也是能够的,例如改成 NullPointerException
ClassNotFoundException
,CloneNotSupportedException
,DataFormatException,IllegalAccessException,InterruptedException
,IOException
,NoSuchFieldException
,NoSuchMethodException
,ParseException,TimeoutException
,XMLParseExceptionClassCastException
,IllegalArgumentException
,IllegalStateException,IndexOutOfBoundsException
,NoSuchElementException,NullPointerException
,SecurityException,SystemException,UnsupportedOperationExceptionOutOfMemoryError
、StackOverflowError
、NoClassDefFoundError、UnsatisfiedLinkError、IOError
、ThreadDeath
、ClassFormatError、InternalError、UnknownError2019-4-26