I\'m trying to understand how the new try-with-resources statement works by recreating it using regular try-catch-finally statements. Given the following test class using Ja
According to section 14.20.3.1 of the Java Language Specification (JLS) for Java 7:
try (VariableModifiersopt R Identifier = Expression ...)
Block
is equivalent to
{
final VariableModifiers_minus_final R Identifier = Expression;
Throwable #primaryExc = null;
try ResourceSpecification_tail
Block
catch (Throwable #t) {
#primaryExc = #t;
throw #t;
} finally {
if (Identifier != null) {
if (#primaryExc != null) {
try {
Identifier.close();
} catch (Throwable #suppressedExc) {
#primaryExc.addSuppressed(#suppressedExc);
}
} else {
Identifier.close();
}
}
}
}
The JLS goes into a lot more detail ... too much to reproduce here.
The Java 7 JLS chapter on try-with-resources is available in HTML form here, and you can get the PDF version of the JLS here.
For the TryWithResources
class, the following class produces equivalent bytecode as the try-with-resources:
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
public class TryCatchFinally {
public static void main(String[] args) {
try {
final GZIPOutputStream gzip = new GZIPOutputStream(System.out);
Throwable gzipEx = null;
try {
gzip.write("TEST".getBytes("UTF-8"));
} catch (Throwable t) {
gzipEx = t;
throw t;
} finally {
if (gzip != null) {
if (gzipEx != null) {
try {
gzip.close();
} catch (Throwable t) {
gzipEx.addSuppressed(t);
}
} else {
gzip.close();
}
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Using Sun JDK 1.7.0, the bytecode and exception tables for the main method in both TryWithResources
and TryCatchFinally
classes is:
stack=3, locals=6, args_size=1
0: new #2 // class java/util/zip/GZIPOutputStream
3: dup
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: invokespecial #4 // Method java/util/zip/GZIPOutputStream."<init>":(Ljava/io/OutputStream;)V
10: astore_1
11: aconst_null
12: astore_2
13: aload_1
14: ldc #5 // String TEST
16: ldc #6 // String UTF-8
18: invokevirtual #7 // Method java/lang/String.getBytes:(Ljava/lang/String;)[B
21: invokevirtual #8 // Method java/util/zip/GZIPOutputStream.write:([B)V
24: aload_1
25: ifnull 95
28: aload_2
29: ifnull 48
32: aload_1
33: invokevirtual #9 // Method java/util/zip/GZIPOutputStream.close:()V
36: goto 95
39: astore_3
40: aload_2
41: aload_3
42: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
45: goto 95
48: aload_1
49: invokevirtual #9 // Method java/util/zip/GZIPOutputStream.close:()V
52: goto 95
55: astore_3
56: aload_3
57: astore_2
58: aload_3
59: athrow
60: astore 4
62: aload_1
63: ifnull 92
66: aload_2
67: ifnull 88
70: aload_1
71: invokevirtual #9 // Method java/util/zip/GZIPOutputStream.close:()V
74: goto 92
77: astore 5
79: aload_2
80: aload 5
82: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
85: goto 92
88: aload_1
89: invokevirtual #9 // Method java/util/zip/GZIPOutputStream.close:()V
92: aload 4
94: athrow
95: goto 103
98: astore_1
99: aload_1
100: invokevirtual #13 // Method java/io/IOException.printStackTrace:()V
103: return
Exception table:
from to target type
32 36 39 Class java/lang/Throwable
13 24 55 Class java/lang/Throwable
13 24 60 any
70 74 77 Class java/lang/Throwable
55 62 60 any
0 95 98 Class java/io/IOException
For the TryWithResources2
class, the following class produces equivalent bytecode as the try-with-resources:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
public class TryCatchFinally2 {
public static void main(String[] args) {
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
Throwable baosEx = null;
try {
final GZIPOutputStream gzip = new GZIPOutputStream(baos);
Throwable gzipEx = null;
try {
gzip.write("TEST".getBytes("UTF-8"));
} catch (Throwable t) {
gzipEx = t;
throw t;
} finally {
if (gzip != null) {
if (gzipEx != null) {
try {
gzip.close();
} catch (Throwable t) {
gzipEx.addSuppressed(t);
}
} else {
gzip.close();
}
}
}
} catch (Throwable t) {
baosEx = t;
throw t;
} finally {
if (baos != null) {
if (baosEx != null) {
try {
baos.close();
} catch (Throwable t) {
baosEx.addSuppressed(t);
}
} else {
baos.close();
}
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
The bytecode and exception tables for the main method in both TryWithResources2
and TryCatchFinally2
classes is:
stack=3, locals=10, args_size=1
0: new #2 // class java/io/ByteArrayOutputStream
3: dup
4: invokespecial #3 // Method java/io/ByteArrayOutputStream."<init>":()V
7: astore_1
8: aconst_null
9: astore_2
10: new #4 // class java/util/zip/GZIPOutputStream
13: dup
14: aload_1
15: invokespecial #5 // Method java/util/zip/GZIPOutputStream."<init>":(Ljava/io/OutputStream;)V
18: astore_3
19: aconst_null
20: astore 4
22: aload_3
23: ldc #6 // String TEST
25: ldc #7 // String UTF-8
27: invokevirtual #8 // Method java/lang/String.getBytes:(Ljava/lang/String;)[B
30: invokevirtual #9 // Method java/util/zip/GZIPOutputStream.write:([B)V
33: aload_3
34: ifnull 114
37: aload 4
39: ifnull 61
42: aload_3
43: invokevirtual #10 // Method java/util/zip/GZIPOutputStream.close:()V
46: goto 114
49: astore 5
51: aload 4
53: aload 5
55: invokevirtual #12 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
58: goto 114
61: aload_3
62: invokevirtual #10 // Method java/util/zip/GZIPOutputStream.close:()V
65: goto 114
68: astore 5
70: aload 5
72: astore 4
74: aload 5
76: athrow
77: astore 6
79: aload_3
80: ifnull 111
83: aload 4
85: ifnull 107
88: aload_3
89: invokevirtual #10 // Method java/util/zip/GZIPOutputStream.close:()V
92: goto 111
95: astore 7
97: aload 4
99: aload 7
101: invokevirtual #12 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
104: goto 111
107: aload_3
108: invokevirtual #10 // Method java/util/zip/GZIPOutputStream.close:()V
111: aload 6
113: athrow
114: aload_1
115: ifnull 185
118: aload_2
119: ifnull 138
122: aload_1
123: invokevirtual #13 // Method java/io/ByteArrayOutputStream.close:()V
126: goto 185
129: astore_3
130: aload_2
131: aload_3
132: invokevirtual #12 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
135: goto 185
138: aload_1
139: invokevirtual #13 // Method java/io/ByteArrayOutputStream.close:()V
142: goto 185
145: astore_3
146: aload_3
147: astore_2
148: aload_3
149: athrow
150: astore 8
152: aload_1
153: ifnull 182
156: aload_2
157: ifnull 178
160: aload_1
161: invokevirtual #13 // Method java/io/ByteArrayOutputStream.close:()V
164: goto 182
167: astore 9
169: aload_2
170: aload 9
172: invokevirtual #12 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
175: goto 182
178: aload_1
179: invokevirtual #13 // Method java/io/ByteArrayOutputStream.close:()V
182: aload 8
184: athrow
185: goto 193
188: astore_1
189: aload_1
190: invokevirtual #15 // Method java/io/IOException.printStackTrace:()V
193: return
Exception table:
from to target type
42 46 49 Class java/lang/Throwable
22 33 68 Class java/lang/Throwable
22 33 77 any
88 92 95 Class java/lang/Throwable
68 79 77 any
122 126 129 Class java/lang/Throwable
10 114 145 Class java/lang/Throwable
10 114 150 any
160 164 167 Class java/lang/Throwable
145 152 150 any
0 185 188 Class java/io/IOException