Exceptions and resources
Module 20 read files with a Scanner in try (...), and said that this form of try closes the file. This step looks at what that means, and when it happens.
Resources
An open file is a resource: something a program borrows from the operating system while it uses it, and gives back when it's done, by closing it. Until then the system keeps the file marked as in use. On some systems another program can't change or delete a file while it's open, and a program that opens files without closing them can run out of files it's allowed to have open at once. Everything a program has open is closed when it ends, but some programs, such as a web server, run for months.
A Scanner that reads a file has a method close(), and try-with-resources calls it for you:
try (Scanner scanner = new Scanner(Paths.get("settings.txt"))) {
// read with the scanner
} catch (Exception e) {
// the reading failed
}
- The resource is created in the parentheses after
try. It exists only inside thetryblock. - When the
tryblock ends, Java calls the resource'sclose(), however the block ends: after its last line, with areturn, or with an exception. - When an exception comes out of the
tryblock, the resource is closed first, and then thecatchblock runs. - When creating the resource fails, for example because there's no such file, there's no Scanner to close, and Java goes straight to the
catchblock. - A try-with-resources needs no
catchblock at all when its method passes the exception on withthrows IOException. The file is closed all the same, and the exception goes on to the caller.
Seeing it happen
Closing a Scanner prints nothing, so here's a class whose close() does. An object can be a resource when its class implements Java's interface AutoCloseable, which has one method, close(). The Scanner's class implements it, and so does this faucet, which turns the water on when it's created and off when it's closed:
public class Main {
public static void main(String[] args) {
try (Faucet faucet = new Faucet("kitchen")) {
System.out.println("Filling the kettle");
int cups = Integer.parseInt("four");
System.out.println("Filled " + cups + " cups");
} catch (NumberFormatException e) {
System.out.println("Error: " + e.getMessage());
}
System.out.println("Done");
}
}public class Faucet implements AutoCloseable {
private String room;
public Faucet(String room) {
this.room = room;
System.out.println("Water on in the " + room);
}
@Override
public void close() {
System.out.println("Water off in the " + this.room);
}
}Water on in the kitchen
Filling the kettle
Water off in the kitchen
Error: For input string: "four"
DoneThe water goes off before the catch block runs: the resource is closed as soon as the try block ends. (You won't need resources of your own in this course: Java's own, such as the Scanner, already implement AutoCloseable.)
Several resources can go in the parentheses, separated by semicolons. They're closed in the opposite order: the one created last is closed first.
Returning from inside
Because the resource is closed however the block ends, a method can return from the middle of a try-with-resources. This one returns the first line of a file that starts with a given text. When it finds the line, it returns at once, and the file is closed on the way out:
volume=7
theme=darkimport java.nio.file.Paths;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
System.out.println(findLine("settings.txt", "theme"));
System.out.println(findLine("settings.txt", "language"));
System.out.println(findLine("colors.txt", "theme"));
}
public static String findLine(String fileName, String start) {
try (Scanner scanner = new Scanner(Paths.get(fileName))) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.startsWith(start)) {
return line;
}
}
} catch (Exception e) {
return "no file " + fileName;
}
return "none";
}
}
theme=dark
none
no file colors.txtThe long way
Before try-with-resources came to Java (in version 7), programs closed their files themselves, in a finally block, so that it happened even after an exception:
Scanner scanner = new Scanner(Paths.get("settings.txt"));
try {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} finally {
scanner.close();
}
You'll still see this in older code. try-with-resources does the same with less to write, and nothing to forget.
Not the keyboard
One Scanner never goes in a try-with-resources: new Scanner(System.in). Closing it closes System.in too, and after that the program can't read the keyboard any more, not even with a new Scanner. The keyboard isn't a file the program borrowed, so leave that Scanner open.