One of the common questions I usually ask in Java interviews is "What is try-with-resources?" since it is an important and basic feature in Java to write clean code. It provides a simple and clean way to ensure that resources are closed automatically, making code cleaner, safer, and more readable. In this post, we'll explore what try-with-resources
is, how it works, and why it's a superior approach to the old finally
block.
try-catch-finally
First let's see how developers used to manage resources like files, database connections, etc. before Java 7. To properly manage a resource, you had to use a try-catch-finally
block. The finally
block was essential to ensure that the resource's close()
method was called, regardless of whether the try
block completed normally or threw an exception.
public String readFirstLineFromFile(String path) throws IOException { BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); return br.readLine(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
This code has several problems:
br != null
) because the BufferedReader
might not have been successfully instantiated if an exception occurred in its constructor.try-catch
: The close()
method itself can throw an IOException
, so you need another try-catch
block inside the finally
block.try
block, and another exception is thrown in the finally
block (e.g., from br.close()
), the exception from the finally
block will be propagated, and the original exception from the try
block will be lost ("masked"). This can make debugging tricky.try-with-resources
The try-with-resources
statement simplifies all of this. Any object that implements the java.lang.AutoCloseable
interface can be used as a resource. The statement ensures that the close()
method of the resource is called automatically at the end of the block.
Hereโs the same file-reading example rewritten using try-with-resources
:
public String readFirstLineFromFileWithTWR(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
Look at how much cleaner that is! Hereโs whatโs happening:
BufferedReader
is declared and instantiated inside the parentheses ()
following the try
keyword.BufferedReader
is now effectively final
and its scope is limited to the try
block.try
block finishes (either normally or due to an exception), the close()
method of the BufferedReader
is automatically called. There's no need for a finally
block.AutoCloseable
InterfaceThe magic behind try-with-resources
is the AutoCloseable
interface (and its parent, Closeable
). This interface has a single method:
public interface AutoCloseable { void close() throws Exception; }
Any class that implements this interface can be used in a try-with-resources
statement. Most standard Java resources, like InputStream
, OutputStream
, Scanner
, java.sql.Connection
, Statement
, and ResultSet
, already implement AutoCloseable
.
If an exception is thrown inside the try
block and another one is thrown when close()
is called, the exception from the try
block is the one that gets propagated. The exception from the close()
method is "suppressed" and attached to the main exception.
You can access these suppressed exceptions using the getSuppressed()
method on the thrown exception.
try { // some code that throws an exception } catch (Exception e) { Throwable[] suppressed = e.getSuppressed(); for (Throwable t : suppressed) { // Handle suppressed exceptions } }
You can declare multiple resources in a try-with-resources
statement by separating them with a semicolon. They will be closed in the reverse order of their declaration.
try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) { // Use the zip file and writer }
In this example, writer
will be closed first, followed by zf
.
AutoCloseable
ClassesYou can make your own classes compatible with try-with-resources
by implementing the AutoCloseable
interface. This is useful for managing the lifecycle of your own custom components.
Let's create a simple Resource
that needs to be cleaned up.
class MyResource implements AutoCloseable { private final String name; public MyResource(String name) { this.name = name; System.out.println(name + ": Opened."); } public void doWork() { System.out.println(name + ": Doing work."); } @Override public void close() { System.out.println(name + ": Closed."); } } // Using the custom resource public class Main { public static void main(String[] args) { try (MyResource res1 = new MyResource("Resource1"); MyResource res2 = new MyResource("Resource2")) { res1.doWork(); res2.doWork(); } } }
Output:
Resource1: Opened. Resource2: Opened. Resource1: Doing work. Resource2: Doing work. Resource2: Closed. Resource1: Closed.
As you can see, the resources are closed automatically in the reverse order of their creation.
try-with-resources
close()
is always called.try-catch
blocks or manual null checks in the finally
block.For more in-depth tutorials on AI, Java, Spring, and modern software development practices, follow me for more content:
๐ Blog ๐ YouTube ๐ LinkedIn ๐ Medium ๐ Github
For more core java interview questions, check out core java interview questions.
Stay tuned for more content on the latest in AI and software engineering!
Explore the most significant changes in JDK 24. Learn about language enhancements, performance improvements, new APIs, and security upgrades with practical code examples.
This blog introduces Scoped Values, a preview feature in Java 23, which provides an advanced way of sharing data in concurrent applications addressing the limitations of thread-local variables.
Get instant AI-powered summaries of YouTube videos and websites. Save time while enhancing your learning experience.