Multiple Resources in Try-With-Resources
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Try-with-resources supports multiple resources in one header, separated by semicolons. Java evaluates their initializers from left to right. On exit, it closes the resources in the opposite order, so the last successfully initialized resource closes first.
Reverse order matters when one resource depends on another. An ObjectOutputStream may wrap a file OutputStream, or a parser may wrap a reader. The outer layer can flush trailers, buffered data, or parser state before the underlying transport disappears. Listing the underlying resource first and its wrapper second produces the correct wrapper-first close order.
For example, try (OutputStream file = Files.newOutputStream(path); BufferedOutputStream buffer = new BufferedOutputStream(file)) initializes the file and then the buffer. When the block ends, the buffer closes first and then the file variable is closed. Closing the wrapper commonly closes the underlying stream too, so APIs should tolerate or avoid redundant ownership; often declaring only the outermost resource is enough when the inner resource is created directly inside it.
Partial initialization is safe. If the second or third initializer throws, Java closes every earlier resource whose initialization completed. If the body throws and closes also fail, the body exception stays primary. If the body succeeds but multiple close calls fail, the first failure encountered in reverse close order is primary and later close failures are suppressed.
The order is deterministic, but the design should still make dependencies and ownership obvious. Do not put unrelated long-lived shared objects in the resource list merely for convenience. Declare resources in dependency order, close the scope as soon as their work finishes, and inspect suppressed exceptions when cleanup failures matter.
Multiple Resources in Try-With-Resources
Direct Answer
Declare multiple resources separated by semicolons. Java initializes them left to right and closes every successfully initialized resource in reverse order, which correctly unwinds wrappers and dependent resources.
Deep Dive & Architecture
Think of each successful initializer as pushing one resource onto a stack. Leaving the try body pops that stack. This last-in, first-out rule makes layered I/O predictable: a compressor can finish its trailer before its buffered destination closes, and the buffer can flush before the file descriptor closes.
When a later initializer fails, it was never pushed because no usable resource value was produced. The resources already on the stack still unwind. This closes a gap that manual code frequently misses: partial acquisition is itself an exceptional exit that needs cleanup.
Close failures follow the same ordered unwinding. Java continues attempting later closes even after one close has thrown. If there was no earlier body failure, the first close failure becomes primary; other close failures are attached as suppressed. This prevents one broken close from skipping every remaining cleanup while preserving their diagnostics.
Wrapper ownership deserves attention. Closing an outer Java stream normally closes its delegate, so separately listing both may cause a harmless second close for Closeable streams, but that pattern is not universally safe for arbitrary AutoCloseable types. Prefer a single outer declaration when it clearly owns the nested construction, or document why multiple independently acquired resources each require a declaration.
Code Example
import java.util.ArrayList;
import java.util.List;
class MultipleResources {
static final class NamedResource implements AutoCloseable {
private final String name;
private final List<String> closed;
NamedResource(String name, List<String> closed) {
this.name = name;
this.closed = closed;
}
public void close() { closed.add(name); }
}
public static void main(String[] args) {
List<String> closed = new ArrayList<>();
try (var first = new NamedResource("first", closed);
var second = new NamedResource("second", closed)) {
System.out.println("working");
}
if (!closed.equals(List.of("second", "first"))) throw new AssertionError(closed);
System.out.println(closed);
}
}
Check your understanding
Answer these without looking back at the article.
- Why are multiple resources closed in reverse order?
- What closes if the second resource initializer fails?
- Which exception is primary if two close methods fail after a successful body?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.