1.1 引言
要点:异常是运行时错误。异常处理使得程序可以处理运行时错误,并且继续通常的执行。
在程序运行过程中,如果JVM检测出一个不可能执行的操作,就会出现运行时错误(例如,使用一个越界的下标访问数组等)。在Java中,运行时错误会作为异常抛出。异常就是一种现象,表示阻止正常运行的错误或情况。如果异常没有被处理,那么程序将会非正常终止。那么如何处理异常使得程序可以继续运行或者优雅的终止呢?
1.2 异常处理概述
要点:异常是从方法抛出的。方法的调用者可以捕获以及处理该异常。
第一种方法是使用逻辑,判断可能出现的错误(例如:除数为0),给出提示(提示语)以及接下来的操作(放入循环中重新输入或结束输入)
第二种就是我们的异常机制了。
方法如何通知它的调用者一个异常产生了呢?Java可以让一个方法抛出一个异常,该一场可以被调用者捕获和处理。如下例:
import java.util.Scanner;
public class QuotientWithException{
public static int quotient(int number1,int number2) {
if(number2==0) {
throw new ArithmeticException("Divisor cannot be zero");//异常类创建的异常对象并抛出,调用catch块
}
return number1/number2;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Enter two Integers: ");
int number1 = input.nextInt();
int number2 = input.nextInt();
try {
int result = quotient(number1,number2);//这里面可能要抛出异常
System.out.println(number1+" / "+number2+" is "+result);
}catch(ArithmeticException ex){
System.out.println("Divisor cannot be zero");//异常处理办法
}
System.out.println("OK~");
}
}
这样编译器就不会报错,而是当发生指定的动作抛出指定错误类型而后执行你指定的动作。
1.3 异常类型
要点:异常是对象,而对象都采用类来定义,异常的根类是java.lang.Throwable.可以通过继承Exception或者其子类来创建
这些异常类可以分为三种主要类型:系统错误、异常和运行时异常。
(1)系统错误
系统错误(System Error)是由Java虚拟机抛出的,用Error类(内部系统错误)表示,这样的错误很少发生,如果发生,除了通知用户以及尽量稳妥地终止程序外,几乎什么也不能做。
(2)异常(Exception)
异常是用Exception类表示的,它描述的是由你的程序和外部环境所引起的错误,这些错误能被程序捕获和处理。
(3)运行时异常(Runtime Exception)是用RunTimeException类表示的,它描述的是程序设计错误,例如错误的类型转换等。
Runtime Exception、Error以及它们的子类都称为免检异常。所有其他异常都称为必检异常,编译器强制程序员检查并通过try—catch块处理它们。
2.1 关于异常处理的更多讨论
要点:异常的处理器是通过当前的方法开始,沿着方法调用链,按照异常的反向传播方向找到的。
Java的异常处理模型基于三种操作:声明一个异常、抛出一个异常和捕获一个异常。
2.1.1 声明异常
每个方法都必须声明它可能抛出的必检异常的类型。这称为声明异常。除了Error和RuntimeException,其他异常都必须在方法头中显式声明。这样方法的调用者才会被告知有异常。
在方法中声明一个异常,就要在方法头中使用关键字throws:
public void myMethod() throws IOException,Exception2,...n
关键字throws表明mvMethod,可能会抛出它后面的那些异常列表里的异常
注意:如果父类中的方法没有声明异常,那么就不能在子类中对其重写时声明异常
2.1.2 抛出异常
检测到错误的程序可以创建一个合适的异常类型的实例并抛出它,这称为抛出一个异常。
IllegalArgumentException ex = new IllegalArgumentException ("Wrong Argument");//异常描述信息可选
throw ex;
或者
throw new IlleagalArgumentException("Wrong Argument");
注意:声明异常的关键字是throws,抛出异常的关键字是throw
2.1.3 捕获异常
当抛出一个异常时,可以在try—catch块中捕获或处理它。如果try块中的某条语句抛出一个异常,Java就会跳过try中剩余的语句,然后开始查找处理这个异常的代码。处理这个异常的代码称为异常处理器,可以从当前方法开始,沿着方法调用链,按照异常的反向传播方向找到这个处理器。从第一个到最后一个逐个检查catch块,判断在catch块中的异常类实例是否是该异常对象的类型,如果是,就将该异常对象赋值给所声明的变量。然后执行catch中的代码。如果没有发现异常和其处理器,Java就会退出这个方法,然后把异常传给这个方法的调用者。父类的异常类型也必须比子类的异常类型范围大。
2.1.4 从异常中获取信息
2.1.5 finally字句
要点:无论异常是否发生,finally字句总会被执行
有时候,无论异常是否发生,都希望执行某些代码,Java的finally字句可以用来实现这个目的。
语法结构:
try {
statements;
}catch(TheException ex){
handling ex;
}
finally {
finalStatements;
}
三种情况:(1)try中没有异常(2)try中有异常被捕获(3)try中有一场没有被捕获
上面三种情况finally块均执行。
2.1.6 何时使用异常
要点:当错误需要被方法的调用者处理的时候,方法应该抛出一个异常。
try块包括正常情况下执行的代码,catch块下是异常情况需要处理的代码,异常处理将错误处理代码从正常的编程任务中分离出来,但由于异常处理需要沿着方法调用链来传播异常,所以需要更多的时间和资源。应该创建一个异常对象并抛出,如果能在发生异常的方法中处理异常,那么就不需要抛出或使用异常。
一般来说,一个项目中多个类都会发生的共同异常应该考虑设计为一个异常类。对于简单错误,最好进行局部处理(if-else),无须抛出异常,当必须处理不可预料的错误状况时应使用try-catch,否则用逻辑测试去处理,不要轻易浪费系统资源。
2.1.7 重新抛出异常
要点:如果异常处理器不能处理一个异常,或者只是简单地希望它地调用者注意到该异常,Java允许该异常处理器重新抛出异常。
try{
statements;
}catch{TheException ex}{
perform operations before exits;
throws ex;
}
语句throw ex重新抛出异常给调用者,以便调用者的其他处理器处理异常ex的机会。
2.1.8 链式异常
要点:与一个异常一起抛出一个异常,构成链式异常。
public static void main(String args[]) {
try {
method1();
}
catch(Exception ex){
ex.printStackTrace();
}
}
public static void method1() throws Exception{
try {
method2();
}catch(Exception ex) {
throw new Exception("new Info from method2",ex);
}
}
public static void method2() throws Exception{
throw new Exception("new Info from method1");
}
method1方法把method2方法的异常包装成了崭新的异常,新异常被抛出,在main中被捕获,进行打印,先打印新异常,在打印meithod2中的原始异常。
2.1.9 创建自定义异常
要点:可以通过继承java,lang,Exception类来定义一个自定义异常类。
因为Java已经提供了相当多的异常类,那么尽量使用它们,如果遇到不能用预定义类来充分描述问题,那么就可以通过集成类来创建自己的异常类。
public class InvalidRadiusException rxtend Exception{
private double radius;
public InvalidRadiusException (double radius){
super("Invalid radius"+radius);
this.radius = radius;
}
}
其中是重写了父类的异常信息。