Understanding Java Try‑with‑Resources: Syntax, Bytecode, and Best Practices
The article explains Java’s try‑with‑resources feature as syntactic sugar that the compiler rewrites into a try‑finally structure with sophisticated exception handling, demonstrates the generated bytecode, clarifies AutoCloseable versus Closeable, and offers best‑practice guidelines for reliable resource management.
This article provides an in-depth analysis of the Java try-with-resources syntax, confirming it is syntactic sugar and presenting its actual implementation via decompiled bytecode.
The author first explains the motivation for using try-with-resources introduced in JDK 7 to simplify resource management and ensure automatic release of resources such as database connections and I/O streams.
A sample Resource class implementing AutoCloseable is shown:
public class Resource implements AutoCloseable{
public void read(){ System.out.println("do something"); }
@Override
public void close() throws Exception { System.out.println("closed"); }
}Without try-with-resources, resources must be closed manually in a finally block:
public static void f1(){
Resource resource = new Resource();
try { resource.read(); }
finally {
try { resource.close(); } catch (Exception e) {}
}
}Using try-with-resources yields cleaner code:
public static void f2(){
try(Resource resource = new Resource()){ resource.read(); }
catch(Exception e) {}
}The article notes that Resource can also implement Closeable , requiring the close method to throw IOException instead of the generic Exception .
public class Resource implements Closeable{
public void read(){ System.out.println("do something"); }
@Override
public void close() throws IOException { System.out.println("closed"); throw new IOException(); }
}It clarifies that Closeable extends AutoCloseable and merely specializes the exception type of close() from Exception to IOException .
public interface AutoCloseable{ void close() throws Exception; }
public interface Closeable extends AutoCloseable{ void close() throws IOException; }The JavaDoc for AutoCloseable.close() is presented and translated, emphasizing that the method releases underlying resources, is invoked automatically by try-with-resources, and implementers should declare more specific exceptions or avoid throwing exceptions when the close operation cannot fail.
The JavaDoc for Closeable.close() is similarly explained, noting that invoking close on an already closed stream has no effect and that resources should be released before throwing an IOException .
To reveal the underlying mechanism, the author decompiles the two methods using javap . The decompiled bytecode shows that try-with-resources is transformed into a try‑finally block with sophisticated exception handling:
public static void f1(){
Resource resource = new Resource();
try { resource.read(); }
finally {
try { resource.close(); } catch (Exception var7) {}
}
} public static void f2(){
try {
Resource resource = new Resource();
Throwable var1 = null;
try { resource.read(); }
catch (Throwable var11) { var1 = var11; throw var11; }
finally {
if (resource != null) {
if (var1 != null) {
try { resource.close(); }
catch (Throwable var10) { var1.addSuppressed(var10); }
} else { resource.close(); }
}
}
} catch (Exception var13) {}
}Key observations from the decompiled code include:
The new Resource() allocation is moved inside the try block.
The call to resource.read() is caught as Throwable and its value stored.
The finally block checks if the resource is non‑null before closing.
If an exception occurred during read() , any exception from close() is captured and added as a suppressed exception via Throwable.addSuppressed .
The outer catch handles any exception thrown from the close method when no prior exception exists.
The article explains Throwable.addSuppressed , which appends a supplied exception to the list of suppressed exceptions maintained by a Throwable instance, and is thread‑safe. Its JavaDoc is provided and translated, describing how suppressed exceptions are collected when try‑with‑resources propagates an exception from the try block while recording exceptions from the finally block.
public final synchronized void addSuppressed(Throwable exception){
if (exception == this) throw new IllegalArgumentException("Self suppression not permitted", exception);
if (exception == null) throw new NullPointerException("null exception");
if (suppressedExceptions == null) return;
if (suppressedExceptions == SUPPRESSED_SENTINEL) suppressedExceptions = new ArrayList<>(1);
suppressedExceptions.add(exception);
}The piece concludes with a brief note on JDK 9 improvements to the try‑with‑resources feature (JSR 334) and a list of best practices, such as:
try‑with‑resources always invokes the resource’s close() method after the try block, regardless of whether an exception was thrown.
When multiple resources are declared, their close() methods are invoked in the reverse order of their creation.
If the try block throws an exception, all resource close() methods are executed first, then the catch block, and finally the finally block.
Exceptions thrown during resource construction must be caught in the corresponding catch block.
Exceptions declared by the automatic close() call must also be caught in the try’s catch block.
When both the try block and the close operation throw exceptions, the try block’s exception is propagated and the close exception is recorded as a suppressed exception via Throwable.addSuppressed .
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.