Real applications read configuration, write logs, and process uploaded files. Java's I/O classes handle all of this through the concept of streams - a sequence of data flowing in or out.
Writing to a file
try (BufferedWriter writer = new BufferedWriter(new FileWriter("notes.txt"))) {
writer.write("Learning Java I/O");
writer.newLine();
writer.write("Second line");
} catch (IOException e) {
System.out.println("Failed to write: " + e.getMessage());
}
Reading from a file
try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Failed to read: " + e.getMessage());
}
Why try-with-resources
The try (... = ...) syntax automatically closes the file (or any Closeable resource) when the block finishes, even if an exception is thrown - before this existed, developers had to remember to close resources manually in a finally block, and it was easy to forget and leak file handles.
Reading a file more simply
For smaller files, Files (from java.nio.file) offers a shorter option:
List<String> lines = Files.readAllLines(Path.of("notes.txt"));
lines.forEach(System.out::println);