try-with-resources

Files, database connections, network sessions and printers must be closed, even when something fails halfway. Writing that in finally blocks is long and easy to get wrong. try-with-resources closes everything for you, in reverse order, before your catch runs, and never loses the original exception. You'll see exactly how it works, write your own AutoCloseable classes, read suppressed exceptions, and then practise with 11 problems.

Module 9 · Exception Handling⏱ 22 minIntermediate11 practice problems
1 The story

The server that ran out of connections

Riya's internship project, a fee-payment portal, works perfectly in testing. On the last day of fee submission, after a few hundred failed payments, the whole portal hangs with “Cannot get a connection from the pool”. Every failed payment had opened a database connection, thrown an exception, and skipped the line that closed it.

Manual close: leaks on failure 10 of 10 connections stuck open Cannot get a connection try-with-resources: always returned try (Connection c = pool.get()) { success or exception: close() is called the pool never runs dry myphatshala.in
Riya

Sir, I did close the connection. It's the last line of the method!

Arjun Sir

And when the payment threw an exception, the last line never ran. You could move it into finally, but then you need null checks, a nested try for close() itself, and you might still lose the real error.

Riya

That sounds like a lot of code for one close.

Arjun Sir

That's why Java 7 added try-with-resources. Declare the connection in brackets after try, and Java closes it for you on every path, in the right order, without hiding the original exception.

2 Definition

What is try-with-resources?

Formal definition (write this in exams)

The try-with-resources statement (Java 7+) declares one or more resources in parentheses after try. A resource is an object whose class implements java.lang.AutoCloseable. When the try block completes, normally or abruptly, the close() method of each resource is called automatically, in the reverse order of declaration, before any catch or finally block of the same statement runs. If the try block throws and close() also throws, the exception from close() is added to the original as a suppressed exception. Since Java 9, an existing final or effectively final variable may be used as a resource.

In simple words

“Here are the things I'm borrowing. Whatever happens in this block, give them all back when it ends: last borrowed, first returned. And if returning one also goes wrong, remember that too, but tell me about the first problem first.”

Words you'll meet in this lesson

ResourceSomething that must be released: file, connection, socket, lock, session.
AutoCloseableInterface with one method, close(). Required for resources.
CloseableOlder I/O interface (close() throws IOException); extends AutoCloseable.
Resource leakA resource that is never closed and slowly uses up a limit.
Suppressed exceptionA secondary exception (usually from close) attached to the main one.
Effectively finalA variable that is never reassigned after it gets its value.

The rules at a glance

Syntaxtry (A a = new A(); B b = new B()) { … }; catch and finally are optional
RequirementThe type must implement AutoCloseable, or javac says try-with-resources not applicable to variable type
Close orderReverse of declaration: b closes before a
WhenRight after the try block ends, before catch and finally run
ExceptionsBody's exception wins; close's exception is attached and read with getSuppressed()
Resource variablesImplicitly final (can't be reassigned). Java 9+: try (existingVar) if effectively final. A null resource is simply not closed.

Exam tip: “In try-with-resources, does close() run before or after catch?” Before. By the time your catch block runs, every resource of that statement is already closed. Also: if close() declares a checked exception (like IOException), you must catch or declare it even if the body throws nothing.

3 Types and ways to use it

What it does for you, three ways

Reverse-order closing

Several resources in one statement are closed last-opened-first, so dependent resources (a statement on a connection) close before what they depend on.

LIFO

Closing on every path

Normal end, return, break or exception: close always happens, and before the catch block, so handlers never see a half-open resource.

no leaks

Suppressed, not lost

If the body fails and close fails too, you get the body's exception, with the close failure attached. A manual finally would have thrown the real error away.

getSuppressed()
class Res implements AutoCloseable {
    private final String name;
    private final boolean failOnClose;

    Res(String name, boolean failOnClose) {
        this.name = name;
        this.failOnClose = failOnClose;
        System.out.println("  open " + name);
    }

    @Override
    public void close() {
        System.out.println("  close " + name);
        if (failOnClose) throw new IllegalStateException("close failed: " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("1) Normal end:");
        try (Res a = new Res("A", false); Res b = new Res("B", false)) {
            System.out.println("  body");
        }

        System.out.println("2) Body throws:");
        try (Res a = new Res("A", false)) {
            throw new IllegalStateException("body failed");
        } catch (IllegalStateException e) {
            System.out.println("  catch: " + e.getMessage());
        }

        System.out.println("3) Body and close both throw:");
        try (Res a = new Res("A", true)) {
            throw new IllegalStateException("body failed");
        } catch (IllegalStateException e) {
            System.out.println("  catch: " + e.getMessage());
            System.out.println("  suppressed: " + e.getSuppressed()[0].getMessage());
        }
    }
}
1) Normal end: open A open B body close B close A 2) Body throws: open A close A catch: body failed 3) Body and close both throw: open A close A catch: body failed suppressed: close failed: A

