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.
Sir, I did close the connection. It's the last line of the method!
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.
That sounds like a lot of code for one close.
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.
What is try-with-resources?
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.
“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
close(). Required for resources.close() throws IOException); extends AutoCloseable.The rules at a glance
| Syntax | try (A a = new A(); B b = new B()) { … }; catch and finally are optional |
|---|---|
| Requirement | The type must implement AutoCloseable, or javac says try-with-resources not applicable to variable type |
| Close order | Reverse of declaration: b closes before a |
| When | Right after the try block ends, before catch and finally run |
| Exceptions | Body's exception wins; close's exception is attached and read with getSuppressed() |
| Resource variables | Implicitly 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.
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.
LIFOClosing 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 leaksSuppressed, 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());
}
}
}Manual finally vs try-with-resources
Manual try { … } finally { r.close(); } | try (R r = …) { … } | |
|---|---|---|
| Lines of code | Many: declare outside, null check, nested try for close | One line in the header |
| Several resources | Nested try/finally blocks, easy to get the order wrong | Separated by ;, closed in reverse automatically |
| Body and close both throw | Close's exception replaces the body's; the real cause is lost | Body's exception kept; close's attached as suppressed |
| Forgetting to close | Possible (and common) | Impossible for declared resources |
| Available since | Java 1.0 | Java 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)).
Using try-with-resources, start to finish
Make sure the type is AutoCloseable
JDK resources already implement it:class PrinterSession implements AutoCloseable { public void close() { … } }FileReader,BufferedReader,Scanner, JDBCConnection,Statement,ResultSet, sockets, streams. AStringdoesn't, sotry (String s = "x")is a compile error.Declare the resources in the brackets
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.try (Conn c = new Conn(); Query q = new Query()) {Use them in the block
The resource variables are implicitly final:System.out.println(q.balanceOf("SB-1042")); }q = new Query();inside the block gives auto-closeable resource q may not be assigned.Java closes them in reverse
At the end of the block:
javac generates the code you would otherwise write by hand: calls toq.close(), thenc.close(). Then catch (if an exception is pending and matches), then finally.close()on every exit path, handlers of typeThrowable, and calls toThrowable.addSuppressed. You can see them withjavap -c.Read suppressed exceptions when you need them
Stack traces print suppressed exceptions indented under “Suppressed:”. In code,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)e.getSuppressed()returns them as an array (empty if there are none).
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.
- Open: the first resource, a database connection, is created.
- Open again: a statement is created using the connection. Both are now open.
- Failure: running the query throws an exception (a timeout). The rest of the block is skipped.
- Auto-close 1: before anything else, Java closes the statement, the last one opened.
- Auto-close 2: then the connection. Its
close()fails as well. - catch: the handler receives the timeout, the real problem, and can read the closing failure with
getSuppressed().
- The connection object #1 is created. Java remembers it as a resource of this statement.
- The statement #2 is created. Resources form a stack: the newest is on top.
- The body throws #3. Instead of leaving at once, the generated code catches it internally (as a
Throwable) and holds on to it. close()is called on the top of the stack, #2. It succeeds.close()on #1 throws #4. Because #3 is already pending, the generated code calls#3.addSuppressed(#4)instead of letting #4 replace it.- All resources are closed. #3 is rethrown and the catch block receives it, with #4 available from
getSuppressed().
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.
- First resource: the cash drawer opens. Java notes it must be closed at the end of the block.
- Second resource: the receipt printer is switched on. Both are listed in the try brackets, so neither needs a close call in our code.
- Failure: the printer runs out of paper in the middle of bill 7712 and throws a
PrinterException. The rest of the block is skipped. - Reverse order: before the exception goes anywhere, Java calls
p.close(): the last resource opened is closed first. - Drawer locked: then
d.close()locks the drawer. Even with a failed bill, no cash drawer is left open at the counter. - Catch: only after both are closed does the catch block run and show the cashier a helpful message. Had a close also failed, it would be attached as a suppressed exception.
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.
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.
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.
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.
Examples are simplified. Frameworks often manage connections for you, but underneath they rely on the same close-on-every-path guarantee.
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);
}
}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 usenew FileReader("fees.csv").catch (IOException e)Needed becausereadLine()andclose()declare the checked IOException.- No
finallyNothing to clean up by hand: that's the point.
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.
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.
account = SB-1042
connection open SB-1042: Rs 15250 query closed connection closed done
Approach
- Open both in one try-with-resources:
try (Conn c = new Conn(); Query q = new Query()). - Run the query inside the block.
- When the block ends, Java calls
q.close()and thenc.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.
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.
rows = {"2.5", "1,2", "4.0"}file closed after 2 rows Import failed: For input string: "1,2"
Approach
importAllopens the file in try-with-resources and adds up the weights.- Row 2 makes
Double.parseDoublethrow aNumberFormatException;importAllhas no catch. - Before the exception leaves
importAll, Java closes the file. Thenmain'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.
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.
bill no. 5531; printer jams; closing fails too
Cashier sees: Paper jam on bill 5531 Support log: Printer offline during close
Approach
PrinterSession.printthrows for the jam;close()throws as well.- try-with-resources keeps the jam as the main exception and attaches the close error with
addSuppressed. mainprints the main message and loops overgetSuppressed().
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.
Check yourself
Quick revision
Lesson 9.6 in one screen
try (Res r = …) { }closesrautomatically 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.
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.
session name "billing"
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");
}
}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.
Two resources, reverse order
Open two resources in one try-with-resources statement and predict the order of the open and close messages.
resources "connection" then "statement"
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");
}
}
}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.
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.
text = "Asha\nRavi\n\nMeena\n"
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);
}
}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.
Close happens before catch
The body of the try throws. Predict the order of “use”, “close”, “catch” and “finally” messages.
resource "printer"; printing fails
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");
}
}
}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.
Read the suppressed exception
The body throws, and then close() throws too. Print the main exception and every suppressed one.
upload fails; closing the connection also fails
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());
}
}
}
}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:”.
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?
body throws "save failed"; close throws "close failed"
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);
}
}
}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.
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+).
a ledger created before the try
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");
}
}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.
Scanner is a resource too
Add up the numbers in a line of text with a Scanner opened in try-with-resources.
text = "120 80 45 255"
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);
}
}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.
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.)
date,desc,amount 01-07,Salary,52000 03-07,Rent,-18000 05-07,Groceries,-2,450 09-07,UPI refund,350
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)");
}
}How it works:
- The reader is closed automatically however the loop ends, so a long-running import service never leaks file handles.
- 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.
NumberFormatExceptionis a subclass ofIllegalArgumentException, so the same catch would also handle a non-numeric amount.
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.
amount Rs 799; failures on attempts 1 and 2
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");
}
}How it works:
- Every attempt gets its own session, and every session is closed, even the failed ones: notice “closed” is printed before “failed”.
- Without automatic closing, each failed attempt could leave a connection open on the gateway, and under load the pool would run out.
paid = truesits after the charge, so it is only reached when the charge succeeds.
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.
settings text: maxCart=25 currency=INR timeoutSec=3O
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);
}
}How it works:
Properties.loadreads everykey=valueline; the reader is closed automatically right after.getProperty(key, default)handles missing keys (hereregion).- “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.
“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
processthrows,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.
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.
“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.linesreturns 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.
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 AutoCloseablefromlocks.acquire(orderId); callers writetry (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.