try with resource 语句允许指定即使在例外情况下也应执行的操作,语法简洁(与嵌套的finally 块相比)。它还有另一个优点:当多个操作失败时,异常不会相互影响,而是在主 throwable 上注册为 suppressed throwables。一个缺点是资源的关闭顺序与它们创建时的顺序相反,因此我们必须颠倒这些行的顺序:
public static void main(String[] args) {
for(int test = 0; test < 16; test++) {
int currentTest = test;
System.out.println("Test "+currentTest);
try(AutoCloseable c1 = () -> line4(currentTest); // 4
AutoCloseable c2 = () -> line3(currentTest); // 3
AutoCloseable c3 = () -> line2(currentTest)) { // 2
line1(currentTest); // 1
}
catch(Exception ex) {
System.out.print("got exception ");
ex.printStackTrace(System.out);
}
System.out.println();
}
}
public static void line1(int whichTest) {
if((whichTest & 1) != 0) {
System.out.println("letting line 1 fail");
throw new RuntimeException("line 1 failed");
} else System.out.println("line1 executed");
}
public static void line2(int whichTest) {
if((whichTest & 2) != 0) {
System.out.println("letting line 2 fail");
throw new RuntimeException("line 2 failed");
} else System.out.println("line2 executed");
}
public static void line3(int whichTest) {
if((whichTest & 4) != 0) {
System.out.println("letting line 3 fail");
throw new RuntimeException("line 3 failed");
} else System.out.println("line3 executed");
}
public static void line4(int whichTest) {
if((whichTest & 8) != 0) {
System.out.println("letting line 4 fail");
throw new RuntimeException("line 4 failed");
} else System.out.println("line4 executed");
}
这个示例程序贯穿所有可能的场景。我缩短了输出以仅显示一些示例:
Test 0
line1 executed
line2 executed
line3 executed
line4 executed
Test 1
letting line 1 fail
line2 executed
line3 executed
line4 executed
got exception java.lang.RuntimeException: line 1 failed
at SafeActions.line1(SafeActions.java:23)
at SafeActions.main(SafeActions.java:10)
Test 9
letting line 1 fail
line2 executed
line3 executed
letting line 4 fail
got exception java.lang.RuntimeException: line 1 failed
at SafeActions.line1(SafeActions.java:23)
at SafeActions.main(SafeActions.java:10)
Suppressed: java.lang.RuntimeException: line 4 failed
at SafeActions.line4(SafeActions.java:41)
at SafeActions.lambda$main$0(SafeActions.java:7)
at SafeActions.main(SafeActions.java:7)
Test 15
letting line 1 fail
letting line 2 fail
letting line 3 fail
letting line 4 fail
got exception java.lang.RuntimeException: line 1 failed
at SafeActions.line1(SafeActions.java:23)
at SafeActions.main(SafeActions.java:10)
Suppressed: java.lang.RuntimeException: line 2 failed
at SafeActions.line2(SafeActions.java:29)
at SafeActions.lambda$main$2(SafeActions.java:9)
at SafeActions.main(SafeActions.java:7)
Suppressed: java.lang.RuntimeException: line 3 failed
at SafeActions.line3(SafeActions.java:35)
at SafeActions.lambda$main$1(SafeActions.java:8)
at SafeActions.main(SafeActions.java:7)
Suppressed: java.lang.RuntimeException: line 4 failed
at SafeActions.line4(SafeActions.java:41)
at SafeActions.lambda$main$0(SafeActions.java:7)
at SafeActions.main(SafeActions.java:7)
直接使用AutoCloseable 接口的一个缺点是它声明可能会抛出Exception,因此迫使我们捕获Exception。如果动作没有抛出检查异常或非常特定的类型,创建自己的扩展AutoCloseable 的函数接口很有用(如果是IOException,已经有java.io.Closeable)。
interface MyAction extends AutoCloseable {
@Override public void close();
}
public static void main(String[] args) {
int currentTest = 11;
try(MyAction c1 = () -> line4(currentTest);
MyAction c2 = () -> line3(currentTest);
MyAction c3 = () -> line2(currentTest)) {
line1(currentTest);
}
}
由于这个示例没有捕获异常,我还删除了在第二次迭代之后不会执行的循环。
letting line 1 fail
letting line 2 fail
line3 executed
letting line 4 fail
Exception in thread "main" java.lang.RuntimeException: line 1 failed
at SafeActions.line1(SafeActions.java:17)
at SafeActions.main(SafeActions.java:11)
Suppressed: java.lang.RuntimeException: line 2 failed
at SafeActions.line2(SafeActions.java:23)
at SafeActions.lambda$main$2(SafeActions.java:10)
at SafeActions.main(SafeActions.java:8)
Suppressed: java.lang.RuntimeException: line 4 failed
at SafeActions.line4(SafeActions.java:35)
at SafeActions.lambda$main$0(SafeActions.java:8)
at SafeActions.main(SafeActions.java:8)