异常的高级功能

此示例介绍了一些高级功能和异常用例。

以编程方式检查 callstack

Version >= Java SE 1.4

异常堆栈跟踪的主要用途是提供有关应用程序错误及其上下文的信息,以便程序员可以诊断并修复问题。有时它可以用于其他事情。例如,SecurityManager 类可能需要检查调用堆栈以确定是否应该信任正在进行调用的代码。

你可以使用异常以编程方式检查调用堆栈,如下所示:

    Exception ex = new Exception();   // this captures the call stack
    StackTraceElement[] frames = ex.getStackTrace();
    System.out.println("This method is " + frames[0].getMethodName());
    System.out.println("Called from method " + frames[1].getMethodName());

这有一些重要的警告:

  1. StackTraceElement 中提供的信息有限。没有比 printStackTrace 显示的更多信息。 (框架中局部变量的值不可用。)

  2. getStackTrace() 的 javadoc 声明允许 JVM 遗漏帧:

    在某些情况下,某些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧。在极端情况下,允许没有关于此 throwable 的堆栈跟踪信息的虚拟机从此方法返回零长度数组。

优化异常构造

如其他地方所述,构造异常是相当昂贵的,因为它需要捕获和记录有关当前线程上所有堆栈帧的信息。有时,我们知道该信息永远不会用于给定的异常; 例如堆栈跟踪永远不会被打印出来。在这种情况下,我们可以在自定义异常中使用实现技巧来导致无法捕获信息。

Throwable 构造函数调用 Throwable.fillInStackTrace() 方法时,捕获 stacktraces 所需的堆栈帧信息。这个方法是 public,这意味着子类可以覆盖它。诀窍是覆盖从 Throwable 继承的方法,什么都不做; 例如

  public class MyException extends Exception {
      // constructors

      @Override 
      public void fillInStackTrace() {
          // do nothing
      }
  }

这种方法的问题在于,覆盖 fillInStackTrace() 的异常永远不能捕获堆栈跟踪,并且在你需要堆栈跟踪的情况下无用。

擦除或替换堆栈跟踪

Version >= Java SE 1.4

在某些情况下,以正常方式创建的异常的堆栈跟踪包含不正确的信息或开发人员不希望向用户显示的信息。对于这些场景,Throwable.setStackTrace 可用于替换保存信息的 StackTraceElement 对象数组。

例如,以下内容可用于丢弃异常的堆栈信息:

 exception.setStackTrace(new StackTraceElement[0]);

抑制异常

Version >= Java SE 7

Java 7 引入了 try-with-resources 构造,以及异常抑制的相关概念。请考虑以下代码段:

try (Writer w = new BufferedWriter(new FileWriter(someFilename))) {
    // do stuff
    int temp = 0 / 0;    // throws an ArithmeticException
}

当抛出异常时,try 会在 w 上调用 close(),它会刷新任何缓冲的输出,然后关闭 FileWriter。但是如果在冲洗输出时抛出 IOException 会发生什么?

会发生什么是清除资源时抛出的任何异常都会被抑制。捕获异常,并将其添加到主要异常的抑制异常列表中。接下来, try-with-resources 将继续清理其他资源。最后,将重新引入主要异常。

如果在资源初始化期间抛出异常,或者 try 块正常完成,则会出现类似的模式。抛出的第一个异常成为主要异常,并且抑制了由清理引起的后续异常。

可以通过调用 getSuppressedExceptions 从主要异常对象中检索抑制的异常。