Manual finally vs try-with-resources

Manual try { … } finally { r.close(); }try (R r = …) { … }
Lines of codeMany: declare outside, null check, nested try for closeOne line in the header
Several resourcesNested try/finally blocks, easy to get the order wrongSeparated by ;, closed in reverse automatically
Body and close both throwClose's exception replaces the body's; the real cause is lostBody's exception kept; close's attached as suppressed
Forgetting to closePossible (and common)Impossible for declared resources
Available sinceJava 1.0Java 7 (existing variables: Java 9)

Only what's in the brackets is closed. In try (BufferedReader br = new BufferedReader(new FileReader(f))), closing br also closes the FileReader inside it. But if the BufferedReader constructor itself threw, the FileReader would never be closed. For full safety, declare each resource separately: try (FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr)).

4 Step by step

Using try-with-resources, start to finish

  1. Make sure the type is AutoCloseable

    class PrinterSession implements AutoCloseable {
        public void close() { … }
    }
    JDK resources already implement it: FileReader, BufferedReader, Scanner, JDBC Connection, Statement, ResultSet, sockets, streams. A String doesn't, so try (String s = "x") is a compile error.
  2. Declare the resources in the brackets

    try (Conn c = new Conn(); Query q = new Query()) {
    They are created left to right. If the second constructor throws, the first resource (already open) is closed, and the second, which never existed, is not.
  3. Use them in the block

        System.out.println(q.balanceOf("SB-1042"));
    }
    The resource variables are implicitly final: q = new Query(); inside the block gives auto-closeable resource q may not be assigned.
  4. Java closes them in reverse

    At the end of the block: q.close(), then c.close(). Then catch (if an exception is pending and matches), then finally.

    javac generates the code you would otherwise write by hand: calls to close() on every exit path, handlers of type Throwable, and calls to Throwable.addSuppressed. You can see them with javap -c.
  5. Read suppressed exceptions when you need them

    Exception in thread "main" java.lang.IllegalStateException: upload failed at 60%
    	at Upload.send(Main.java:3)
    	at Main.main(Main.java:15)
    	Suppressed: java.lang.IllegalStateException: connection reset while closing
    		at Upload.close(Main.java:8)
    		at Main.main(Main.java:14)
    Stack traces print suppressed exceptions indented under “Suppressed:”. In code, e.getSuppressed() returns them as an array (empty if there are none).
5 See it happen

Two resources, a failure, and a suppressed exception: both views

Left: the line and the console. Right: the open resources (last opened on top) and the exception objects.

