Skip to main content
Advertisement

8.4 try-with-resources and Advanced Exception Handling

Java 7's try-with-resources solves the hassle and mistakes that arise when you must always close resources after using them.

1. The Problem with Resource Management

Files, database connections, and network sockets must be closed after use. The old way required a finally block and was error-prone.

// ❌ Old style - verbose and error-prone
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try { reader.close(); } catch (IOException e) { e.printStackTrace(); }
}
}

2. try-with-resources (Java 7+)

Declare resources in try ( ... ) parentheses and they are automatically closed when the block exits — whether normally or via exception.

Requirement: The resource class must implement java.lang.AutoCloseable (or java.io.Closeable).

// ✅ try-with-resources - clean and safe!
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("File read error: " + e.getMessage());
}
// reader.close() is called automatically!

Managing Multiple Resources

try (
FileInputStream in = new FileInputStream("source.txt");
FileOutputStream out = new FileOutputStream("dest.txt")
) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("File copy complete!");
} catch (IOException e) {
e.printStackTrace();
}

Custom AutoCloseable

class DatabaseConnection implements AutoCloseable {
DatabaseConnection(String url) { System.out.println("DB connected: " + url); }
void query(String sql) { System.out.println("Query: " + sql); }
@Override
public void close() { System.out.println("DB connection closed"); }
}

try (DatabaseConnection conn = new DatabaseConnection("jdbc:mysql://localhost/shop")) {
conn.query("SELECT * FROM users");
} // conn.close() called automatically

3. Multi-catch (Java 7+)

Combine multiple exceptions in one catch block using the | operator.

// ❌ Old: duplicate catch blocks
try { /* ... */ }
catch (FileNotFoundException e) { handle(e); }
catch (ParseException e) { handle(e); }

// ✅ Multi-catch
try {
String data = readFile("config.txt");
int value = Integer.parseInt(data);
} catch (IOException | NumberFormatException e) {
System.out.println("Data processing error: " + e.getMessage());
}

4. Exception Chaining

Preserve the original cause when wrapping an exception.

class DataService {
void loadData(String path) throws DataLoadException {
try {
new FileReader(path);
} catch (FileNotFoundException e) {
throw new DataLoadException("Data file not found: " + path, e);
}
}
}

class DataLoadException extends Exception {
DataLoadException(String message, Throwable cause) { super(message, cause); }
}

try {
new DataService().loadData("missing.csv");
} catch (DataLoadException e) {
System.out.println("High-level error: " + e.getMessage());
System.out.println("Cause: " + e.getCause().getMessage());
}

5. Checked vs Unchecked Exceptions

TypeExamplesHandling
CheckedIOException, SQLExceptionMust use try-catch or declare throws
UncheckedNullPointerException, IllegalArgumentExceptionOptional
ErrorOutOfMemoryError, StackOverflowErrorGenerally not handled

Pro Tip

Real-world exception handling patterns:

  1. Checked → Unchecked wrapping: Wrap library-checked exceptions at API boundaries to avoid forcing callers to write try-catch.

    static String readFileSafely(String path) {
    try { return Files.readString(Path.of(path)); }
    catch (IOException e) { throw new RuntimeException("File read failed: " + path, e); }
    }
  2. Context in error messages: "File not found" is less useful than "Profile image for user ID 123 (/uploads/123.jpg) not found."

  3. Lambda-based resource helper pattern:

    static <T> T withDb(String url, ThrowingSupplier<T> action) {
    try (var conn = new DatabaseConnection(url)) { return action.get(); }
    catch (Exception e) { throw new RuntimeException(e); }
    }
Advertisement