这是试图回答我自己的问题,但它使用实验和 Java 编译器的结果,所以它并没有特别解决哲学或类似的问题。
以下是 catch-cleanup-and-rethrow 的一些示例代码:
public CompoundResource catchThrowable() throws Exception {
InputStream stream1 = null;
InputStream stream2 = null;
try {
stream1 = new FileInputStream("1");
stream2 = new FileInputStream("2");
return new CompoundResource(stream1, stream2);
} catch (Throwable t) {
if (stream2 != null) {
stream2.close();
}
if (stream1 != null) {
stream1.close();
}
throw t;
}
}
编译为以下字节码:
public Exceptions$CompoundResource catchThrowable() throws java.lang.Exception;
Code:
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: new #2 // class java/io/FileInputStream
7: dup
8: ldc #3 // String 1
10: invokespecial #4 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
13: astore_1
14: new #2 // class java/io/FileInputStream
17: dup
18: ldc #5 // String 2
20: invokespecial #4 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
23: astore_2
24: new #6 // class Exceptions$CompoundResource
27: dup
28: aload_0
29: aload_1
30: aload_2
31: invokespecial #7 // Method Exceptions$CompoundResource."<init>":(LExceptions;Ljava/io/Closeable;Ljava/io/Closeable;)V
34: areturn
35: astore_3
36: aload_2
37: ifnull 44
40: aload_2
41: invokevirtual #9 // Method java/io/InputStream.close:()V
44: aload_1
45: ifnull 52
48: aload_1
49: invokevirtual #9 // Method java/io/InputStream.close:()V
52: aload_3
53: athrow
Exception table:
from to target type
4 34 35 Class java/lang/Throwable
接下来是一些检查失败的代码,在最后和清理中具有相同的语义:
public CompoundResource finallyHack() throws Exception {
InputStream stream1 = null;
InputStream stream2 = null;
boolean success = false;
try {
stream1 = new FileInputStream("1");
stream2 = new FileInputStream("2");
success = true;
return new CompoundResource(stream1, stream2);
} finally {
if (!success) {
if (stream2 != null) {
stream2.close();
}
if (stream1 != null) {
stream1.close();
}
}
}
}
编译为以下内容:
public Exceptions$CompoundResource finallyHack() throws java.lang.Exception;
Code:
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: iconst_0
5: istore_3
6: new #2 // class java/io/FileInputStream
9: dup
10: ldc #3 // String 1
12: invokespecial #4 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
15: astore_1
16: new #2 // class java/io/FileInputStream
19: dup
20: ldc #5 // String 2
22: invokespecial #4 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
25: astore_2
26: iconst_1
27: istore_3
28: new #6 // class Exceptions$CompoundResource
31: dup
32: aload_0
33: aload_1
34: aload_2
35: invokespecial #7 // Method Exceptions$CompoundResource."<init>":(LExceptions;Ljava/io/Closeable;Ljava/io/Closeable;)V
38: astore 4
40: iload_3
41: ifne 60
44: aload_2
45: ifnull 52
48: aload_2
49: invokevirtual #9 // Method java/io/InputStream.close:()V
52: aload_1
53: ifnull 60
56: aload_1
57: invokevirtual #9 // Method java/io/InputStream.close:()V
60: aload 4
62: areturn
63: astore 5
65: iload_3
66: ifne 85
69: aload_2
70: ifnull 77
73: aload_2
74: invokevirtual #9 // Method java/io/InputStream.close:()V
77: aload_1
78: ifnull 85
81: aload_1
82: invokevirtual #9 // Method java/io/InputStream.close:()V
85: aload 5
87: athrow
Exception table:
from to target type
6 40 63 any
63 65 63 any
仔细看看这里发生了什么,它似乎生成了相同的字节码,就好像你在返回点和 catch 块内复制了整个 finally 块一样。换句话说,就好像你这样写:
public CompoundResource finallyHack() throws Exception {
InputStream stream1 = null;
InputStream stream2 = null;
boolean success = false;
try {
stream1 = new FileInputStream("1");
stream2 = new FileInputStream("2");
success = true;
CompoundResource result = new CompoundResource(stream1, stream2);
if (!success) {
if (stream2 != null) {
stream2.close();
}
if (stream1 != null) {
stream1.close();
}
}
return result;
} catch (any t) { // just invented this syntax, this won't compile
if (!success) {
if (stream2 != null) {
stream2.close();
}
if (stream1 != null) {
stream1.close();
}
}
throw t;
}
}
如果有人真的写了那个代码,你会嘲笑他们。在成功分支中,成功总是正确的,所以有一大块代码永远不会运行,所以你生成的字节码永远不会被执行,只会让你的类文件膨胀。在异常分支中,成功总是错误的,因此您在进行您知道必须发生的清理之前对值执行不必要的检查,这再次增加了类文件的大小。
最需要注意的是:
catch (Throwable)
和解决方案实际上都finally
捕获了所有异常。
因此,就回答这个问题而言,“可以捕获Throwable
执行清理吗?”...
我仍然不确定,但我知道如果抓住Throwable
它不合适,那么使用finally
它也不合适。如果finally
也不行,还剩下什么?