A fee-payment query that times out 6 steps
What you see try (Conn c = pool.get(); Stmt s = c.prepare(sql)) { s.execute(); // times out // auto: s.close() // auto: c.close() → fails too } catch (IllegalStateException e) open connection open statement close statement close connection catch: query timeout suppressed: pool unreachable First resource opened. Second resource opened on top of it. The body throws; nothing printed yet. Last opened, first closed. Closing fails, but it isn't lost. Catch gets the real problem + the extra. myphatshala.in
Behind the screen Open resources Exceptions (heap) #2 Stmt (top) #2 closed #1 Conn #1 closed (with error) #3 IllegalStateException "query timeout" suppressed: [ ] suppressed: [ #4 ] #4 IllegalStateException "pool unreachable" #1 opened: Java will close it later. #2 opened on top: it will close first. #3 thrown; it is held while Java closes. #2.close() runs and succeeds. #1.close() throws #4: #3.addSuppressed(#4). Only now does the catch receive #3. myphatshala.in
6 Real-world use

try-with-resources in real systems

Anything that holds an operating-system handle, a network connection or a lock should be opened in try-with-resources.

Watch it with an example: a billing counter that always locks the cash drawer

A supermarket counter opens the cash drawer and the receipt printer as resources. The printer runs out of paper mid-bill. Watch Java close both, printer first, before the cashier's catch block shows a message.

Paper runs out, the drawer still locks 6 steps
Animated example try (CashDrawer d = till.open(); ReceiptPrinter p = new …()) { p.print(bill7712); // out of paper! // auto: p.close() // auto: d.close() } catch (PrinterException e) { … } Cash drawer OPEN Cash drawer LOCKED Receipt printer ON OUT OF PAPER OFF opened 1st, closed 2nd opened 2nd, closed 1st PrinterException (held while Java closes) Bill 7712 saved. Reprint after refill. Resource 1: the drawer opens. Resource 2: the printer starts. Printing fails: an exception is thrown. Java closes the printer first (last opened). Then it locks the drawer. Only now does the catch run. myphatshala.in

1. JDBC: connection, statement, result set

Plain JDBC code opens all three in one try-with-resources, so they are closed in the right order (result set, statement, connection) and returned to the pool even when a query fails.

pool.getConnection()→try (Connection c; PreparedStatement ps; ResultSet rs)→Back to the pool

2. Reading uploads and reports

CSV imports, bank statements and log processing read files with BufferedReader or Files.lines inside try-with-resources. On a busy server, each leaked file handle counts against an operating-system limit.

Uploaded CSV→try (BufferedReader br = …)→Rows imported, file closed

3. HTTP clients and sockets

Calls to payment gateways or SMS providers open network connections and response streams. Closing them promptly lets the client reuse connections; leaking them eventually blocks new calls.

Gateway call→try (response body)→Connection reused

4. Your own AutoCloseable wrappers

Teams wrap locks, temporary files, tracing spans or “maintenance mode” switches in small AutoCloseable classes, so try (var lock = locks.acquire(orderId)) guarantees release.

acquire()→try (…) { work }→Always released

Examples are simplified. Frameworks often manage connections for you, but underneath they rely on the same close-on-every-path guarantee.

7 Run the code

Riya's fee file, read safely

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class Main {
    public static void main(String[] args) {
        String feeFile = "Riya,45000\nKabir,45000\nSana,22500\n";
        long total = 0;
        int students = 0;
        try (BufferedReader br = new BufferedReader(new StringReader(feeFile))) {
            String line;
            while ((line = br.readLine()) != null) {
                String[] parts = line.split(",");
                total += Long.parseLong(parts[1]);
                students++;
            }
        } catch (IOException e) {
            System.out.println("Could not read fees: " + e.getMessage());
        }
        System.out.println(students + " students, total fees Rs " + total);
    }
}
3 students, total fees Rs 112500
  • try (BufferedReader br = …)The reader is the resource. It is closed when the block ends, whatever happens inside.
  • new StringReader(feeFile)Reads from a String so the program runs anywhere. For a real file use new FileReader("fees.csv").
  • catch (IOException e)Needed because readLine() and close() declare the checked IOException.
  • No finallyNothing to clean up by hand: that's the point.
8 Line by line

Two doors opened, closed in reverse

A bank locker visit: the gate opens, then the locker. When the block ends, Java calls close() on the locker first, then the gate, with no close call written anywhere.

Worked examples: real problems, solved and traced in memory

Three jobs where something must always be closed. The resources are small classes of our own that print when they open and close, so you can see every step. Watch the close() frames being pushed automatically, in reverse order, before any catch block runs.

Example 1Banking · database access

Connection and statement closed in reverse order

Problem statement

A balance query needs a database connection and, on top of it, a statement. Both must be closed after the query, the statement first. Model them with two small AutoCloseable classes.

Input
account = SB-1042
Expected output
connection open
SB-1042: Rs 15250
query closed
connection closed
done

Approach

  1. Open both in one try-with-resources: try (Conn c = new Conn(); Query q = new Query()).
  2. Run the query inside the block.
  3. When the block ends, Java calls q.close() and then c.close(): reverse order, no code needed.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Logistics · shipment import

The import fails, the file is still closed

Problem statement

A courier company imports shipment weights from a partner's file. One row is broken. The file must be closed anyway, and the error reported by the caller. A small ShipmentFile class stands in for the real file.

Input
rows = {"2.5", "1,2", "4.0"}
Expected output
file closed after 2 rows
Import failed: For input string: "1,2"

Approach

  1. importAll opens the file in try-with-resources and adds up the weights.
  2. Row 2 makes Double.parseDouble throw a NumberFormatException; importAll has no catch.
  3. Before the exception leaves importAll, Java closes the file. Then main's catch runs.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3Retail · billing counter

A printer jam plus a failed close: the suppressed exception

Problem statement

At a billing counter, printing the receipt fails (paper jam). While closing the printer session, the device also reports an error. The cashier screen should show the real problem, and the support log should still see the closing error.

Input
bill no. 5531; printer jams; closing fails too
Expected output
Cashier sees: Paper jam on bill 5531
Support log: Printer offline during close

Approach

  1. PrinterSession.print throws for the jam; close() throws as well.
  2. try-with-resources keeps the jam as the main exception and attaches the close error with addSuppressed.
  3. main prints the main message and loops over getSuppressed().

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 9.6 in one screen

  • try (Res r = …) { } closes r automatically on every path (Java 7+).
  • Resources must implement AutoCloseable (Closeable extends it).
  • Several resources: separate with ;; closed in reverse order.
  • Close happens before catch and finally of the same statement.
  • Body's exception wins; close failures become suppressed: e.getSuppressed().
  • Resource variables are implicitly final; Java 9+ allows try (existingEffectivelyFinalVar).
  • If close() declares a checked exception, catch or declare it.

Interview and viva questions

What is try-with-resources and why was it introduced?

A form of try (Java 7) that declares resources in parentheses and closes them automatically when the block ends. It replaced long, error-prone finally blocks that needed null checks and nested try/catch for close(), and that could lose the original exception if close() also failed.

What is a suppressed exception?

When an exception is already being thrown and another one occurs while cleaning up (typically in close()), the second is attached to the first with addSuppressed instead of replacing it. You read them with getSuppressed(), and stack traces show them under “Suppressed:”.

What is the difference between AutoCloseable and Closeable?

AutoCloseable (java.lang, Java 7) has void close() throws Exception. Closeable (java.io, older) extends it with close() throws IOException and its close is expected to be idempotent: calling it twice has no extra effect. Implementations may narrow the throws clause or remove it entirely.

Can we use catch and finally with try-with-resources?

Yes, both are optional. They run after all resources have been closed, so in the catch block the resources are no longer usable, and the catch also receives exceptions thrown by the resource constructors or by close().

What happens if a resource is null?

Nothing special: Java checks for null before calling close, so a null resource is simply not closed and no NullPointerException is thrown by the statement itself. Using the null variable inside the block would still throw, of course.

Practice problems

Practise opening resources in try-with-resources, predicting the order in which things open, run, close and get caught, and reading suppressed exceptions. Readers over in-memory text stand in for files so every program runs anywhere. The last three are real business tasks.

P1Easy

Your own AutoCloseable

Write a class Session that implements AutoCloseable and prints when it opens and closes. Use it in a try-with-resources block.

Input
session name "billing"
Expected output
open billing
working in billing
close billing
after try
Show solution and explanation
class Session implements AutoCloseable {
    private final String name;

    Session(String name) {
        this.name = name;
        System.out.println("open " + name);
    }

    void work() {
        System.out.println("working in " + name);
    }

    @Override
    public void close() {
        System.out.println("close " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        try (Session s = new Session("billing")) {
            s.work();
        }
        System.out.println("after try");
    }
}
open billing working in billing close billing after try

How it works: any class that implements AutoCloseable (one method: close()) can be used in the parentheses after try. Java calls close() automatically when the block ends. Our close doesn't throw checked exceptions, so no catch is needed.

P2Easy

Two resources, reverse order

Open two resources in one try-with-resources statement and predict the order of the open and close messages.

Input
resources "connection" then "statement"
Expected output
open connection
open statement
run query
close statement
close connection
Show solution and explanation
class Res implements AutoCloseable {
    private final String name;

    Res(String name) {
        this.name = name;
        System.out.println("open " + name);
    }

    @Override
    public void close() {
        System.out.println("close " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        try (Res con = new Res("connection");
             Res st = new Res("statement")) {
            System.out.println("run query");
        }
    }
}
open connection open statement run query close statement close connection

How it works: resources open from left to right and close in the reverse order, like plates on a stack. That's what you want: the statement depends on the connection, so it must be closed first, while the connection is still open.

P3Easy

Count lines with a BufferedReader

Read text line by line with a BufferedReader (here over a StringReader, standing in for a file) and count non-empty lines.

Input
text = "Asha\nRavi\n\nMeena\n"
Expected output
Names: 3
Show solution and explanation
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class Main {
    public static void main(String[] args) {
        String text = "Asha\nRavi\n\nMeena\n";
        int count = 0;
        try (BufferedReader br = new BufferedReader(new StringReader(text))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (!line.isEmpty()) count++;
            }
        } catch (IOException e) {
            System.out.println("Read failed: " + e.getMessage());
        }
        System.out.println("Names: " + count);
    }
}
Names: 3

How it works: BufferedReader implements Closeable, which extends AutoCloseable. Its readLine() and close() declare the checked IOException, so a catch (or throws) is required. With a real file you would write new BufferedReader(new FileReader("names.txt")); everything else stays the same.

P4Medium

Close happens before catch

The body of the try throws. Predict the order of “use”, “close”, “catch” and “finally” messages.

Input
resource "printer"; printing fails
Expected output
1. printer on
2. printing
3. printer off
4. catch: paper jam
5. finally
Show solution and explanation
class Printer implements AutoCloseable {
    Printer() { System.out.println("1. printer on"); }

    void print() {
        System.out.println("2. printing");
        throw new IllegalStateException("paper jam");
    }

    @Override
    public void close() { System.out.println("3. printer off"); }
}

public class Main {
    public static void main(String[] args) {
        try (Printer p = new Printer()) {
            p.print();
            System.out.println("never printed");
        } catch (IllegalStateException e) {
            System.out.println("4. catch: " + e.getMessage());
        } finally {
            System.out.println("5. finally");
        }
    }
}
1. printer on 2. printing 3. printer off 4. catch: paper jam 5. finally

How it works: resources are closed as soon as the try block ends, normally or by an exception, and before any catch or finally of the same statement runs. So in the catch block the printer is already off. This is a common viva question.

P5Medium

Read the suppressed exception

The body throws, and then close() throws too. Print the main exception and every suppressed one.

Input
upload fails; closing the connection also fails
Expected output
Main problem: upload failed at 60%
  suppressed: connection reset while closing
Show solution and explanation
class Upload implements AutoCloseable {
    void send() {
        throw new IllegalStateException("upload failed at 60%");
    }

    @Override
    public void close() {
        throw new IllegalStateException("connection reset while closing");
    }
}

public class Main {
    public static void main(String[] args) {
        try (Upload u = new Upload()) {
            u.send();
        } catch (IllegalStateException e) {
            System.out.println("Main problem: " + e.getMessage());
            for (Throwable s : e.getSuppressed()) {
                System.out.println("  suppressed: " + s.getMessage());
            }
        }
    }
}
Main problem: upload failed at 60% suppressed: connection reset while closing

How it works: the body's exception is the important one, so Java keeps it as the main exception. The exception from close() is not lost; it is attached with addSuppressed, and getSuppressed() returns all such extras (Java 7+). Stack traces print them under “Suppressed:”.

P6Medium

Manual finally can lose the real error

Compare a hand-written try/finally with try-with-resources when both the body and close() fail. Which exception reaches the catch in each case?

Input
body throws "save failed"; close throws "close failed"
Expected output
Manual finally: close failed, suppressed 0
try-with-resources: save failed, suppressed 1
Show solution and explanation
class Store implements AutoCloseable {
    void save() {
        throw new IllegalStateException("save failed");
    }

    @Override
    public void close() {
        throw new IllegalStateException("close failed");
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Store s = new Store();
            try {
                s.save();
            } finally {
                s.close();
            }
        } catch (IllegalStateException e) {
            System.out.println("Manual finally: " + e.getMessage()
                + ", suppressed " + e.getSuppressed().length);
        }

        try (Store s = new Store()) {
            s.save();
        } catch (IllegalStateException e) {
            System.out.println("try-with-resources: " + e.getMessage()
                + ", suppressed " + e.getSuppressed().length);
        }
    }
}
Manual finally: close failed, suppressed 0 try-with-resources: save failed, suppressed 1

How it works: in the manual version, the exception thrown inside finally replaces the one from the body, so the real cause, “save failed”, disappears completely. try-with-resources keeps “save failed” as the main exception and attaches “close failed” as suppressed. This is one of the main reasons the feature exists.

P7Medium

Java 9: an existing variable as the resource

Create the resource before the try statement and list just the variable in the parentheses. This works only if the variable is final or effectively final (Java 9+).

Input
a ledger created before the try
Expected output
ledger opened
added Rs 500
added Rs 250
ledger closed
done
Show solution and explanation
class Ledger implements AutoCloseable {
    Ledger() { System.out.println("ledger opened"); }

    void add(int amount) { System.out.println("added Rs " + amount); }

    @Override
    public void close() { System.out.println("ledger closed"); }
}

public class Main {
    public static void main(String[] args) {
        Ledger ledger = new Ledger();
        try (ledger) {
            ledger.add(500);
            ledger.add(250);
        }
        System.out.println("done");
    }
}
ledger opened added Rs 500 added Rs 250 ledger closed done

How it works: before Java 9 you had to declare a new variable inside the parentheses. Now an existing variable is allowed as long as it is never reassigned (effectively final). Assigning ledger = new Ledger(); anywhere would make javac reject it: variable ledger used as a try-with-resources resource neither final nor effectively final.

P8Medium

Scanner is a resource too

Add up the numbers in a line of text with a Scanner opened in try-with-resources.

Input
text = "120 80 45 255"
Expected output
Total: 500
Show solution and explanation
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        int total = 0;
        try (Scanner sc = new Scanner("120 80 45 255")) {
            while (sc.hasNextInt()) {
                total += sc.nextInt();
            }
        }
        System.out.println("Total: " + total);
    }
}
Total: 500

