Java Try-with-Resources Explained with Examples

Try-with-resources closes eligible resources when control leaves a try block.

Lesson 68 of 127Intermediate13 min read

Try-with-resources closes eligible resources when control leaves a try block. It prevents the common mistake of closing a file only on success, and it defines how failures during cleanup relate to an earlier failure. A resource must implement AutoCloseable or a suitable subinterface such as Closeable. The resource declarations are part of the try header. The examples show a real reader, reverse closing order, and suppressed exceptions, all using Java 17 without additional libraries.

Declare the resource at its ownership boundary

A resource created in the try header belongs to that block's cleanup boundary. Whether the body completes normally, returns, or throws, Java attempts to close the initialized resources during ordinary control flow. The close call is automatic, but creation can still fail before the body starts. Keep the resource scope as small as the work permits. Closing a stream can also affect its underlying resource, so understand which layer owns the connection or file.

AutoCloseable supplies the close contract

AutoCloseable declares close, which may throw Exception. Closeable is an I/O-oriented subinterface with a narrower IOException contract. A custom resource can implement close to release its owned state, but implementing the interface does not magically allocate or manage anything. The operation must really define what closed means. Some objects tolerate repeated closes and some have more specific behavior; use the documented resource contract instead of assuming all classes behave identically.

Use a previously declared resource carefully

Since Java 9, an already declared final or effectively final resource variable can appear in the header as try (reader). This is useful when obtaining the resource through another step. The variable remains in scope afterward, but its resource has been closed, so further operations can fail. Reassigning that variable would break the effectively-final rule. Keeping references in scope does not mean their external resource remains usable.

Multiple resources close in reverse order

When the header declares first and then second, second closes before first. This order supports layered resources where a later wrapper depends on an earlier resource. If constructing a later resource fails, earlier successfully initialized resources are still closed. Choose declarations according to ownership; do not independently close a shared resource that callers expect to keep using. Reverse order is a language guarantee, not something to recreate manually with guessed finally statements.

Understand primary and suppressed failures

If the body throws and closing also throws, the body failure remains primary and the cleanup failure is attached as suppressed. getSuppressed exposes those additional failures. If the body completes normally but close fails, that closing failure propagates. Preserving both pieces is useful because the cleanup problem may affect recovery or diagnostics without hiding what originally broke. Log the full Throwable when appropriate instead of discarding its cause and suppressed information.

Use the construct without overstating its reach

Try-with-resources handles closing, not rollback of every state change or guaranteed survival of process termination. Reading invalid data still needs validation and a suitable catch. A file created by your program is not automatically deleted merely because its writer closes. Manage fixture deletion or transactional cleanup separately. Continue with file handling to distinguish stream ownership, stored data, and deleting filesystem entries.

Practice tasks and checks

Modify ReaderResource to return from a helper inside the ownership block and verify the reader still closes. For ResourceCloseOrder, add a third resource and predict all closing messages before running it. Then make the second constructor fail and reason about which earlier resources were successfully initialized and must close. For SuppressedResource, remove the body failure and observe that the close failure becomes primary. Check suppressed-array length before indexing in your general reporter. Finally, write a file example that closes its writer and separately deletes its temporary file. Explain why those actions solve different lifecycle responsibilities. Use the resource construct for cleanup without assuming it validates data or reverses every external change.

Java Summary

  • Resources close during ordinary control flow even after return or failure.
  • Multiple resources close in reverse initialization order.
  • Cleanup failures are suppressed when a body failure is already primary.
  • Continue with file lifecycle operations when these examples are clear.

Java Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Close a real reader automatically

Save this program as ReaderResource.java. Compile it with javac ReaderResource.java and run it with java ReaderResource.

Expected output:

read obtains the first UTF-16 code unit from Notes, which is N. Leaving the try closes the StringReader even though the variable remains accessible. The later read therefore throws IOException and the catch reports the closed state. The example deliberately demonstrates that scope and resource usability are different. In normal application code, avoid using the reader after its ownership block rather than catching repeated closed-resource failures.

