Week 4 Day 3 — try-with-resources and finally
Goal
Today I want resource cleanup that does not hide the original failure.
Main questions:
- What is
AutoCloseable? - How does try-with-resources close?
- What is a suppressed exception?
- When do I still use
finally? - Which resources does Spring already close for me?
1. The leak problem
Files, sockets, and JDBC connections are not heap objects the GC will politely return to the OS on time. If I new FileInputStream and throw before close(), I leak a handle.
The old pattern:
InputStream in = new FileInputStream(file);
try {
return in.readAllBytes();
} finally {
in.close();
}
If readAllBytes() throws and close() throws, finally’s exception wins in this sketch and the original is lost — unless I am very careful. That is why try-with-resources exists.
2. Try-with-resources
try (InputStream in = Files.newInputStream(path)) {
return in.readAllBytes();
}
The resource must implement AutoCloseable (close()). Several resources are allowed; they close in reverse order of declaration.
close() runs if the resource was assigned, even when the body throws, even when the body returns.
I can use an already-declared effectively-final variable:
var reader = Files.newBufferedReader(path);
try (reader) {
return reader.readLine();
}
Memory sentence:
Try-with-resources closes
AutoCloseables in reverse order, even when the body throws.
3. Suppressed exceptions
If the body throws A and close() throws B:
Ais the exception that propagatesBis attached as suppressed:A.getSuppressed()containsB
try (var broken = new BrokenCloseable()) {
throw new IllegalStateException("body");
}
// caller sees IllegalStateException, suppressed: the close() exception
That is the opposite of a naive finally { close(); } where close() can replace the body exception.
Closeable extends AutoCloseable and declares close() throws IOException. AutoCloseable.close() may throw Exception. For new types I implement AutoCloseable and keep close() cheap and idempotent.
4. finally still exists
Use finally for cleanup that is not an AutoCloseable: restoring a ThreadLocal, stopping a timer, unlocking if I am not using try-with-resources on a Lock (I prefer lock.unlock() in finally or a wrapper).
lock.lock();
try {
work();
} finally {
lock.unlock();
}
Never return from finally. It discards an exception from the try block and replaces it with a normal return. Interviewers wait for that trap.
try {
throw new IllegalStateException("real");
} finally {
return 0; // the exception vanishes
}
5. Spring connection
I close what I opened. I do not close what the container owns.
| Resource | Who closes |
|---|---|
Files.newInputStream in my code | I do — try-with-resources |
JDBC Connection I opened myself | I do |
JdbcTemplate / DataSource from Spring | Spring / the pool |
EntityManager in a @Transactional service | Spring |
@Value Resource stream I opened | I do |
Calling entityManager.close() inside a transactional service fights the container. Calling connection.close() on a pooled connection I got from DataSourceUtils the wrong way can return it twice or not at all.
InputStream from a multipart upload: I still close it if I opened it.
Memory sentence:
Close what I opened. Leave Spring-managed
EntityManagerand pooled connections alone.
6. Common traps
Trap 1: finally { return; } hiding the original exception.
Trap 2: Closing a Spring-managed EntityManager or a connection the transaction still owns.
Trap 3: close() that throws a new exception without attaching the first as suppressed — which try-with-resources already handles if I just implement close.
Trap 4: Assuming GC closes files.
Trap 5: Nested try to close three streams by hand instead of one try-with-resources with three declarations.
Practice Questions and Answers
Question 1
Try-with-resources vs finally?
Answer:
Try-with-resources closes AutoCloseable resources in reverse order and keeps the body’s exception primary, with close() failures suppressed. finally is for cleanup that is not a closeable. I do not return from finally.
Question 2
What happens if both the body and close() throw?
Answer:
The body’s exception propagates. The close() exception is on getSuppressed(). I do not lose the original failure.
Question 3
Does Spring close EntityManager for me?
Answer:
Yes, when it opened it for a @Transactional method (or an Open-Session-In-View filter). I do not close it in the service. I do close streams and files I opened myself.
Question 4
Why is return in finally dangerous?
Answer:
It completes the method normally and discards an exception from try or catch. The caller never sees the failure.
Question 5
In which order do two resources close?
Answer:
Reverse of declaration: the last declared is closed first, like a stack. That matters when the second resource depends on the first (a writer wrapping a stream).
Memory sentences
Prefer try-with-resources;
finallyis for non-closeable cleanup.
Body exception wins;
close()failures are suppressed.
Close what I opened. Do not close the container’s
EntityManager.