Skip to main content

Week 4 Day 3 — try-with-resources and finally

Goal

Today I want resource cleanup that does not hide the original failure.

Main questions:

  1. What is AutoCloseable?
  2. How does try-with-resources close?
  3. What is a suppressed exception?
  4. When do I still use finally?
  5. 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:

  • A is the exception that propagates
  • B is attached as suppressed: A.getSuppressed() contains B
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.

ResourceWho closes
Files.newInputStream in my codeI do — try-with-resources
JDBC Connection I opened myselfI do
JdbcTemplate / DataSource from SpringSpring / the pool
EntityManager in a @Transactional serviceSpring
@Value Resource stream I openedI 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 EntityManager and 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; finally is for non-closeable cleanup.

Body exception wins; close() failures are suppressed.

Close what I opened. Do not close the container’s EntityManager.

Next: Week 4 Day 4 — Domain Exceptions and Translation