How it works: Scanner implements Closeable, and its close() doesn't throw checked exceptions, so no catch is needed. Be careful with new Scanner(System.in): closing it also closes System.in, which you usually don't want in a program that reads input again later.

P9Business problem

Import a bank statement

A bank statement arrives as CSV text (date, description, amount). Read it with a BufferedReader in try-with-resources, skip the header, add up valid amounts and list bad rows. (A StringReader stands in for the uploaded file.)

Input
date,desc,amount
01-07,Salary,52000
03-07,Rent,-18000
05-07,Groceries,-2,450
09-07,UPI refund,350
Expected output
Bad row "05-07,Groceries,-2,450": expected 3 fields, got 4
Net change: Rs 34350 (1 bad rows)
Show solution and explanation
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class Main {
    public static void main(String[] args) {
        String csv = "date,desc,amount\n01-07,Salary,52000\n03-07,Rent,-18000\n"
                   + "05-07,Groceries,-2,450\n09-07,UPI refund,350\n";
        long net = 0;
        int bad = 0;
        try (BufferedReader br = new BufferedReader(new StringReader(csv))) {
            br.readLine();                      // skip header
            String line;
            while ((line = br.readLine()) != null) {
                String[] f = line.split(",");
                try {
                    if (f.length != 3) throw new IllegalArgumentException("expected 3 fields, got " + f.length);
                    net += Long.parseLong(f[2]);
                } catch (IllegalArgumentException e) {
                    bad++;
                    System.out.println("Bad row \"" + line + "\": " + e.getMessage());
                }
            }
        } catch (IOException e) {
            System.out.println("Could not read statement: " + e.getMessage());
        }
        System.out.println("Net change: Rs " + net + " (" + bad + " bad rows)");
    }
}
Bad row "05-07,Groceries,-2,450": expected 3 fields, got 4 Net change: Rs 34350 (1 bad rows)

How it works:

  1. The reader is closed automatically however the loop ends, so a long-running import service never leaks file handles.
  2. The inner try handles one row; the outer try-with-resources handles reading. “-2,450” contains a comma, so the row splits into 4 fields and is rejected.
  3. NumberFormatException is a subclass of IllegalArgumentException, so the same catch would also handle a non-numeric amount.
P10Business problem

Payment retries, one session per attempt

Each payment attempt opens a GatewaySession that must be closed whether the attempt works or not. The gateway fails twice, then succeeds. Retry up to 3 times.

Input
amount Rs 799; failures on attempts 1 and 2
Expected output
Attempt 1
  session 1 opened
  session 1 closed
  failed: timeout
Attempt 2
  session 2 opened
  session 2 closed
  failed: timeout
Attempt 3
  session 3 opened
  charged Rs 799
  session 3 closed
Payment done
Show solution and explanation
class GatewaySession implements AutoCloseable {
    private final int id;

    GatewaySession(int id) {
        this.id = id;
        System.out.println("  session " + id + " opened");
    }

    void charge(int amount, boolean fail) {
        if (fail) throw new IllegalStateException("timeout");
        System.out.println("  charged Rs " + amount);
    }

    @Override
    public void close() {
        System.out.println("  session " + id + " closed");
    }
}

