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 OlegThere 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
DoneClosing 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 failureSeveral 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 ASuppressed 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 closingCommon 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 failedDo all the work with the resource inside the block and pass a finished result outside — a string, a list, a number.