Return 尝试使用资源。这是 JVM 的正确行为吗?

Return in try with resources. Is it correct behaviour of JVM?

在下面的代码示例中,我希望将 1 作为方法 testM() 的 return 值。但是由于 TestAutoCloseable.close() 方法中的异常,我得到了意外的行为。

我的问题是:"Is it a normal behaviour of JVM?"

public static void main(String[] args) {
    ProgrammerTwo programmerTwo = new ProgrammerTwo();
    System.out.println(programmerTwo.testM());
}

int testM() {
    try (TestAutoCloseable closable = new TestAutoCloseable()) {
        System.out.println("Do first return");
        return 1;
    } catch (IOException e) {
        System.out.println("handled");
    }
    System.out.println("Do something, that shouldn't do if first return have happened");
    return 2;
}

static class TestAutoCloseable implements AutoCloseable {

    @Override
    public void close() throws IOException {
        throw new IOException();
    }
}

因为如果这是正常行为,我们不应该在 try with resources 语句中使用 return 或 break 语句。应该是反模式。

try-with-resources 语句如何工作的详细信息在 JLS 的 this section 中。在您的情况下,它是一个 extended try-with-resources 因为它有一个 catch 子句,如以下引用中所定义(请注意末尾突出显示的语句)。

A try-with-resources statement with at least one catch clause and/or a finally clause is called an extended try-with-resources statement.

The meaning of an extended try-with-resources statement:

try ResourceSpecification
     Block
[Catches]
[Finally]

is given by the following translation to a basic try-with-resources statement nested inside a try-catch or try-finally or try-catch-finally statement:

try {
  try ResourceSpecification          <--- exception thrown in this basic try-with-resources
      Block
}
[Catches]
[Finally]

The effect of the translation is to put the resource specification "inside" the try statement. This allows a catch clause of an extended try-with-resources statement to catch an exception due to the automatic initialization or closing of any resource.

这意味着关闭资源发生在内部外部try块的主体内,导致在catch中抛出并处理异常块,控制恢复到扩展 try-with-resources 语句之后的语句。

实际上,整个方法 testM 等同于:

int testM() {
    try {
       final TestAutoCloseable closable = new TestAutoCloseable();
       Throwable #primaryExc = null;
       try {
           System.out.println("Do first return");
           return 1;
       } catch (Throwable #t) {
           #primaryExc = #t;
           throw #t;
       } finally {
           if (closable != null) {
              if (#primaryExc != null) {
                 try {
                    closable.close();
                 } catch (Throwable #suppressedExc) {
                    #primaryExc.addSuppressed(#suppressedExc);
                 }
              } else {
                 closable.close();
              }
          }
       }
    } catch (IOException e) {
       System.out.println("handled");
    }
    System.out.println("Do something, that shouldn't do if first return have happened");
    return 2;
}

简而言之,当您的 try-with-resource 块退出时,会抛出一个 IOException(因为您是从 close() 抛出它的,所以值 1 从未从该方法返回,而是 JVM 移动到 catch 块,因为根据规则,一旦发生任何异常,try 块中的所有剩余代码都不会被执行,而是 JVM 移动到catch block.so 现在你的 catch 块运行,然后你剩下的代码被执行