public class Main {
    public static void main(String[] args) {
        boolean paid = false;
        for (int attempt = 1; attempt <= 3 && !paid; attempt++) {
            System.out.println("Attempt " + attempt);
            try (GatewaySession s = new GatewaySession(attempt)) {
                s.charge(799, attempt < 3);
                paid = true;
            } catch (IllegalStateException e) {
                System.out.println("  failed: " + e.getMessage());
            }
        }
        System.out.println(paid ? "Payment done" : "Payment failed");
    }
}
Attempt 1 session 1 opened session 1 closed failed: timeout Attempt 2 session 2 opened session 2 closed failed: timeout Attempt 3 session 3 opened charged Rs 799 session 3 closed Payment done

How it works:

  1. Every attempt gets its own session, and every session is closed, even the failed ones: notice “closed” is printed before “failed”.
  2. Without automatic closing, each failed attempt could leave a connection open on the gateway, and under load the pool would run out.
  3. paid = true sits after the charge, so it is only reached when the charge succeeds.
P11Business problem

Load app settings with Properties

Load key=value settings with java.util.Properties from a reader in try-with-resources. Use defaults for missing keys and report a broken number clearly.

Input
settings text:
maxCart=25
currency=INR
timeoutSec=3O
Expected output
timeoutSec is not a number, using 30
maxCart=25, region=IN-West, timeout=30
Show solution and explanation
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;

