Java异常
Java异常体系图
Exception的子类有很多,主要可概括为运行时异常和非运行时异常
Java异常体系结构
Throwable类时是所有异常的错误的超类,两个直接子类为Error和Exception,分别表示错误和异常,其中异常类Exception又分为运行时异常和非运行时异常,这两种异常有很大区别。
- Error和Exception
Error是程序无法处理的错误,他是有JVM产生和抛出的,比如OutOfMemoryError,ThreadDeath等。这些异常发生时,Java虚拟机JVM一般会选择线程终止。Exception是程序本身可以处理的异常,这种异常分为两大类,运行时异常和非运行时异常。程序中应当尽可能去处理这些异常 - 运行时异常和非运行时异常
运行时异常都是RuntimeException类及其子类异常,如NullPointException、IndexOutOfBoundsException等,这些异常可以再运行的过程中不被检查,程序可以选择捕获这些异常,也可以选择不捕获这些异常。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。非运行时异常时RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲必须进行处理的异常,如果不处理,程序就不能通过,如IOException,SQLException等等。
异常的捕获与处理
常见的异常捕获为以下方式
try{
}catch(Exception e1){
}catch(Exception e2){
}finally{
}
注意事项:
- try、catch、finally三个语句块均不能单独使用,三者可以组成try…catch…finally,try…catch,try…finally三种结构,catch语句可以有一个或者多个,finally语句只能有一个
- try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
- 多个catch块时候,最多只会匹配其中一个异常类且只会执行该catch块代码,而不会再执行其它的catch块,且匹配catch语句的顺序为从上到下,也可能所有的catch都没执行。
try…catch…finally只能捕获本线程抛出的异常,其他线程抛出的异常则无法捕获,下面为代码演示
public class UseOwnUncaughtException {
public static void main(String[] args) {
//Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtException("lym"));
Runnable runnable = new Runnable() {
@Override
public void run() {
throw new RuntimeException();
}
};
try{
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}catch (RuntimeException e){
e.printStackTrace();
}
}
}
那么如何全局处理异常?为什么要全局处理,不处理行不行?
全局异常有两种处理方法:
- 在每个子线程中都捕获异常
- 在主线程中使用UncaughtExceptionHandler
使用该Handler的原因:主线程中可以轻松发现异常,子线程却不行,子线程抛出未捕获异常主线程不受影响,正常执行。
UncaughtExceptionHandler的调用策略
public class ThreadGroup implements Thread.UncaughtExceptionHandler{
public void uncaughtException(Thread t,Throwable e){
//默认情况下parent是null
if(parent!=null){
parent.uncaughtException(t,e);
}else{
//调用Thread.setDefaultUncaughtExceptionHandler()
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if(ueh != null){
ueh.uncaughtException(t,e);
}else if(!(e instanceof ThreadDeath)){
System.err.print("Exception in thread" + t,getName);
e.printStackTrace(System.err);
}
}
}
}
UncaughtExceptionHandler的使用方法
由上述代码可知,如果父类为null的情况下,会调用Thread.setDefaultUncaughtexceptionHandler接口的uncaughtException方法,所以我们需要实现Thread.setDefaultUncaughtexceptionHandler接口并重写该方法。下面为具体的代码演示。
public class MyUncaughtException implements Thread.UncaughtExceptionHandler {
private String name;
public MyUncaughtException(String name){
this.name = name;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.WARNING,"线程异常终止啦" + t.getName(),e);
System.out.println(name+"捕获的异常");
}
}
public class UseOwnUncaughtException {
public static void main(String[] args) {
//Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtException("lym"));
Runnable runnable = new Runnable() {
@Override
public void run() {
throw new RuntimeException();
}
};
try{
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}catch (RuntimeException e){
e.printStackTrace();
}
}
}
try-catch-finally代码块的执行顺序
- try没有捕获异常时,try代码块中的语句依次被执行,跳过catch。如果存在finally则执行finally代码块,否则执行后续代码。
- try捕获到异常时,如果没有与之匹配的catch子句,则该异常交给JVM处理。如果存在finally,则其中的代码仍然被执行,但是finally之后的代码不会被执行。
- try捕获到异常时,如果存在与之匹配的catch,则跳到该catch代码执行处理。如果存在finally则执行finally代码块,执行完finally代码块之后继续执行后续代码,否则直接执行后续代码,另外注意,try代码块出现异常之后的代码不会被执行。
try代码块:用于捕获异常,其后可以接零个或者多个catch快。如果没有catch快,后面必需跟finally快,来完成资源释放等操作,另外不建议在finally中使用return。不用尝试通过catch来控制代码流程。
catch代码块:用于捕获异常,并在其中处理异常。finally代码块:用于捕获异常,finally代码总会执行。如果try代码块或者catch代码块中有return语句时,finally代码块将在方法返回前被执行。注意一下几种情况。finally不会被执行。
- 在前边的代码中使用
System.exit()
退出应用。 - 程序所在的线程死亡或者cpu关闭
- 如果在finally代码块中的操作有产生异常,则该finally代码块不能完全执行结束,同时异常会覆盖前边抛出的异常。