首页 > 编程笔记

Java try catch用法详解

根据 Java 的异常机制,如果一个异常对象被交给 Java 虚拟机处理,那么 Java 虚拟机会终止提交该异常对象的线程,进而可能会导致 Java 程序异常退出。因此,在开发过程中,需要捕获产生的异常对象,并对异常进行处理。

在 Java 中,使用 try-catch 语句块可以捕获和处理异常,语法格式如下:
try {
    // 包裹可能会出现异常的代码片段
    // 异常机制会收集代码中出现的异常信息,并封装成异常对象
} catch (异常类型的声明1 引用捕捉到的异常对象) {
    // 处理异常
} catch (异常类型的声明2 引用捕捉到的异常对象) {
    // 处理异常
}
将可能会出现异常的代码片段放入 try{} 中,Java 虚拟机会检查 try{} 中的代码,如果有异常,那么 Java 虚拟机先将异常信息封装成相应的异常对象,再转移到 catch(){} 中进行处理,执行 catch(){} 中的语句。

catch 语句块在 try 语句块之后,表示捕获 try 语句块中出现的异常并对异常进行处理。被 catch 语句块捕获的异常不会再向当前方法的调用者传递,可以避免因异常对象被提交给 Java 虚拟机导致 Java 程序异常退出。

关键字 catch 后面的圆括号用于声明该语句块捕获的异常类型,所有该类型的异常对象和子类的异常对象,均会被该 catch 语句块捕获。允许在 catch 语句块中指定匹配的异常类型的设计,主要用于满足“不同类型的异常使用不同的处理逻辑”这一需求。根据该语法规则,一个 try 语句块后可能存在一个或多个 catch 语句块,每个 catch 语句块匹配不同类型的异常。

当 try 语句块中抛出一个异常对象时,该 try 语句块后的多个 catch 语句块按照从上到下的顺序对异常对象的类型进行匹配,匹配的规则是判断当前异常对象的类型是否是 catch 语句块中声明捕获的类型,或者是该类型的子类。如果一个 catch 语句块不匹配,那么继续向下匹配。如果找到匹配的 catch 语句块,那么执行该 catch 语句块中的处理逻辑,后续的 catch 语句块将不会进行匹配和执行。

如果所有的 catch 语句块均不匹配,那么异常对象会被抛给方法调用者,在其中查找是否有匹配的 catch 语句块。可以简单地理解为,在一次异常处理中,一个 try 语句块后面的多个 catch 语句块中最多只会执行一个 catch 语句块。

在开发多个连续的 catch 语句块时需要注意,如果前面的 catch 语句块声明捕获的异常类型过大,那么会导致后面的 catch 语句块没有被执行的机会。

例如,在第一个 catch 语句块中声明捕获 Exception 类型,在第二个 catch 语句块中声明捕获 RuntimeException 类型,此时所有的异常均会被第一个 catch 语句块捕获,第二个 catch 语句块没有被执行的机会。

因此,从上到下的 catch 语句块使用的异常类型可以是同级别的,当多个异常类型中有父子关系时,应该先写子类异常,再写父类异常。

finally语句块

finally 语句块在 try 语句块或 catch 语句块之后:
try {
    // 包裹可能会出现异常的代码片段
    // 异常机制会收集代码中出现的异常信息,并封装成异常对象
} catch (异常类型的声明1 引用捕捉到的异常对象) {
    // 处理异常
} catch (异常类型的声明2 引用捕捉到的异常对象) {
    // 处理异常
} finally{
    // 代码块
}
不论 try 语句块中是否出现异常,finally 语句块都会被执行,主要用于执行释放资源的逻辑。

根据 Java 的异常机制,如果某行代码出现异常,那么 Java 会自动触发异常机制,跳转到异常处理逻辑中,此时出现异常的那行代码后面的代码将不会被执行,也就是说,try 语句块并不能保证其中所有的代码都被执行。

在一些场景中,一段程序的最后需要有一些负责“收尾”的代码,如重置计数器、删除不再使用的数组或集合、关闭流等。假如将这些负责“收尾”的代码放在 try 语句块的末尾,当出现异常时,这些代码将不会被执行,从而影响程序的正常逻辑,或者导致资源得不到释放等。为了解决这样的问题,可以在 try 语句块和它后续的所有 catch 语句块中均添加负责“收尾”的代码。但是这种方案会导致负责“收尾”的代码重复出现多次,从而影响代码的可维护性。使用 Java 提供的 finally 语句块可以解决上述问题。

【实例】异常的捕获和处理。
package chapter6;
public class Test {
    public static void main(String args[]) {
        try {
            Class cs = Class.forName("chapter6.Circle");
            Circle circle = (Circle) cs.newInstance();
            circle.setRadius(100);
            System.out.println("circle的面积 " + circle.getArea());
            System.out.println("circle的周长 " + circle.getLength());
            Class cs2 = Class.forName("Rect");
            // 由于上一行代码出现异常,因此不会执行下面这行代码
            System.out.println("异常代码后的代码");
        } catch (Exception e) {
            System.out.println("不能加载 Rect类:" + e.getMessage());
        } finally {
            System.out.println("finally中的代码一定会被执行");
        }
    }
}

package chapter6;
public class Circle {
    private double radius, area, length;

    public double getArea() {
        area = Math.PI * radius * radius;
        return area;
    }

    public double getLength() {
        length = 2 * Math.PI * radius;
        return length;
    }

    public void setRadius(double r) {
        radius = r;
    }
}
运行结果为:

circle的面积 31415.926535897932
circle的周长 628.3185307179586
不能加载 Rect类:Rect
finally中的代码一定会被执行

推荐阅读

副业交流群 关注微信公众号,加入副业交流群,学习变现经验,交流各种打法。