CodeGym /Courses /JAVA 25 SELF /Using try-with-resources: automatic resource closing

Using try-with-resources: automatic resource closing

JAVA 25 SELF
Level 36 , Lesson 4
Available

1. Introduction

Working with files in Java (and not only!) is always working with external resources. When you open a file, the operating system allocates a “descriptor” — a special identifier that allows your program to read from and write to the file. The number of such descriptors is limited: if you don’t close files, your program can quickly consume all available resources and start throwing mysterious errors like "Too many open files".

Moreover, if a file isn’t closed, it may remain locked for other programs. For example, you opened a file for writing, forgot to close it, and now neither you nor anyone else can modify or delete it. A kind of “never-ending hostage situation” in the world of file systems.

Real-world example

FileInputStream fis = new FileInputStream("data.txt");
int b = fis.read();
// ... do something, then forgot fis.close()

If you don’t call the close() method, the file will remain “hanging” until the program finishes. In large applications, this can lead to resource leaks and even to application crashes.

2. The old way: finally + close()

Before Java 7, the classic way to guarantee closing a file looked like this:

FileInputStream fis = null;
try {
    fis = new FileInputStream("data.txt");
    // read the file
    int b = fis.read();
    // ...
} catch (IOException e) {
    System.out.println("Error reading file: " + e.getMessage());
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            System.out.println("Error closing file: " + e.getMessage());
        }
    }
}

Drawbacks of this approach

  • It’s easy to forget the finally block and end up with resource leaks.
  • Lots of boilerplate, especially if there are multiple streams.
  • If an exception occurs during closing, you also need to catch it separately.
  • The code becomes bulky and less readable.

3. The modern approach: try-with-resources

Fortunately, Java 7 introduced syntax that solves these problems elegantly and automatically — try-with-resources.

What it looks like

try (FileInputStream fis = new FileInputStream("data.txt")) {
    int b = fis.read();
    // work with the file
} catch (IOException e) {
    System.out.println("Error working with file: " + e.getMessage());
}
// fis is already closed automatically here!

The key idea: all resources declared in the parentheses after try are automatically closed after the block finishes — even if an exception occurs in the middle. No need to write finally, no need to catch separate closing errors — Java will handle it for you.

Which classes can you use in try-with-resources?

Any class that implements the AutoCloseable interface (or the older Closeable). This is almost all standard I/O streams: FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, Scanner, PrintWriter, and many others.

4. try-with-resources syntax: details and examples

Single resource

try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Error: " + e.getMessage());
}
// reader is closed automatically!

Multiple resources

You can declare multiple resources separated by semicolons:

try (
    BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
    BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))
) {
    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.newLine();
    }
} catch (IOException e) {
    System.out.println("Error copying: " + e.getMessage());
}
// both streams are closed!

Close order: resources are closed in the reverse order of their declaration. writer.close() is called first, then reader.close(). This matters if one stream depends on another.

Using custom classes

If you’re writing your own class that works with resources, simply implement the AutoCloseable interface:

class MyResource implements AutoCloseable {
    public void doSomething() {
        System.out.println("Working with the resource!");
    }

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

try (MyResource res = new MyResource()) {
    res.doSomething();
}
// After exiting the block, it will print: "Resource closed!"

5. How it works: diagram

flowchart TD
    A[Opening a resource in try-with-resources] --> B{Did an exception occur in the try block?}
    B -- No --> C[Resource is closed automatically]
    B -- Yes --> D[Resource is closed automatically]
    D --> E[Exception is propagated]
    C --> F[Program continues execution]

Conclusion: regardless of whether there was an error, the resource will always be closed!

6. Examples: rewriting code “the new way”

Before (old style):

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("input.txt"));
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Error: " + e.getMessage());
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            System.out.println("Error while closing: " + e.getMessage());
        }
    }
}

After (try-with-resources):

try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Error: " + e.getMessage());
}
// That's it, no finally!

7. What happens if an error occurs during closing?

Sometimes the close operation itself can throw an exception (for example, if the file suddenly disappears). In try-with-resources, such exceptions are not lost: if there was already an exception in the try block and a second one occurred while closing the resource, it will be added as a suppressed exception to the primary one. You can see this using the Throwable.getSuppressed() method.

Example

try (MyResource res = new MyResource()) {
    throw new IOException("Error in try block");
} catch (IOException e) {
    System.out.println("Primary error: " + e.getMessage());
    for (Throwable suppressed : e.getSuppressed()) {
        System.out.println("Suppressed exception: " + suppressed.getMessage());
    }
}

8. Which classes support try-with-resources?

It’s simple: any class that implements the AutoCloseable interface. Here are just some of the standard ones:

Class Purpose
FileInputStream
Read bytes from a file
FileOutputStream
Write bytes to a file
FileReader/FileWriter
Read/write text
BufferedReader/Writer
Buffering streams
PrintWriter
Write formatted text
Scanner
Read data from a file/console
ObjectInputStream/Output
Serialization/deserialization
ZipInputStream/Output
Working with ZIP archives
Socket, ServerSocket
Network connections

If you use third-party libraries, check the documentation: if there’s a close() method, the class most likely supports try-with-resources.

9. Tips and useful nuances

You can declare variables outside try (since Java 9): you can use already declared resources if they are final or effectively final:

BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
try (reader) {
    // ...
}

It’s not just for files: try-with-resources is handy for any resources: network connections, databases, any objects with a close() method.

Don’t ignore exceptions: even with try-with-resources, don’t forget to catch and handle exceptions — it isn’t a silver bullet, just a convenient way to avoid leaks.

Don’t close the resource manually inside try: you don’t need to — Java will do it for you! If you call close() manually and then the try block ends, there will be an attempt to close an already closed resource. That’s usually safe but can be confusing.

10. Common mistakes when using try-with-resources

Error No. 1: forgot to use try-with-resources at all. If you’re still writing finally { resource.close(); } — you’re either in 2011 or you haven’t read this lecture! Use the modern syntax.

Error No. 2: declared the resource outside try and just use it inside. This code will not close the resource automatically:

BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
try {
    // ... using reader
} finally {
    // And here you forgot to close it!
}

Error No. 3: calling close() manually inside the try block. This isn’t critical, but it’s redundant and can lead to double closing. Just trust Java.

Error No. 4: catching only Exception and ignoring I/O specifics. It’s better to catch specific exceptions (FileNotFoundException, IOException) to give the user clear messages.

Error No. 5: not handling suppressed exceptions. If an error occurs while closing a resource, it may be suppressed. If you analyze errors, don’t forget about getSuppressed().

1
Task
JAVA 25 SELF, level 36, lesson 4
Locked
Sealing the message in stone with a magical seal ✨
Sealing the message in stone with a magical seal ✨
1
Task
JAVA 25 SELF, level 36, lesson 4
Locked
Counting ancient manuscripts in the library 📚
Counting ancient manuscripts in the library 📚
1
Survey/quiz
Reading and writing files, level 36, lesson 4
Unavailable
Reading and writing files
Reading and writing files
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION