Question 9 · Section 7

What is try-with-resources?

Previously you had to manually close resources in finally:

Language versions: English Russian Ukrainian

Junior Level

Definition

Try-with-resources (TWR) - a Java 7+ construct. Minimum version: Java 7. For Java 6 and below - use finally. In Android - API 19+ (KitKat). Automatically closes resources after exiting the try block.

Why it’s needed

Previously you had to manually close resources in finally:

// Old approach (before Java 7)
BufferedReader br = null;
try {
    br = new BufferedReader(new FileReader("file.txt"));
    br.readLine();
} finally {
    if (br != null) br.close(); // Manually!
}

Now automatic:

// Modern approach (Java 7+)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    br.readLine();
} // br.close() is called automatically!

Which resources can be used

Any that implement AutoCloseable:

  • FileInputStream, BufferedReader - files
  • Connection, Statement - database
  • Socket - network

Multiple resources

try (FileInputStream fis = new FileInputStream("in.txt");
     FileOutputStream fos = new FileOutputStream("out.txt")) {
    // Both will be closed automatically
}

When NOT to use try-with-resources

  1. Factory methods - you create a resource and return it to the caller (TWR will close it before return)
  2. Resource lives longer than one method - lifecycle is managed externally
  3. Pooled resources (HikariCP connection) - return to pool != closing, pool manages itself

Middle Level

How it works

The compiler expands TWR into code with finally and suppressed exception handling:

// Your code
try (Resource r = new Resource()) {
    r.doWork();
}

// Compiler creates this template to:
// (1) guarantee resource closing
// (2) not lose exception from close() if there was already an error in try
//     -- it's added to the suppressed list of primaryException
// Expands to:
Resource r = new Resource();
Throwable primaryException = null;
try {
    r.doWork();
} catch (Throwable t) {
    primaryException = t;
    throw t;
} finally {
    if (r != null) {
        if (primaryException != null) {
            try { r.close(); }
            catch (Throwable t) { primaryException.addSuppressed(t); }
        } else {
            r.close();
        }
    }
}

Suppressed Exceptions

If exception #1 was thrown in try, and exception #2 during close():

  • #1 remains the main one
  • #2 is added as suppressed
  • You get the full picture: Error A (Suppressed: Error B)

Get suppressed: exception.getSuppressed()

Nested resource trap effect

// DANGEROUS - FileWriter may leak!
try (BufferedWriter writer = new BufferedWriter(new FileWriter("file.txt"))) {
    // If BufferedWriter's constructor throws, FileWriter will NOT close
}

// SAFE - each resource separately
try (FileWriter fw = new FileWriter("file.txt");
     BufferedWriter writer = new BufferedWriter(fw)) {
    writer.write("data");
}

Java 9+ - effectively final

You can use external variables:

BufferedReader br = createReader();
try (br) { // br is effectively final
    br.readLine();
}

Senior Level

Close order (LIFO)

Resources are closed in the reverse order of their declaration:

LIFO (Last In, First Out) - last opened, first closed. Like a stack of plates: last one you put - first one you take. For resources this means: the most “inner” resource is closed first.

try (Socket socket = new Socket("localhost", 8080);
     OutputStream os = socket.getOutputStream();
     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os))) {
    // Will close: writer -> os -> socket
}

This is critical for stream stacks. If you close Socket before Stream, Stream will fail on flush attempt.

Null Resources

If a resource in parentheses is initialized as null, TWR simply ignores it - no NPE.

Performance: Suppressed Overhead

When closing 1000 resources with mass failure, the main exception object contains a huge suppressed list. This can hit memory.

Edge Cases

  • Catch/Finally order - your catch and finally execute after resources are closed
  • Inside catch, resources are already unavailable (closed)
  • Compiler creates a hidden local variable for external resources (Java 9+)

Diagnostics

  • Throwable.getSuppressed() - always check this array for infrastructure issues
  • Static Analysis - SonarLint finds resources created inside try block but not in parentheses
  • javap -v - see generated bytecode to understand the mechanism

Interview Cheat Sheet

Must know:

  • Try-with-resources (TWR) - Java 7+, automatically closes resources after exiting try
  • Resource must implement AutoCloseable
  • Compiler expands TWR to finally with suppressed exception handling
  • Suppressed exceptions: if both try and close() failed - #1 is main, #2 is suppressed
  • Close order - LIFO (last opened, first closed)
  • Nested resource trap effect: new B(new A()) - if B fails, A leaks
  • Java 9+ supports effectively final external variables
  • Null resource in TWR is simply ignored - no NPE

Frequent follow-up questions:

  • Why are nested resources dangerous? - If outer resource’s constructor fails, inner one leaks; declare each separately
  • In what order are resources closed? - LIFO: last declared is closed first
  • What happens if resource = null? - TWR ignores it, no NPE
  • When NOT to use TWR? - Factory methods (return from method), pooled resources, resource lives longer than method

Red flags (NOT to say):

  • “I create resources inside try but not in parentheses” - they won’t close automatically
  • “Suppressed exceptions don’t matter on mass failure” - they can contain critical diagnostics
  • “TWR only works with InputStream” - works with any AutoCloseable

Related topics:

  • [[What are the requirements for resources in try-with-resources]]
  • [[What is the AutoCloseable interface]]
  • [[What is the difference between AutoCloseable and Closeable]]
  • [[What are suppressed exceptions]]
  • [[Is the execution of a finally block guaranteed]]