Java异常处理机制
异常处理机制的意义
开发人员总希望自己的程序能够正常地一步一步执行下去不出错误,但是没有人敢保证自己写的程序百分百完美。不管是自己写的程序还是调用别人的程序,执行过程中可能会出现意想不到的错误。如果不考虑异常的情况,程序一旦出现异常就无法继续执行下去了。下面举个例子:
public class Test { public static void main(String[] args) { System.out.println("1. 除法运算开始了"); System.out.println("2. 运算结果:" + 2 / 0); // 除数为0,此时程序肯定会出错 System.out.println("3. 除法运算结束啦"); } }
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero at cn.chengh.demo.Test.main(Test.java:6) 1. 除法运算开始了
程序执行到计算的那一步的时候发生异常,然后整个程序就终止运行了。程序以这种粗暴的方式结束好像不太体面,有时候我们想要即使这个程序中途出现了异常情况,程序也可以继续向下执行,最后整个程序以正常的情况结束。
好在Java提供了非常健全的异常处理机制,供开发人员处理程序可能出现的各种异常情况。
Java中有这些关键字和异常处理有关:try、catch、finally、throw、throws。
程序中处理异常的格式如下:
try { // try块中存放有可能出现异常的代码 } catch (异常类1 实例化对象) { // 处理该异常 } catch (异常类n 实例化对象) { // 处理该异常 } finally { // 不管是否出现异常,finally块中代码一定会执行 }
可以使用多个catch块来分别处理各种不同的异常
finally块非必须,可以有也可以没有
利用异常处理机制,上面的程序可以优化成这个样子:
public class Test { public static void main(String[] args) { System.out.println("1. 除法运算开始了"); try { System.out.println("2. 运算结果:" + 2 / 0); // 除数为0,此时程序肯定会出错 } catch (ArithmeticException e) { System.out.println("2. 啊,运算出现异常!"); } System.out.println("3. 除法运算结束啦"); } }
执行结果:
1. 除法运算开始了 2. 啊,运算出现异常! 3. 除法运算结束啦
哇,现在即使程序出现了异常,程序也能够完整的执行完。这就是程序需要异常处理机制的原因。
异常的种类
在Java中,Throwable类是所有异常类的父类,所有的异常类都继承自Throwable。异常分为两大类,Error类和Exception类。Error类表示在JVM中发生的错误,这类错误是无法在程序中处理的,比如OutOfMemoryError。另一大类是Exception,这类异常是开发人员在编写程序时可以处理的。Exception类下又分为两种类型的异常,RuntimeException类,称为运行时异常。其他的都称为非运行时异常。
非运行时异常要求开发人员必须要使用try catch进行处理,否则程序编译无法通过。而运行时异常不强制要求开发人员使用 try catch进行处理,如果没有使用try catch处理且在运行时发生了异常,异常会交给JVM进行默认的异常处理。举个运行时异常的例子
class Car { public void run() {} } public class Test { public static void main(String[] args) { Car car = null; car.run(); } }
该程序运行会报NullPointerException异常,但是在程序中没有使用try catch处理也是可以编译通过,因为NullPointerException是RuntimeException类型的异常。
异常处理流程
1、程序执行过程中产生异常,JVM首先会实例化出一个该异常类的对象。
2、产生了异常类对象后,判断发生异常的代码是否在try块中。如果不在try块中,则由JVM进行默认的异常处理(输出异常信息,结束程序的运行);如果在try块中,则该异常类对象会和后面的每一个catch语句的异常类型进行匹配。
3、异常类对象从上到下依次匹配catch中的异常类型。如果匹配成功,则进入该catch块执行异常处理程序。
4、不管是否匹配成功,如果后面有finally块,都会执行finally块中的程序。执行完了finally块的程序后,会根据之前异常匹配的结果决定程序接下来的执行走向。如果之前有匹配的catch块,则会继续执行finally块后面的程序,一切看起来都很正常;如果之前没有匹配的catch块,则执行完finally块程序后异常交给JVM进行默认处理(输出异常信息,结束程序的运行)。
关键字解释
throws关键字
throws关键字用于方法上,表示该方法中程序出现异常,则将该异常对象抛给执行该方法处去处理。
class MyUtil { // 方法中使用throws表示该方法出现异常会抛给调用此方法者处理 public static int div(int a, int b) throws Exception { return a / b; } } public class Test { public static void main(String[] args) { // 这里调用了div方法,所以要在这里处理div可能抛出的异常 try { System.out.println(MyUtil.div(10 ,0)); } catch (Exception e) { e.printStackTrace(); } } }
throw关键字
throw用来手工抛出一个异常类实例化对象
public class Test { public static void main(String[] args) { try { throw new Exception("自定义异常!"); } catch (Exception e) { e.printStackTrace(); } } }
自定义异常
Java本身提供的异常类有限,在实际开发项目中是不够的。有时候需要根据实际的需求定制一些异常类,Java支持开发人员自定义异常类。自定义的异常类需要继承Exception或RuntimeException类。
举个例子
class NegativeException extends Exception { public NegativeException(String msg) { super(msg); } } public class Test { public static void main(String[] args) { int result = 1 - 2; try { if (result < 0) { throw new NegativeException("数值为负!"); } } catch (Exception e) { e.printStackTrace(); } } }