Java try-with-resources

In the last lesson you learned about the Java finally block. But closing resources by hand in finally is still painful, with extra lines and null checks you can forget. Java gives us a cleaner tool that does the closing for you: try-with-resources.

πŸ€” Why do we need try-with-resources?

Say you open a file and read from it. You must close that file when you are done. With a plain finally, closing one file takes a lot of careful code:

  • You declare reader outside the try, just so finally can reach it.
  • You add a null check, because if the file failed to open, reader is still null and close() would crash.
  • The close() call itself can throw, which tangles things further.

What we actually want is to say β€œopen this, use it, and close it for me when I am done.” That is exactly what try-with-resources does.

🧩 What is try-with-resources?

Try-with-resources is a form of try where you declare the resource inside round brackets, like try (Resource r = ...). Java then automatically closes that resource the moment the block ends, whether it finished normally or threw an error.

This is the basic shape:

try (Resource r = openSomething()) {
// use r here
} // βœ… Java calls r.close() automatically right here

Here is what makes it special:

  • You declare the resource right inside try(...), not above the try.
  • Java closes it for you when the block ends, so there is no manual close line.
  • It closes even when an exception is thrown inside the block.
  • You do not need a finally just for closing, and you do not need null checks.

πŸ“‚ Before and after: reading a file

Let’s put the old way and the new way side by side, doing the same job. Both read the first line of a file.

This is the old way with a manual finally close. It works, but notice all the supporting code around the actual work.

import java.io.BufferedReader;
import java.io.FileReader;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
System.out.println("First line: " + line);
} finally {
if (reader != null) {
reader.close(); // ❌ manual close with a null check
}
}
}
}

Now the same thing with try-with-resources. The reader is declared in the brackets, so Java owns the closing.

import java.io.BufferedReader;
import java.io.FileReader;
public class Main {
public static void main(String[] args) throws Exception {
// βœ… reader is opened in try(...) and closed for us automatically
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
System.out.println("First line: " + line);
} // reader.close() happens here on its own, no finally needed
}
}

Output

First line: Hello from the file

Compare the two. The new version has no reader = null, no null check, and no finally. Same result, far less code, and nothing to forget.

πŸ”§ What can go in the brackets?

Not every object can sit in try(...). The resource must know how to close itself:

  • It has to implement AutoCloseable, an interface with one method called close().
  • A related one, Closeable, is the same idea aimed at input and output streams. You do not need to memorize the difference.
  • The everyday resources already support it: files, readers, writers, Scanner, database connections, network sockets.
  • So they all drop straight into a try-with-resources.

Here a Scanner is opened in the brackets and closed for you when the block ends.

import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// βœ… Scanner implements AutoCloseable, so it works here
try (Scanner scanner = new Scanner(System.in)) {
System.out.print("Enter your city: ");
String city = scanner.nextLine();
System.out.println("You live in " + city);
} // scanner.close() runs automatically
}
}

Output

Enter your city: Berlin
You live in Berlin

The Scanner is opened, used, and closed without a single manual close line.

πŸ”— Opening more than one resource

You often need two things open at once, like reading from one file and writing to another. You can declare several resources in the same try(...). Just separate them with a semicolon.

This opens a reader and a writer together, and both get closed for you.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class Main {
public static void main(String[] args) throws Exception {
// βœ… two resources in one try, both closed automatically
try (BufferedReader reader = new BufferedReader(new FileReader("in.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("out.txt"))) {
String line = reader.readLine();
writer.write(line);
System.out.println("Copied one line from in.txt to out.txt");
} // both are closed here, no finally needed
}
}

Output

Copied one line from in.txt to out.txt

One nice detail. When you open several resources, Java closes them in the reverse order it opened them. So the writer above closes first, then the reader. The last thing you opened often depends on the first thing, so unwinding backwards is the safe order. And you get it for free.

πŸͺ Combining with catch

Try-with-resources still works with catch. The closing happens first, then your catch runs. So the order is: use the resource, close it automatically, then handle any error.

This reads a file and catches the case where the file is missing.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("missing.txt"))) {
System.out.println(reader.readLine());
} catch (IOException e) {
// βœ… resource is already closed before we get here
System.out.println("Could not read the file: " + e.getMessage());
}
}
}

Output

Could not read the file: missing.txt (No such file or directory)

