java异常 #
参考:http://www.cnblogs.com/skywang12345/p/3544168.html
基本概念
异常的概念
Java异常是Java提供的一种识别及响应错误的一致性机制(注意:编译出现的问题叫语法错误,不叫异常)。
Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。在有效使用异常的情况下,异常能清晰的回答what, where, why这3个问题:异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出。Java通过API中Throwable类的众多子类描述各种不同的异常。因而,Java异常都是对象,是Throwable子类的实例。
Java异常机制用到的几个关键字:try、catch、finally、throw、throws。
- try:用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
- catch:用于捕获异常。catch用来捕获try语句块中发生的异常。
- finally:finally语句块总是会被执行。它主要用于回收在try块里打开的物理资源(如数据库连接、网络连接和磁盘文件)。finally块执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,就不会跳回执行,直接停止。
- throw:用于抛出异常。
- throws:用在方法签名中,用于声明该方法可能抛出的异常。
Java异常类层次结构图
查看jdk8的文档,我们可以看到:
- Error的直接子类:AnnotationFormatError, FactoryConfigurationError, IOError, ServiceConfigurationError, ThreadDeath, VirtualMachineError ...
- Exception 的直接子类: DataFormatException, IllegalClassFormatException, InterruptedException, IOException, ReflectiveOperationException, RuntimeException, SQLException, TimeoutException...
- Exception的子类RuntimeException的子类: ArithmeticException(算数异常), BufferOverflowException(缓冲区溢出异常), ClassCastException(类型转换异常), IndexOutOfBoundsException(数组越界异常), NullPointerException(空指针异常)...
Error类
Error类描述了Java运行时系统的内部错误和资源耗尽错误。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM请求内存不够时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception类
运行时异常
Exception类是程序本身可以处理的异常。一般分为两个分支:一个分支派生于RuntimeException;另一个分支包含其他异常,即非运行时异常。
RuntimeException 类表示由程序错误导致的异常,比如若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。如果出现了运行时异常,那么一定是你的问题。这些异常是未检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有通过throws进行声明抛出,也会编译通过。
非运行时异常
程序本身没有问题,但由于像IO错误这类问题导致的异常属于其他异常,即非运行时异常,比如如IOException、SQLException等以及用户自定义的Exception异常。从程序语法角度讲必须进行处理,如果不处理,程序就不能编译通过。
已检查异常和未检查异常
通常,Java的异常(包括Exception和Error)分为已检查异常(checked exceptions)和未检查的异常(unchecked exceptions)。
未检查的异常:派生于Error类和RuntimeException 类的所有异常。
已检查的异常:所有其他的异常。编译器将核查是否为所有的已检查异常提供了异常处理器。也就是说:这种异常的特点是Java编译器会检查它,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么通过throws进行声明抛出,否则编译不会通过。
注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。
异常处理机制
一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error类),要么就应该避免(RuntimeException 类)。如果方法没有声明所有可能发生的已检查异常,编译器就会给出一个错误信息。当然,除了声明异常外,还可以捕获异常,这样会使异常不被抛到方法之外,也不需要throws规范。
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息,运行时系统负责寻找处置异常的代码并执行。从方法中抛出的任何异常都必须使用throws子句。
捕获异常:捕捉异常通过try-catch语句或者try-catch-finally语句实现。
对于运行时异常、错误或已检查异常,Java技术所要求的异常处理方式有所不同。
由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。
对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。
对于所有的已检查异常,Java规定:一个方法必须捕捉,或者声明抛出到方法之外。也就是说,当一个方法选择不捕捉已检查异常时,它必须声明抛出异常。能够捕捉异常的方法,需要提供相符类型的异常处理器。所捕捉的异常,可能是由于自身语句所引发并抛出的异常,也可能是由某个调用的方法或者Java运行时系统等抛出的异常。也就是说,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。
总体来说,Java规定:对于已检查异常必须捕捉、或者声明抛出,允许忽略不可查的RuntimeException和Error。
捕获异常
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
例子
例子1
public static void main(String[] args) {
try {
int i = 10/0;
System.out.println("i="+i);
} catch (ArithmeticException e) {
System.out.println("Caught Exception");
System.out.println("e.getMessage(): " + e.getMessage());
System.out.println("e.toString(): " + e.toString());
System.out.println("e.printStackTrace():");
e.printStackTrace();
} finally {
System.out.println("run finally");
}
}
输出:
结果说明:在try语句块中有除数为0的操作,该操作会抛出java.lang.ArithmeticException异常。通过catch,对该异常进行捕获。
观察结果我们发现,并没有执行System.out.println("i="+i)。这说明try语句块发生异常之后,try语句块中的剩余内容就不会再被执行了,并且最终执行了finally语句块。