Java中的异常传播
在方法代码的执行中,如果发生异常情况,则该方法的正常流程将中断。为了处理异常情况,创建并抛出异常对象。可以在抛出该异常的方法中处理该异常,也可以将其传递给调用方堆栈中的其他方法进行处理。这种遍历方法调用堆栈以查找可以处理引发的异常的异常处理程序的过程在Java中称为异常传播。
Java中的异常传播
为了到达代码中的某个方法,在两者之间调用了一些其他方法。此方法列表称为方法调用堆栈。
当当前方法中发生异常时,异常处理机制将在当前方法中查找异常处理程序,如果找不到,它将转到调用堆栈中的上一个方法(当前方法的调用者方法),依此类推可以处理引发的异常的异常处理程序。
如果没有为抛出的异常提供异常处理程序,则将调用默认异常处理程序来处理该异常。
异常传播流程
假设我们有一个调用堆栈,包含三个方法method1,method2和method3. 从method1调用method2,而method3从method2调用。
如果方法3中发生异常并且方法1中存在异常处理程序,则Java中的异常传播可以如下所示:
Java中具有已检查和未检查的异常的异常传播
如果是未经检查的异常,则不会使用try-catch块或者throws子句强制执行该异常,因此默认情况下会发生异常传播。
如果发生检查异常,则必须使用throws关键字声明方法中未处理的异常。这表明必须将异常传播到调用方方法。如果调用者方法希望进一步传播它,则它还可以使用throws关键字声明抛出的异常。
具有未经检查的异常的异常传播Java示例
让我们采用上述三种方法的相同方法层次。在method3中发生异常的地方,并在寻找合适的异常处理程序时传播到method1.
public class ExceptionPropagationDemo { public static void main(String[] args) { ExceptionPropagationDemo ep = new ExceptionPropagationDemo(); ep.method1(); } // This method forwards the exception void method3(){ System.out.println("In method3"); int[] numArr = {4,5,6}; int number = numArr[5]; } // This method forwards the exception void method2(){ System.out.println("In method2"); method3(); } // Exception is handled in this method void method1(){ try{ System.out.println("In method1"); method2(); } catch(Exception e){ System.out.println("Exception handled"); e.printStackTrace(); } } }
输出:
In method1 In method2 In method3 Exception handled java.lang.ArrayIndexOutOfBoundsException: 5 at com.theitroad.ExceptionPropagationDemo.method3(ExceptionPropagationDemo.java:14) at com.theitroad.ExceptionPropagationDemo.method2(ExceptionPropagationDemo.java:20) at com.theitroad.ExceptionPropagationDemo.method1(ExceptionPropagationDemo.java:27) at com.theitroad.ExceptionPropagationDemo.main(ExceptionPropagationDemo.java:7)
在上面的代码中,方法3中发生异常,因为试图获取长度为3的数组的索引5处的值。这将导致引发ArrayIndexOutOfBoundsException。由于method3不提供任何异常处理机制,因此将搜索下一个方法(方法2),因此也没有找到异常处理机制,因此异常会传播到要对其进行处理的method1. 我们可以在异常跟踪中看到相同的流程,其中异常起源于method3,并传播到method1.
具有检查异常的异常传播Java示例
如果检查异常,如果我们没有在方法中提供异常处理,则需要使用throws子句明确指定异常。
我们来看一个带有已检查异常的异常传播示例。在方法3中,这里有一个读取文件的代码,并且流也被关闭,这两个活动都可能导致检查异常。由于我们必须处理该异常,因此可以使用throws子句指定它。请注意,method2不提供异常处理,因此也使用throws子句指定了它。这样,异常会传播到方法1进行处理。
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class ExceptionPropagationDemo { public static void main(String[] args) { ExceptionPropagationDemo ep = new ExceptionPropagationDemo(); ep.method1(); } // This method forwards the exception void method3() throws IOException{ System.out.println("In method3"); BufferedReader br = null; try{ br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:\test1.txt")))); }finally{ if(br != null) br.close(); } } // This method forwards the exception void method2() throws IOException{ System.out.println("In method2"); method3(); } // Exception is handled in this method void method1(){ try{ System.out.println("In method1"); method2(); } catch(IOException e){ System.out.println("Exception handled"); e.printStackTrace(); } } }
输出:
In method1 In method2 In method3 Exception handled java.io.FileNotFoundException: D:\test1.txt (The system cannot find the file specified) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(Unknown Source) at java.io.FileInputStream.(Unknown Source) at com.theitroad.ExceptionPropagationDemo.method3(ExceptionPropagationDemo.java:22) at com.theitroad.ExceptionPropagationDemo.method2(ExceptionPropagationDemo.java:34) at com.theitroad.ExceptionPropagationDemo.method1(ExceptionPropagationDemo.java:41) at com.theitroad.ExceptionPropagationDemo.main(ExceptionPropagationDemo.java:13)