Q20 of 40 · Core Java

How do try-with-resources statements work? Why are they preferred over try-finally?

Core JavaMidtry-with-resourcesautocloseablejava-exceptionsresource-management

Short answer

Short answer: try-with-resources automatically calls close() on any AutoCloseable declared in the parentheses when the block exits — whether normally or via exception. This eliminates the resource-leak bugs common in try-finally patterns where an exception in the finally block suppresses the original exception.

Detail

Before try-with-resources (Java 7), the correct way to handle a closeable resource was:

Resource r = new Resource();
try {
    r.use();
} finally {
    r.close(); // what if close() throws? swallows the original exception!
}

If r.use() threw an IOException and then r.close() also threw, the finally block's exception would replace the original — you'd see the close exception, not the use exception. The original was silently swallowed.

try-with-resources solves both problems: resources are closed in reverse declaration order after the block exits, and if both the block and close() throw, the close exception is suppressed (attached to the original via addSuppressed()) rather than replacing it. You still get the meaningful original exception, and the suppressed close exception is accessible via getSuppressed() if needed.

Multiple resources can be declared, separated by semicolons: try (A a = new A(); B b = new B()). B closes first, then A (reverse order).

Any class implementing AutoCloseable works — including Selenium's WebDriver, JDBC Connection and PreparedStatement, file streams, HTTP clients, and socket connections. In test frameworks like JUnit 5, custom AutoCloseable extensions registered via @RegisterExtension use this same mechanism.

// EXAMPLE

TryWithResources.java

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;

// ❌ try-finally: exception in close() swallows the real exception
Connection conn = dataSource.getConnection();
try {
    PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
    ps.setLong(1, userId);
    ResultSet rs = ps.executeQuery();
    // ... process results
} finally {
    conn.close(); // if this throws, original exception is lost
}

// ✅ try-with-resources: auto-close, suppressed exceptions preserved
try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement(
         "SELECT id, name FROM users WHERE active = ?")) {

    ps.setBoolean(1, true);

    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            System.out.println(rs.getLong("id") + ": " + rs.getString("name"));
        }
    } // ResultSet closed first
} // PreparedStatement closed, then Connection — reverse declaration order

// Custom AutoCloseable resource (e.g., a test context)
class TestSession implements AutoCloseable {
    TestSession() { /* setup */ }
    @Override public void close() { /* teardown */ }
}

try (var session = new TestSession()) {
    session.runScenario("checkout");
} // session.close() always called

// WHAT INTERVIEWERS LOOK FOR

The two problems solved (guaranteed close + exception suppression rather than swallowing), reverse-order close for multiple resources, and the AutoCloseable interface as the mechanism. JDBC or Selenium examples show real-world relevance.

// COMMON PITFALL

Saying try-with-resources 'catches exceptions'. It doesn't — it closes resources and preserves exceptions. The block's exceptions still propagate. Also missing: the suppressed exception mechanism, which is the key improvement over try-finally.