Java
import java.io.IOException;
import java.io.StringReader;
public class ReaderResource {
    public static void main(String[] args) throws IOException {
        StringReader reader = new StringReader("Notes");
        try (reader) {
            System.out.println((char) reader.read());
        }
        try {
            reader.read();
        } catch (IOException error) {
            System.out.println("Reader is closed");
        }
    }
}
Output
N
Reader is closed
Example 2

Observe reverse resource cleanup

Save this program as ResourceCloseOrder.java. Compile it with javac ResourceCloseOrder.java and run it with java ResourceCloseOrder.

Expected output:

The body calculates twenty-one before leaving the block. Cleanup then calls close on second followed by first, and the diagnostic messages reveal that order. The custom class is an instrumented ownership example rather than an actual file or connection. Its close method performs no checked throwing operation, so main does not need an exception declaration for those particular resources.

Java
public class ResourceCloseOrder {
    static class Resource implements AutoCloseable {
        private final String name;
        Resource(String name) { this.name = name; }
        @Override public void close() { System.out.println("Closed " + name); }
    }
    public static void main(String[] args) {
        try (Resource first = new Resource("first");
             Resource second = new Resource("second")) {
            System.out.println(7 * 3);
        }
    }
}
Output
21
Closed second
Closed first
Example 3

Inspect a suppressed close failure

Save this program as SuppressedResource.java. Compile it with javac SuppressedResource.java and run it with java SuppressedResource.

Expected output:

The body raises IllegalArgumentException first. During cleanup, close raises IllegalStateException. Java retains the body failure as the caught primary exception and records the cleanup failure in its suppressed array. This example knows one suppressed failure exists because it deliberately constructs that path. A general error reporter must inspect the array's length rather than assuming every exception has a first suppressed entry.

Java
public class SuppressedResource {
    static class BrokenClose implements AutoCloseable {
        @Override public void close() {
            throw new IllegalStateException("Close failed");
        }
    }
    public static void main(String[] args) {
        try (BrokenClose resource = new BrokenClose()) {
            throw new IllegalArgumentException("Body failed");
        } catch (Exception error) {
            System.out.println(error.getClass().getSimpleName());
            System.out.println(error.getSuppressed()[0].getClass().getSimpleName());
        }
    }
}
Output
IllegalArgumentException
IllegalStateException

Java FAQ

Does closing a writer delete its file?

No. Closing releases the writer's resource and flushes according to its contract. The stored filesystem entry remains unless your program explicitly deletes it or uses a separate documented lifecycle mechanism.

What if a second resource fails during construction?

Resources successfully initialized earlier in the header are still closed. The body does not start with an incomplete resource set, and any cleanup failures are related to the construction failure through the specified suppression rules.

Which objects can I use in try-with-resources?

Any object whose class implements AutoCloseable. That includes file streams, readers and writers, Scanner, JDBC connections, statements and result sets, and, since Java 19, ExecutorService. Your own classes can implement AutoCloseable to get the same automatic clean-up.

Can try-with-resources have catch and finally blocks?

Yes. The resources are closed first, as soon as the try block ends, and then any matching catch and the finally block run. That means a catch block can no longer use the resource, which is already closed.

Do I still need to call close() myself?

No. Calling close() on a resource declared in the try header is unnecessary, because Java closes it automatically on every exit path. Calling it twice is usually harmless for standard classes, but it adds noise and can hide the ownership rule the statement expresses.

Java Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

Which interface makes a resource eligible for this construct?

Multiple choicemedium

Which resource closes first when declared first then second?

Java Quiz

2 questions to check what you learned.

  1. What remains primary when both body and close throw?

    • The body exception
    • The close exception always
    • No exception
  2. Does a variable remaining in scope mean its reader is open?

    • No
    • Always
    • Only for StringReader