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 Java 7 try-with-resources:
import java.io.IOException; import java.util.zip.GZIPOutputStream; public class TryWithResources { public static void main(String[] args) { try (GZIPOutputStream gzip = new GZIPOutputStream(System.out)) { gzip.write("TEST".getBytes("UTF-8")); } catch (IOException ioe) { ioe.printStackTrace(); } } }
How would you rewrite this class to use try-catch-finally statements which produces exactly the same bytecode as the try-with-resources statement produces? Also, same question when two resources are used, as in the following example:
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; public class TryWithResources2 { public static void main(String[] args) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(baos)) { gzip.write("TEST".getBytes("UTF-8")); } catch (IOException ioe) { ioe.printStackTrace(); } } }
A try-with-resources statement can have catch and finally blocks just like an ordinary try statement.
Note: A try -with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try -with-resources statement, any catch or finally block is run after the resources declared have been closed.
The try-with-resources statement ensures that each resource is closed at the end of the statement execution. If we don't close the resources, it may constitute a resource leak and also the program could exhaust the resources available to it. You can pass any object as a resource that implements java.
automatic resource management or try-with-resources is a new exception handling mechanism that was introduced in Java 7, which automatically closes the resources used within the try-catch block.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With