捕捉并抛出异常:会发生什么 "under the hood"?

Catching and Throwing an Exception: What happens "under the hood"?

我不熟悉 error/exception 处理。 当异常为 caughtthrown 时发生了什么 "under the hood"?

即在 try-catch 块中捕获异常然后抛出它有什么意义?

例如:

  try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

异常用于处理程序流程中通常不会发生的事情。再次抛出异常的要点是更改异常类型,如您的示例所示。这样做,您可以创建自定义异常并将其放入另一个异常捕获块中。

编辑

这是一个例子:

假设您有一个与线程一起使用的方法。在你的方法中你有一个延迟:

Thread.sleep(3000);

但是如果你只写这个,你会得到一个错误,因为睡眠方法是这样声明的:

void java.lang.Thread.sleep(long millis) throws InterruptedException

所以你必须捕获异常:

try{
Thread.sleep(3000);
}catch(InterruptedException e){
//do stuff
}

现在,您不喜欢 InterruptedException 并且想要使用您自己的自定义异常来处理它。但是既然 sleep 方法只抛出那个特定的异常,你该怎么做呢?答案是这样的:

try{
Thread.sleep(3000);
}catch(InterruptedException e){
throw new CustomException();
}

此时,如果引发 InterruptedException,它将被捕获并引发 CustomException。

关于异常机制的内部工作原理:有大量关于此的文档。 我特别喜欢这篇文章: http://www.javaworld.com/article/2076868/learn-java/how-the-java-virtual-machine-handles-exceptions.html

超简短摘要:抛出异常时,jvm 在 table 中查找(异常的 init() 方法)继续执行的位置。

对于你问题的第二部分:

what is the point of catching an exception in a try-catch block, then throwing it?

我看到一些捕获异常并抛出另一个异常的原因:

  • 你可能想捕获一个未经检查的异常(因为你知道, "something bad might happen") 并抛出一个已检查的 - 所以调用者必须处理它。

  • 您想使用自定义异常,也许还有额外的 information/logic

  • 您正在实施错误外观,例如抛出异常和 在立面的尽头抓住他们。

您的示例捕获 StuffException 类型的内容,然后抛出 MyException。这样做是为了抽象掉原来的异常;可能是原始异常是调用它的东西不需要知道的实现细节(并且需要能够更改,以便调用者不应该依赖它),并且抛出异常替换它是已发布的 API 的一部分,调用者可以依赖它。 Hibernate就是这样,它捕获在调用JDBC函数过程中产生的SQLException,并将它们包装在Hibernate异常中,因此调用代码将它们识别为从Hibernate抛出,并且调用代码不直接依赖于JDBC 异常,但在 Hibernate 上。如果 JDBC 改变了它抛出的异常,那么一旦 Hibernate 适应它,Hibernate 的用户就不必改变。

这个例子的缺点是,当 e 超出范围时,原始 StuffException 的堆栈跟踪信息被丢弃,而 MyException 的堆栈跟踪从抛出 MyException 的位置开始,使得很难找到实际的来源问题。将旧异常分配给新异常作为其原因将保留原始堆栈跟踪信息:

try {
        //Stuff
    } catch(StuffException e) {
        MyException myException = new MyException();
        myException.initCause(e);
        throw myException;
    }
}

捕获然后抛出异常的一些可能用途:

  • 异常翻译
    • 用未检查的异常替换已检查的异常
    • 将特定于实现的异常与调用者可以更好地处理的一些通用异常进行转换
  • 向异常添加更多上下文/附加信息(例如,某些低级方法失败并且您想识别它失败的输入)
  • 执行附加操作(例如,在将错误发送给调用者之前记录错误)
  • 处理更复杂的异常层次结构

    • 例如如果你想捕获除 FileNotFoundExceptionIOException 的子类型)之外的所有 IOException,你可以这样做:

      try {
          ...
      }
      catch (FileNotFoundException e) {
          throw e;
      }
      catch (IOException e) {
          // handle exception
      }