异常
什么是异常?
程序开发和运行过程中会出现编译错误和运行错误。编译错误容易被发现并排除,而运行错误(逻辑错误和异常)很难预料。因此为了程序的稳定性和可靠性,就需要对程序异常处理和保护。
异常( Exception ):正常程序流程所不能处理或者没有处理的异常情况或异常事件。
一、异常样例
public class DemoException {
public static void main(String[] args) {
String.out.println(5/0);
}
}
运行结果:
二、异常的结构
- Error是程序中无法处理的错误,表示运行应用程序中出现了严重的错误,表示代码运行时 JVM 出现问题。Error不能被程序员通过代码处理,Error很少出现,因此程序员应该关注Exception为父类的分支下的各种异常类。
- Exception程序本身可以捕获并且可以处理的异常。
- 非运行时异常:编译器会检查出此类异常,如果程序中出现此类异常,如IOException,必须对该异常进行处理,使用try-catch捕获,或者使用throws抛出,否则编译不能通过。
- 运行时异常:此类异常属于不可查异常,一般是有程序逻辑错误引起的,在程序中可以选择抛出捕获处理,也可以不处理。
- 常见异常:
- 输入输出异常:IOException
- 文件未找到异常:FileNotFoundException
- 算术异常:ArithmeticException
- 空指针异常:NullPointException
- 数组下标越界异常:ArrayIndexOutOfBoundsException
- 类型强制转换异常:ClassCastException
- 操作数据库异常:SQLException
三、异常处理机制
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。
一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。
3.1 抛出异常
-
throw抛出异常
throw总是出现在函数体中,用来抛出一个Throwable类型的异常。程序会在throw语句后立即终止,后面的语句执行不到。抛出的异常会等待捕获。
例子:
public class DemoException { public static void main(String[] args) { int a = 5; int b = 0; if (b == 0) { throw new ArithmeticException("除数不能为0"); } System.out.println(a/b); } }
运行结果:
throw抛出的异常,会在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
-
throws抛出异常
throws一般声明在方法上。如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常,交给方法的调用者进行处理,方法的调用者也可以选择继续用throws抛出。
throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。
例子:
public class DemoException { public static void main(String[] args) throws FileNotFoundException { fileUpload(); } public static void fileUpload() throws FileNotFoundException { FileInputStream fileInputStream = new FileInputStream("a.txt"); } }
运行结果:
3.2 捕获异常
-
try-catch捕获异常
在Java中,异常通过try-catch语句捕获。
try { // 可能会发生异常的程序代码 // 不发生异常,则忽略catch,继续执行 // 发生异常,则尝试匹配catch } catch (Type1 id1){ // 捕获并处置try抛出的异常类型Type1 // 可通过printStackTrace()抛出具体异常 // 也可自定义异常处理信息 } catch (Type2 id2){ //捕获并处置try抛出的异常类型Type2 } // 一个try至少带一个catch
例子:
public class DemoException { public static void main(String[] args) { try { System.out.println(5/0); }catch (Exception e) { //e.printStackTrace(); System.out.println("除数不能为0"); } } }
运行结果:
-
finally关键字
finally块中的代码,无论有无异常最终都会执行。
finally常用于清理或者关闭资源工作,如数据库连接的关闭、lock锁的unlock等。
try { // 可能会发生异常的程序代码 } catch (Type1 id1) { // 捕获并处理try抛出的异常类型Type1 } catch (Type2 id2) { // 捕获并处理try抛出的异常类型Type2 } finally { // 无论是否发生异常,都将执行的语句块 }
例子:
public class DemoException { public static void main(String[] args) { try { System.out.println(5/0); //System.out.println(5/1); }catch (Exception e) { System.out.println("除数不能为0"); }finally { System.out.println("最终都会执行"); } } }
四、自定义异常
jdk本身提供了足够多的异常,一般不需要自定义异常,仅作了解。
jdk提供的异常类编写步骤:
自定义异常类:
public class MyException extends Exception {
private int number;
public MyException(int number) {
this.number = number;
}
@Override
public String toString() {
return "MyException: (除数不能为0) number = " + number;
}
}
测试异常类:
public class Test {
public static void main(String[] args) {
try {
division(5,0);
} catch (MyException e) {
e.printStackTrace();
}
}
public static int division(int a, int b) throws MyException {
int result = 0;
if (b == 0) {
throw new MyException(b);
}else {
result = a / b;
}
return result;
}
}
运行结果: