Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

try-with-resources

A resource is an object that holds something external: an open file, a network connection, a database connection. Such objects must be closed, otherwise they stay occupied. The try-with-resources statement closes resources automatically — even if an exception happened inside.

Reading a file

Resources are declared in parentheses after try. When the block ends, Java calls their close() method itself. In this example the program first creates a file and then reads it line by line with a BufferedReader:

void main() throws IOException {
    Files.writeString(Path.of("todo.txt"), "Buy bread\nCall Oleg\n");
 
    try (BufferedReader reader = Files.newBufferedReader(Path.of("todo.txt"))) {
        String line;
        while ((line = reader.readLine()) != null) {
            IO.println("- " + line);
        }
    }
}

Output:

- Buy bread
- Call Oleg

There is no need for a finally with reader.close(): the file is closed whatever happens.

AutoCloseable

Any object that implements the AutoCloseable interface, with its single close() method, can go in the parentheses of try. Files, I/O streams and database connections implement it. You can implement it in your own class too, to see when closing happens:

class Connection implements AutoCloseable {
    Connection() {
        IO.println("Connection opened");
    }
 
    void send(String message) {
        IO.println("Sent: " + message);
    }
 
    @Override
    public void close() {
        IO.println("Connection closed");
    }
}
 
void main() {
    try (Connection connection = new Connection()) {
        connection.send("Hello");
    }
    IO.println("Done");
}

Output:

Connection opened
Sent: Hello
Connection closed
Done

Closing on an exception

The main advantage is that the resource is closed on an error too. And close() is called before the catch runs:

class Connection implements AutoCloseable {
    @Override
    public void close() {
        IO.println("Connection closed");
    }
}
 
void main() {
    try (Connection connection = new Connection()) {
        IO.println("Working");
        throw new IllegalStateException("Network failure");
    } catch (IllegalStateException e) {
        IO.println("Error: " + e.getMessage());
    }
}

Output:

Working
Connection closed
Error: Network failure

Several resources

Resources are separated by semicolons. They are closed in reverse order: the last one opened is closed first. This way a source file and its copy are closed in the right sequence:

class Resource implements AutoCloseable {
    private final String name;
 
    Resource(String name) {
        this.name = name;
        IO.println("Opened " + name);
    }
 
    @Override
    public void close() {
        IO.println("Closed " + name);
    }
}
 
void main() {
    try (Resource first = new Resource("A"); Resource second = new Resource("B")) {
        IO.println("Working with A and B");
    }
}

Output:

Opened A
Opened B
Working with A and B
Closed B
Closed A

Suppressed exceptions

What if both the try block and the close() method throw? The exception from try stays the main one, and the second isn't lost: it is attached as a suppressed exception and is available through getSuppressed():

class Faulty implements AutoCloseable {
    @Override
    public void close() {
        throw new IllegalStateException("error while closing");
    }
}
 
void main() {
    try (Faulty resource = new Faulty()) {
        throw new RuntimeException("error while working");
    } catch (RuntimeException e) {
        IO.println("Main: " + e.getMessage());
        IO.println("Suppressed: " + e.getSuppressed()[0].getMessage());
    }
}

Output:

Main: error while working
Suppressed: error while closing

Common mistake

The resource variable exists only inside the try block. Using it after the block is a compile error — by then the resource is already closed:

void main() throws IOException {
    Files.writeString(Path.of("data.txt"), "1\n2\n");
 
    try (BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"))) {
        IO.println(reader.readLine());
    }
    IO.println(reader.readLine());
}

Output:

Main.java:7: error: cannot find symbol
    IO.println(reader.readLine());
               ^
  symbol:   variable reader
  location: class Main
1 error
error: compilation failed

Do all the work with the resource inside the block and pass a finished result outside — a string, a list, a number.

Thanks for reading!

Donate to the site — we work hard for you.