Read the flow. Java opens and uses the reader. The file is missing, so an IOException is thrown. Before jumping to catch, Java closes the reader. Then your catch runs and prints the message. So you get automatic closing and clean error handling in the same compact block.

πŸŒ€ Suppressed exceptions, briefly

What if the body throws an error, and then close() also throws while cleaning up? You now have two errors, but only one can travel up as the main one. Here is how Java handles it:

  • The error from your body wins. That is the one you really care about.
  • The error from close() is not thrown away. It is attached to the main one as a suppressed exception.
  • You can still see it later by calling getSuppressed().
  • This beats the old finally way, where a failing close() could hide the real error completely.

You rarely need to act on this, but it is good to know nothing is silently lost.

⚠️ Common Mistakes

A few try-with-resources slip-ups that catch people out. Each shows the wrong way and then the right way.

Still closing the resource by hand in finally. If the resource is in try(...), Java already closes it. Adding a manual close means you close it twice.

// ❌ wrong: double work, Java already closes it
try (Scanner scanner = new Scanner(System.in)) {
String name = scanner.nextLine();
} finally {
scanner.close(); // and this would not even compile, scanner is out of scope
}
// βœ… right: let try-with-resources do the closing
try (Scanner scanner = new Scanner(System.in)) {
String name = scanner.nextLine();
}

Trying to use something that is not AutoCloseable. Only classes that implement AutoCloseable can go in the brackets. A plain object that has no close() method will not compile here.

// ❌ wrong: a normal String is not AutoCloseable, this will not compile
try (String text = "hello") {
System.out.println(text);
}
// βœ… right: use a real closeable resource
try (Scanner scanner = new Scanner("hello")) {
System.out.println(scanner.next());
}

Using the resource after the block ends. Once the block closes, the resource is closed and its name is gone. You cannot touch it afterwards.

// ❌ wrong: scanner is closed and out of scope here
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("Inside");
}
scanner.nextLine(); // does not compile, scanner no longer exists
// βœ… right: do all your work inside the block
try (Scanner scanner = new Scanner(System.in)) {
String line = scanner.nextLine();
System.out.println(line);
}

βœ… Best Practices

Habits that make resource handling safe and tidy.

  • Prefer try-with-resources for anything closeable. Files, readers, writers, scanners, connections, sockets. If it can be closed, open it in the brackets.
  • Do not mix it with a manual close. Once it is in try(...), Java closes it. Adding your own close is wasted and confusing.
  • Open each resource in its own slot in the brackets. Separate them with a semicolon and let Java close them in reverse order.
  • Keep all the work inside the block. The resource only exists there, so do your reading and writing before the block ends.
  • Reach for a plain finally only for non-closeable cleanup. Resetting a flag or printing a final message still belongs in finally, not here.

🧩 What You’ve Learned

Nicely done. Let’s recap try-with-resources in clear points.

  • βœ… try-with-resources declares a resource in try(...) and closes it automatically when the block ends.
  • βœ… It closes the resource whether the block finishes normally or throws an error, so nothing is left open.
  • βœ… The resource must implement AutoCloseable (or Closeable), which most files, scanners, and connections already do.
  • βœ… It beats a manual finally close: less code, no forgotten close, no null checks.
  • βœ… You can open multiple resources in one try, and Java closes them in reverse order.
  • βœ… If both the body and close() fail, the body’s error wins and the close error is kept as a suppressed exception.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does try-with-resources do for you?

    Why: A resource declared in try(...) is closed automatically when the block ends, success or failure.

  2. 2

    What must a resource implement to be used in try-with-resources?

    Why: The resource must implement AutoCloseable (or Closeable), which provides the close() method Java calls.

  3. 3

    When you open multiple resources, in what order are they closed?

    Why: Resources are closed in the reverse order they were opened, which is the safe way to unwind them.

  4. 4

    Why is try-with-resources better than a manual finally close?

    Why: It removes the manual close, the null checks, and the extra finally, so there is nothing to forget.

πŸš€ What’s Next?

So far Java has thrown exceptions for us, and try-with-resources has cleaned up after them. But sometimes you want to throw an exception yourself, on purpose, when your code spots a problem like invalid input. The keyword for that is throw. Let’s learn it.

Java throw Keyword

Share & Connect