public class Main {
    public static void main(String[] args) {
        String text = "maxCart=25\ncurrency=INR\ntimeoutSec=3O\n";
        Properties p = new Properties();
        try (StringReader in = new StringReader(text)) {
            p.load(in);
        } catch (IOException e) {
            System.out.println("Cannot read settings: " + e.getMessage());
        }
        int maxCart = Integer.parseInt(p.getProperty("maxCart", "10"));
        String region = p.getProperty("region", "IN-West");
        int timeout;
        try {
            timeout = Integer.parseInt(p.getProperty("timeoutSec", "30"));
        } catch (NumberFormatException e) {
            System.out.println("timeoutSec is not a number, using 30");
            timeout = 30;
        }
        System.out.println("maxCart=" + maxCart + ", region=" + region + ", timeout=" + timeout);
    }
}
timeoutSec is not a number, using 30 maxCart=25, region=IN-West, timeout=30

How it works:

  1. Properties.load reads every key=value line; the reader is closed automatically right after.
  2. getProperty(key, default) handles missing keys (here region).
  3. “3O” has a letter O, so parsing it throws; the program falls back to a safe value and says so, instead of refusing to start or silently using garbage.

Real-world scenario questions (asked in company interviews)

Resource leaks rarely show up in testing and often show up in production. Decide your answer first, then open the model answer.

