While a finally block is designed to run reliably regardless of how the try block exits, it can cause unexpected behavior when it throws its own exception or returns a value, both of which silently override whatever happened in the try block.
Key Points: • If an exception is thrown inside finally, for example while closing a resource, it replaces any exception that was already propagating from the try block, and the original exception's details are lost. • A return statement inside finally silently swallows any exception from the try block and also overrides any return value the try block was about to produce. • This is especially dangerous with nested resource cleanup, where closing a second resource throws while the first exception from the try block is still in flight. • try-with-resources mitigates this for Closeable and AutoCloseable resources by attaching suppressed exceptions instead of discarding the original one. • As a rule, finally blocks should be kept simple, avoid return or throw statements, and use try-with-resources for resource cleanup instead of manual close() calls.
Example: A method that throws a custom exception in its try block but then fails to close a stream in its finally block will surface only the IOException from the close, completely hiding the original, likely more important, exception.
Code Example:
try {
return riskyOperation();
} finally {
stream.close(); // if this throws, it replaces any exception above
}Interview Tip: A concise interview answer is:
"Yes — a finally block that throws or returns can silently discard the original exception from the try block, which is a subtle source of lost error information. I avoid that by keeping finally blocks minimal and using try-with-resources, which attaches cleanup failures as suppressed exceptions instead of overwriting the original one."