java.lang 
类 Throwable
java.lang.Object
  继承者 java.lang.Throwable
所有已实现的接口: 
java.io.Serializable 
直接已知子类: 
Error, Exception 


Throwable类是所有异常信息的超类(父类)。

直接子类:Error类,Exception类。

Error类:

是程序无法处理的错误,表示运行应用程序中较严重问题

就是程序运行时候抛出的最严重级别的错误了,如VirtualMachineError,ThreadDeath。抛出了Error的程序从Java设计的角度来讲,程序基本不可以通过后续代码修复,从而理应终止。当然,从语法上来讲,所有这些都可以被写进catch里面,但是Error因为上述原因,不应该被代码处理。


 

Exception类:

有三种类型:编译时异常CheckedException, 运行时异常RuntimeException, 自定义异常。


运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

      运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

 非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

自定义异常:sun提供了很多的异常类给我们用于描述程序中各种的不正常情况,但是sun给我们提供异常类还不足以描述我们现实生活中所有不正常情况,那么这时候我们就需要自定义异常类。

一般在开发中,自定义的异常都是 运行时异常。


异常处理机制:(重点)

在 Java 应用程序中,异常处理机制为:抛出异常,捕获异常。

抛出异常:

throws:当前方法不处理,而是声明抛出,由该方法的调用者来处理;

抛出异常有两种方式:

throws:运用于方法声明之上,定义于方法参数之后,表示当前方法不处理异常,而是提醒该方法的调用者来处理抛出的异常(一个或者多个异常);如:

private static int divide(int num1, int num2) throws Exception {}

throw:运用于方法内部,抛出一个具体的异常对象,中止方法的执行,其语法格式如下:

throw  new  异常类("异常信息");

补充:如果每一个方法都放弃处理异常都直接通过throws声明抛出,最后异常会抛到main方法,如果此时main方法还不处理,会继续抛出给JVMJVM底层的处理机制就是打印异常的跟踪栈信息(e.printStackTrace()方法); runtime异常,默认就是这种处理方式。  

捕获异常:

try-catch:在当前方法中使用try-catch的语句块来处理异常;

try{
            System.out.println("begin");
            System.out.println("10/0="+(10/0));
            System.out.println("try end");
        }catch (Exception e) {
            System.out.println(e.getMessage());//获取异常信息
            System.out.println(e);  //调用toString方法,打印异常类名和异常信息
            e.printStackTrace();  //jvm默认就用这种方式处理异常,异常栈追踪
        }

运行结果如下: 

begin
 / by zero
 java.lang.ArithmeticException: / by zero
 java.lang.ArithmeticException: / by zero
     at com.yjxxt.exception.Test3.main(Test3.java:7)

通过查看运行结果,不难发现,使用try-catch之后,程序遇到异常时不再中断执行,而是跳过异常代码及其之后的try代码块中的剩余代码语句,来到catch代码块中执行我们定义的异常处理代码;

在上述案例中是打印出了异常信息,异常对象信息,异常栈追踪;其中的3个方法都是Throwable类的方法:

  1. String getMessage():获取异常的详细描述信息;
  2. String toString():获取异常的类型、异常描述信息;
  3. void printStackTrace():打印异常的跟踪栈信息并输出到控制台,但不能在System.out.println()中使用该方法;其中包含了异常的类型、异常的原因、异常出现的位置;在开发和调试阶段,该方法都很有用,方便调试和修改;

捕获多个异常:

try{
            System.out.println("begin");
            int num=Integer.valueOf("abc");//异常1
            System.out.println(num);
            
            System.out.println("10/0="+(10/0));//异常2
            System.out.println("try end");
        }catch (ClassCastException e) {
            System.out.println(e.getMessage());//获取异常信息
            System.out.println(e);  //调用toString方法,打印异常类名和异常信息
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println(e.getMessage());//获取异常信息
            System.out.println(e);  //调用toString方法,打印异常类名和异常信息
            e.printStackTrace();  //jvm默认就用这种方式处理异常,异常栈追踪
        }finally {
            System.out.println("end");
        }

