扩展的 try-with-resources 语句到底捕获了什么?
What exactly gets caught in an extended try-with-resources statement?
在下面的代码块中:
try ( /* resources declaration */ ) {
// some dangerous code
} catch (Exception e) {
// error handling and reporting
}
如果 try
块中的代码和自动 close()
语句都抛出异常,会发生什么情况?哪一个会被困在 catch
块中?两个都?只有其中之一?如果有,是哪一个?
此外,如果try
成功而close
不成功怎么办?会进入 catch 块吗?
引自 JLS 部分 14.20.3.1:
In a basic try-with-resources statement that manages a single resource:
- If the initialization of the resource completes abruptly because of a
throw
of a value V
, then the try-with-resources statement completes abruptly because of a throw
of the value V
.
If the initialization of the resource completes normally, and the try
block completes abruptly because of a throw
of a value V
, then:
If the automatic closing of the resource completes normally, then the try-with-resources statement completes abruptly because of a throw
of the value V
.
If the automatic closing of the resource completes abruptly because of a throw
of a value V2
, then the try-with-resources statement completes abruptly because of a throw
of value V
with V2
added to the suppressed exception list of V
.
- If the initialization of the resource completes normally, and the
try
block completes normally, and the automatic closing of the resource completes abruptly because of a throw of a value V
, then the try-with-resources statement completes abruptly because of a throw
of the value V
.
这意味着如果 try
块内的代码和自动 close()
语句都抛出异常,catch
部分将处理 [=16] 抛出的异常=] 块,suppressed exceptions.
中的 close()
抛出异常
此外,这意味着如果 try
块成功但自动 close()
失败, catch
仍将执行并且捕获的异常将是抛出的异常close()
.
这是验证此行为的测试:
public class Main {
public static void main(String[] args) throws Exception {
// try block fails and close() fails
try (T t = new T()) {
throw new Exception("thrown by try part");
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getSuppressed()[0].getMessage());
}
// try block is successful but close() fails
try (T t = new T()) {
//
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class T implements AutoCloseable {
@Override
public void close() throws Exception {
throw new Exception("thrown by close");
}
}
此代码将打印
thrown by try part
thrown by close
thrown by close
表示捕获到的异常是第一部分代码的try部分抛出的异常。第二部分,捕获到的异常确实是close()
.
抛出的异常
try块内部抛出的异常对外抛出。
虽然使用 try-catch-finally,但从 finally 块抛出的异常将向上传播到调用堆栈。
InputStream input = null;
try {
input = new FileInputStream("file.txt");
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
} finally {
if(input != null){
input.close();
}
}
在这种情况下,如果try块和finally块都发生异常(当InputStream关闭时),最后一个被抛出,即使try块抛出的异常可能更多与传播有关。
try(FileInputStream input = new FileInputStream("file.txt")) {
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
虽然在这种情况下,如果在 try 块和 finally 块中都发生异常,则传播第一个异常
在下面的代码块中:
try ( /* resources declaration */ ) {
// some dangerous code
} catch (Exception e) {
// error handling and reporting
}
如果 try
块中的代码和自动 close()
语句都抛出异常,会发生什么情况?哪一个会被困在 catch
块中?两个都?只有其中之一?如果有,是哪一个?
此外,如果try
成功而close
不成功怎么办?会进入 catch 块吗?
引自 JLS 部分 14.20.3.1:
In a basic try-with-resources statement that manages a single resource:
- If the initialization of the resource completes abruptly because of a
throw
of a valueV
, then the try-with-resources statement completes abruptly because of athrow
of the valueV
.If the initialization of the resource completes normally, and the
try
block completes abruptly because of athrow
of a valueV
, then:
If the automatic closing of the resource completes normally, then the try-with-resources statement completes abruptly because of a
throw
of the valueV
.If the automatic closing of the resource completes abruptly because of a
throw
of a valueV2
, then the try-with-resources statement completes abruptly because of athrow
of valueV
withV2
added to the suppressed exception list ofV
.- If the initialization of the resource completes normally, and the
try
block completes normally, and the automatic closing of the resource completes abruptly because of a throw of a valueV
, then the try-with-resources statement completes abruptly because of athrow
of the valueV
.
这意味着如果 try
块内的代码和自动 close()
语句都抛出异常,catch
部分将处理 [=16] 抛出的异常=] 块,suppressed exceptions.
close()
抛出异常
此外,这意味着如果 try
块成功但自动 close()
失败, catch
仍将执行并且捕获的异常将是抛出的异常close()
.
这是验证此行为的测试:
public class Main {
public static void main(String[] args) throws Exception {
// try block fails and close() fails
try (T t = new T()) {
throw new Exception("thrown by try part");
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getSuppressed()[0].getMessage());
}
// try block is successful but close() fails
try (T t = new T()) {
//
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class T implements AutoCloseable {
@Override
public void close() throws Exception {
throw new Exception("thrown by close");
}
}
此代码将打印
thrown by try part
thrown by close
thrown by close
表示捕获到的异常是第一部分代码的try部分抛出的异常。第二部分,捕获到的异常确实是close()
.
try块内部抛出的异常对外抛出。
虽然使用 try-catch-finally,但从 finally 块抛出的异常将向上传播到调用堆栈。
InputStream input = null;
try {
input = new FileInputStream("file.txt");
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
} finally {
if(input != null){
input.close();
}
}
在这种情况下,如果try块和finally块都发生异常(当InputStream关闭时),最后一个被抛出,即使try块抛出的异常可能更多与传播有关。
try(FileInputStream input = new FileInputStream("file.txt")) {
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
虽然在这种情况下,如果在 try 块和 finally 块中都发生异常,则传播第一个异常