Ed-tech · fee portalFresher

“Cannot get a connection” on the last day

A portal works fine in testing, but on the busiest day it hangs with “Cannot get a connection from the pool”. The payment code is Connection c = pool.getConnection(); … process(c); c.close();. What is wrong and how do you fix it?

The interviewer is checking: do you recognise a resource leak on the exception path?

Show a strong answer
  • When process throws, c.close() is skipped and the connection is never returned. Each failure loses one; under load, the pool empties.
  • Fix: try (Connection c = pool.getConnection()) { process(c); }, which returns it on every path.
  • Add monitoring for active/idle pool connections and enable the pool's leak-detection logging to catch any remaining leaks.

Avoid: “increase the pool size”. It only delays the hang.

Banking · statement export1–3 years

The real error was hidden by the close

Logs for a failed statement export show only IOException: Stream closed from a finally block that closes the writer. The developer can't find why the export failed. The code uses try { … } finally { writer.close(); }. Explain and fix.

The interviewer is checking: do you know that finally can mask exceptions and how try-with-resources prevents it?

Show a strong answer
  • The export threw first (the real cause), then close() in finally threw too, and that second exception replaced the first.
  • Use try (BufferedWriter writer = …): the original exception is thrown, and “Stream closed” is attached as suppressed.
  • Make sure logging prints the full stack trace, which includes the “Suppressed:” section.

Avoid: wrapping close() in an empty catch to silence it. That hides problems such as data not flushed to disk.

Analytics · log processing1–3 years

“Too many open files”

A job processes thousands of log files with Files.lines(path).filter(…).count() in a loop. After a while it fails with FileSystemException: Too many open files. Why, when no reader is visible in the code?

The interviewer is checking: do you know that some APIs return resources that must be closed?

Show a strong answer
  • Files.lines returns a Stream that keeps the file open until the stream is closed. Counting doesn't close it, so each loop iteration leaks one file handle until the operating-system limit is reached.
  • Fix: try (Stream<String> lines = Files.lines(path)) { count = lines.filter(…).count(); }. Stream implements AutoCloseable.
  • Check the documentation of any method returning a stream, reader or connection: “must be closed” notes matter.

Avoid: raising the open-files limit as the fix.

Platform · library design3+ years

Designing a lock helper

Your team keeps forgetting to release distributed locks on order ids, leaving orders stuck. Design an API so the release can't be forgotten, and describe what close() should do if releasing fails.

The interviewer is checking: can you apply AutoCloseable to your own resources thoughtfully?

Show a strong answer
  • Return an OrderLock implements AutoCloseable from locks.acquire(orderId); callers write try (OrderLock l = locks.acquire(id)) { … }.
  • Make close() idempotent (safe to call twice) and narrow its throws clause (none, or a specific unchecked exception) so callers don't need a catch just for closing.
  • If release fails, throw a clear exception; when the body also failed it becomes suppressed, so neither problem is lost. Also give locks an expiry time as a safety net.

Avoid: a release() method that callers must remember to call in finally.