Hook: Interviewers love this one because it looks like a small syntax feature, but it secretly tests resource cleanup, exception flow, and whether you can avoid leaks under pressure.
Question: What is try-with-resources in Java?
Answer: Try-with-resources is a try block that automatically closes resources such as files, sockets, and JDBC objects when the block ends. The resource must implement AutoCloseable (a contract that says, 'I know how to close myself'). Java closes the resource even if an exception is thrown, which makes code shorter and safer than manual cleanup.
Interview-Ready Answer: I use try-with-resources whenever I open something that must be closed, like a file, socket, scanner, or database connection. I declare the resource in the try header, and Java closes it automatically at the end of the block, even if the body throws. A nice detail is that multiple resources close in reverse order, and if both the body and close() fail, the body exception wins while close failures are kept as suppressed exceptions. Since Java 9, I can also reuse an effectively final local variable in the resource list, which makes cleanup easier to write and harder to forget.
Detailed Explanation: Try-with-resources is Java’s built-in pattern for automatic cleanup. A resource is any object that holds something external and must be released later, such as a file handle, database connection, or network socket. The key idea is simple: open it, use it, and let Java close it for you no matter how the block exits.
primary exception. A primary exception is the main failure that will be reported to the caller.close() also throws, Java does not lose that error; it attaches it to the primary one as a suppressed exception, meaning a secondary exception that is recorded instead of replacing the main one.Use it whenever the object represents a closeable external resource. That includes InputStream, Reader, Socket, Channel, JDBC Connection, PreparedStatement, and ResultSet. It is especially valuable in exception-heavy code, because human beings are bad at remembering cleanup in every branch.
| Approach | Cleanup | Exception safety | Best for |
|---|---|---|---|
| Manual finally | You write it | Easy to get wrong | Legacy code |
| Try-with-resources | Automatic | Suppressed exceptions preserved | Closable resources |
| No cleanup | None | Leaks likely | Almost never |
null, Java skips the close call instead of throwing a NullPointerException.O(n) for n resources because Java closes each one once; in real systems, the I/O work dwarfs this cost.close() method and represents a scarce external handle, try-with-resources is usually the right default.Memory hook: Think of it like leaving a hotel room: you can walk out in any mood, but housekeeping still comes in to lock the door behind you. If the room already had a problem, the cleaning issue gets written down as a note instead of replacing the main complaint.
Real-World Example: Imagine a checkout service that talks to a PostgreSQL database on every payment. It opens a Connection, runs a PreparedStatement, and reads a ResultSet for inventory and order writes. If one error path forgets to close the connection, the pool slowly fills up, and after enough traffic the app starts timing out with messages like Connection is not available, request timed out after 30000ms.
What goes wrong in the incident: a developer manually closes the statement in one branch but misses the result set in another, or closes things in the wrong order. Users see checkout spinning, retries failing, and the logs show pool exhaustion rather than a clear business error. Try-with-resources fixes the leak by making the cleanup automatic and consistent, which is exactly what a high-traffic production service needs.
public class TryWithResourcesDemo {
public static void main(String[] args) {
normalFlow();
System.out.println("\n---\n");
exceptionAndSuppressed();
System.out.println("\n---\n");
nullResourceExample();
}
private static void normalFlow() {
System.out.println("Normal flow:");
// The key benefit is that we do not need a finally block to remember cleanup.
try (DemoResource r1 = new DemoResource("R1", false);
DemoResource r2 = new DemoResource("R2", false)) {
System.out.println(" inside try body");
} catch (Exception e) {
System.out.println(" unexpected: " + e);
}
}
private static void exceptionAndSuppressed() {
System.out.println("Failure with suppressed exception:");
try (DemoResource r1 = new DemoResource("DB-connection", true);
DemoResource r2 = new DemoResource("ResultSet", true)) {
System.out.println(" doing work");
// This is the primary exception. Close failures will be attached as suppressed.
throw new IllegalStateException("primary failure in body");
} catch (Exception e) {
System.out.println(" caught: " + e);
for (Throwable suppressed : e.getSuppressed()) {
System.out.println(" suppressed: " + suppressed);
}
}
}
private static void nullResourceExample() {
System.out.println("Null resource example:");
// If the expression evaluates to null, Java skips close() safely.
try (DemoResource r = maybeOpen(true)) {
System.out.println(" resource is null? " + (r == null));
} catch (Exception e) {
System.out.println(" unexpected: " + e);
}
}
private static DemoResource maybeOpen(boolean returnNull) {
return returnNull ? null : new DemoResource("Optional", false);
}
private static final class DemoResource implements AutoCloseable {
private final String name;
private final boolean failOnClose;
private boolean closed;
DemoResource(String name, boolean failOnClose) {
this.name = name;
this.failOnClose = failOnClose;
System.out.println(" open " + name);
}
@Override
public void close() throws Exception {
if (closed) {
// Try-with-resources will not normally double-close, but this guard is good practice.
System.out.println(" close skipped (already closed) " + name);
return;
}
closed = true;
System.out.println(" close " + name);
if (failOnClose) {
throw new Exception("close failed for " + name);
}
}
}
}Follow-up & Tricky Questions:
close() also throws? The body exception is the primary exception, and the close failure is added as a suppressed exception. That way you do not lose the real failure that caused the operation to fail.null? Yes. If the expression evaluates to null, Java simply skips the close call, so there is no NullPointerException during cleanup.AutoCloseable. Closeable is a narrower I/O-specific interface that extends it, but AutoCloseable is the general contract.finally block run before or after resources close? If you attach a finally to a try-with-resources statement, Java closes the resources first, then runs the finally block. This ordering is a common exam trap.finally blocks? No. It replaces cleanup for closeable resources, but you may still need finally for non-resource actions like restoring thread-local state or releasing a lock.close() inside the block? You can, but usually should not. Doing so risks double-close bugs or confusing state, and the whole point of the feature is to centralize cleanup automatically.Common Mistakes:
close() manually inside the block; correction: let try-with-resources own cleanup to avoid double-close bugs.getSuppressed() when debugging cleanup failures.Memory Hook: 'Open it, use it, leave it to Java, and if the exit door squeaks, Java writes that squeak down instead of losing the main problem.'
Cheat Sheet:
AutoCloseable.Practice Tasks:
BufferedReader around a file and print the first line.AutoCloseable classes and verify the close order with print statements.close() fail, then inspect the suppressed exceptions.