运行结果如下:

begin
For input string: "abc"
java.lang.NumberFormatException: For input string: "abc"
end
java.lang.NumberFormatException: For input string: "abc"

 注意:
        一个try后面可以接1~n个catch
        catch会从上到下判断执行
        try中的代码如果不出现异常,不会执行catch的判断
        如果try中一旦出现了异常,直接执行对应catch的判断,try中后面的代码不会执行(即使            后面语句有异常,也不执行)
        无论try中是否会出现异常,无论出现了异常执行了哪一个catch,最后都会执行finally中的          代码
        finally中一般定义为资源的关闭等

 


finally代码块

finally语句块表示无论如何(也包括发生异常时)都会最终执行的代码块,比如:当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),在使用完之后,都得最终关闭打开的资源。


接下来讲一个重要的知识点

JAVA中try、catch、finally带return的执行顺序总结

异常处理中,try、catch、finally的执行顺序,大家都知道是按顺序执行的。即,如果try中没有异常,则顺序为try→finally,如果try中有异常,则顺序为try→catch→finally。但是当try、catch、finally中加入return之后,就会有几种不同的情况出现,下面分别来说明一下。

一、try中带有return

private int testReturn1() {
        int i = 1;
        try {
            i++;
            System.out.println("try:" + i);
            return i;
        } catch (Exception e) {
            i++;
            System.out.println("catch:" + i);
        } finally {
            i++;
            System.out.println("finally:" + i);
        }
        return i;
    }

执行结果是:

try:2
finally:3
2

因为当try中带有return时,会先执行return前的代码,然后暂时保存return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息。所以,这里方法返回的值是try中计算后的2,而非finally中计算后的3。但有一点需要注意,再看另外一个例子: 

private List<Integer> testReturn2() {
        List<Integer> list = new ArrayList<>();
        try {
            list.add(1);
            System.out.println("try:" + list);
            return list;
        } catch (Exception e) {
            list.add(2);
            System.out.println("catch:" + list);
        } finally {
            list.add(3);
            System.out.println("finally:" + list);
        }
        return list;
    }

结果是:

try:[1]
finally:[1, 3]
[1, 3]

看完这个例子,可能会发现问题,刚提到return是会临时保存需要返回的信息,不受finally中的影响,为什么这里会有变化?其实问题出在参数类型上,上一个例子用的是基本类型,这里用的引用类型list里存的不是变量本身,而是变量的地址,所以当finally通过地址改变了变量,还是会影响方法返回值的。 

二、catch中带有return

private int testReturn3() {
        int i = 1;
        try {
            i++;
            System.out.println("try:" + i);
            int x = i / 0 ;
        } catch (Exception e) {
            i++;
            System.out.println("catch:" + i);
            return i;
        } finally {
            i++;
            System.out.println("finally:" + i);
        }
        return i;
    }

输出:

try:2
catch:3
finally:4

catch中return与try中一样,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息。所以,这里方法返回的值是try、catch中累积计算后的3,而非finally中计算后的4。

三、finally中带有return 

private int testReturn4() {
        int i = 1;
        try {
            i++;
            System.out.println("try:" + i);
            return i;
        } catch (Exception e) {
            i++;
            System.out.println("catch:" + i);
            return i;
        } finally {
            i++;
            System.out.println("finally:" + i);
            return i;
        }
    }

输出:

try:2
finally:3
3  

 当finally中有return的时候,try中的return会失效,在执行完finally的return之后,就不会再执行try中的return。这种写法,编译是可以编译通过的,但是编译器会给予警告,所以不推荐在finally中写return,这会破坏程序的完整性,而且一旦finally里出现异常,会导致catch中的异常被覆盖。

总结:

1、finally中的代码总会被执行。

2、当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。

3、finally中有return时,会直接在finally中退出,导致try、catch中的return失效。