Direct answer
The recommended approach for modern I/O operations in Java is the NIO.2 (New Input/Output) API, introduced in Java 7, which provides a more powerful and flexible way to work with files and directories through classes such as Path, Files, and Paths, offering improved performance and functionality over the classic java.io package.
Input/Output in Java
Java offers two main I/O APIs: java.io package
(stream-based) and java.nio (New I/O, buffer-based).
In this article we'll explore both approaches.
What You Will Learn
- Byte and character streams
- File reading and writing
- BufferedReader and BufferedWriter
- NIO.2: Path and Files
- Object serialization
- I/O best practices
Classic I/O: java.io
Stream Hierarchy
| Type | Byte Stream | Character Stream |
|---|---|---|
| Input | InputStream | Reader |
| Output | OutputStream | Writer |
| File | FileInputStream/FileOutputStream | FileReader/FileWriter |
| Buffered | BufferedInputStream/BufferedOutputStream | BufferedReader/BufferedWriter |
Reading Text Files
import java.io.*;
public class StudentFileReader {
public static void readStudentList(String path) {
// try-with-resources for automatic closing
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
System.out.println(lineNumber + ": " + line);
lineNumber++;
}
} catch (FileNotFoundException e) {
System.err.println("File not found: " + path);
} catch (IOException e) {
System.err.println("Read error: " + e.getMessage());
}
}
public static void main(String[] args) {
readStudentList("students.txt");
}
}
Writing Text Files
import java.io.*;
import java.util.*;
public class GradeFileWriter {
public static void saveGrades(String path, Map<String, Integer> grades) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) {
writer.write("=== GRADE REGISTRY ===");
writer.newLine();
writer.newLine();
for (Map.Entry<String, Integer> entry : grades.entrySet()) {
String line = entry.getKey() + ": " + entry.getValue();
writer.write(line);
writer.newLine();
}
writer.newLine();
writer.write("Generated automatically");
System.out.println("File saved: " + path);
} catch (IOException e) {
System.err.println("Write error: " + e.getMessage());
}
}
// Append mode: adds to existing file
public static void appendGrade(String path, String student, int grade) {
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(path, true))) { // true = append
writer.write(student + ": " + grade);
writer.newLine();
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
public static void main(String[] args) {
Map<String, Integer> grades = new LinkedHashMap<>();
grades.put("John Smith", 85);
grades.put("Laura White", 92);
grades.put("Joseph Green", 78);
saveGrades("grades.txt", grades);
}
}
Byte Streams
import java.io.*;
public class FileCopier {
public static void copyFile(String source, String destination) {
try (
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(destination);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos)
) {
byte[] buffer = new byte[8192]; // 8KB buffer
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
System.out.println("File copied successfully");
} catch (IOException e) {
System.err.println("Copy error: " + e.getMessage());
}
}
// Read complete byte array
public static byte[] readAllBytes(String path) throws IOException {
try (FileInputStream fis = new FileInputStream(path)) {
return fis.readAllBytes(); // Java 9+
}
}
public static void main(String[] args) {
copyFile("original.pdf", "copy.pdf");
}
}
NIO.2: The Modern API
Introduced in Java 7, NIO.2 provides a more modern and powerful API for file and directory operations.
Path and Paths
import java.nio.file.*;
public class PathManager {
public static void main(String[] args) {
// Creating Paths
Path path1 = Path.of("documents", "students", "grades.txt");
Path path2 = Paths.get("/home/user/documents/students/grades.txt");
Path path3 = Path.of("./data/../documents/grades.txt");
// Path information
System.out.println("File name: " + path1.getFileName());
System.out.println("Directory: " + path1.getParent());
System.out.println("Root: " + path2.getRoot());
System.out.println("Absolute: " + path1.toAbsolutePath());
// Normalization (removes . and ..)
Path normalized = path3.normalize();
System.out.println("Normalized: " + normalized);
// Resolution (combining paths)
Path base = Path.of("/school");
Path relative = Path.of("students/john/grades.txt");
Path complete = base.resolve(relative);
System.out.println("Complete path: " + complete);
// Relativize
Path dir1 = Path.of("/school/students");
Path dir2 = Path.of("/school/teachers/history");
Path relPath = dir1.relativize(dir2);
System.out.println("Relative: " + relPath); // ../teachers/history
// Iterate components
for (Path component : path1) {
System.out.println("Component: " + component);
}
}
}
Files: File Operations
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class FileOperations {
public static void main(String[] args) throws Exception {
Path file = Path.of("students.txt");
// === WRITING ===
// Write complete string
String content = "John Smith\nLaura White\nJoseph Green";
Files.writeString(file, content);
// Write list of lines
List<String> lines = Arrays.asList(
"John Smith: 85",
"Laura White: 92",
"Joseph Green: 78"
);
Files.write(Path.of("grades.txt"), lines);
// Append
Files.writeString(file, "\nNew Student",
StandardOpenOption.APPEND);
// === READING ===
// Read complete string
String allContent = Files.readString(file);
System.out.println(allContent);
// Read all lines
List<String> allLines = Files.readAllLines(file);
for (String line : allLines) {
System.out.println(line);
}
// Stream of lines (lazy, for large files)
Files.lines(file)
.filter(l -> l.contains("Smith"))
.forEach(System.out::println);
// Read bytes
byte[] bytes = Files.readAllBytes(Path.of("document.pdf"));
}
}
import java.nio.file.*;
import java.nio.file.attribute.*;
public class FileDirectoryManagement {
public static void main(String[] args) throws Exception {
Path dir = Path.of("school/students");
Path file = dir.resolve("roster.txt");
// === CHECK EXISTENCE AND TYPE ===
System.out.println("Exists: " + Files.exists(file));
System.out.println("Is file: " + Files.isRegularFile(file));
System.out.println("Is directory: " + Files.isDirectory(dir));
System.out.println("Readable: " + Files.isReadable(file));
System.out.println("Writable: " + Files.isWritable(file));
// === CREATE DIRECTORIES ===
Files.createDirectory(Path.of("new_dir")); // Single
Files.createDirectories(dir); // With parents
// === CREATE FILE ===
if (!Files.exists(file)) {
Files.createFile(file);
}
// === COPY ===
Path copy = Path.of("school/backup/roster_backup.txt");
Files.createDirectories(copy.getParent());
Files.copy(file, copy, StandardCopyOption.REPLACE_EXISTING);
// === MOVE/RENAME ===
Path newLocation = Path.of("archive/roster.txt");
Files.createDirectories(newLocation.getParent());
Files.move(copy, newLocation,
StandardCopyOption.REPLACE_EXISTING);
// === DELETE ===
Files.delete(newLocation); // Exception if not exists
Files.deleteIfExists(newLocation); // Silent
// === ATTRIBUTES ===
BasicFileAttributes attrs = Files.readAttributes(
file, BasicFileAttributes.class
);
System.out.println("Size: " + attrs.size());
System.out.println("Created: " + attrs.creationTime());
System.out.println("Modified: " + attrs.lastModifiedTime());
}
}
Exploring Directories
import java.nio.file.*;
import java.io.IOException;
public class DirectoryExplorer {
public static void main(String[] args) throws IOException {
Path dir = Path.of("school");
// === LIST CONTENTS ===
// Direct listing (non-recursive)
System.out.println("=== Directory contents ===");
try (var stream = Files.list(dir)) {
stream.forEach(System.out::println);
}
// Recursive listing (walk)
System.out.println("\n=== Recursive walk ===");
try (var stream = Files.walk(dir)) {
stream.forEach(System.out::println);
}
// Walk with max depth
try (var stream = Files.walk(dir, 2)) {
stream.filter(Files::isRegularFile)
.forEach(System.out::println);
}
// === SEARCH FILES ===
// With glob pattern
System.out.println("\n=== .txt files ===");
try (var stream = Files.newDirectoryStream(dir, "*.txt")) {
for (Path file : stream) {
System.out.println(file);
}
}
// Find with predicate
System.out.println("\n=== Files > 1KB ===");
try (var stream = Files.find(dir, 10,
(path, attrs) -> attrs.isRegularFile() &&
attrs.size() > 1024)) {
stream.forEach(System.out::println);
}
}
}
Serialization
Serialization allows you to save Java objects to files and read them back later.
import java.io.*;
public class Student implements Serializable {
// UID for versioning
private static final long serialVersionUID = 1L;
private String name;
private String studentId;
private int enrollmentYear;
// transient = not serialized
private transient String temporaryPassword;
public Student(String name, String studentId, int enrollmentYear) {
this.name = name;
this.studentId = studentId;
this.enrollmentYear = enrollmentYear;
}
// Getters
public String getName() { return name; }
public String getStudentId() { return studentId; }
public int getEnrollmentYear() { return enrollmentYear; }
@Override
public String toString() {
return String.format("Student[%s, %s, %d]",
name, studentId, enrollmentYear);
}
}
import java.io.*;
import java.util.*;
public class SerializationManager {
// Save an object
public static void saveStudent(Student student, String file) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file))) {
oos.writeObject(student);
System.out.println("Student saved: " + file);
} catch (IOException e) {
System.err.println("Save error: " + e.getMessage());
}
}
// Load an object
public static Student loadStudent(String file) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file))) {
return (Student) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Load error: " + e.getMessage());
return null;
}
}
// Save list of objects
public static void saveRoster(List<Student> students, String file) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file))) {
oos.writeObject(students);
System.out.println("Roster saved: " + students.size() + " students");
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
@SuppressWarnings("unchecked")
public static List<Student> loadRoster(String file) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file))) {
return (List<Student>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Error: " + e.getMessage());
return Collections.emptyList();
}
}
public static void main(String[] args) {
// Save single student
Student john = new Student("John Smith", "STU001", 2024);
saveStudent(john, "john.dat");
// Load
Student loaded = loadStudent("john.dat");
System.out.println("Loaded: " + loaded);
// Save list
List<Student> students = Arrays.asList(
new Student("John Smith", "STU001", 2024),
new Student("Laura White", "STU002", 2024),
new Student("Joseph Green", "STU003", 2023)
);
saveRoster(students, "students.dat");
// Load list
List<Student> loadedList = loadRoster("students.dat");
loadedList.forEach(System.out::println);
}
}
Complete Example: School Registry System
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class SchoolRegistry {
private static final Path DATA_DIR = Path.of("school_data");
private static final Path STUDENTS_FILE = DATA_DIR.resolve("students.csv");
private static final Path GRADES_FILE = DATA_DIR.resolve("grades.csv");
public static void initialize() throws IOException {
Files.createDirectories(DATA_DIR);
if (!Files.exists(STUDENTS_FILE)) {
Files.writeString(STUDENTS_FILE, "studentId,name,class\n");
}
if (!Files.exists(GRADES_FILE)) {
Files.writeString(GRADES_FILE, "studentId,subject,grade,date\n");
}
}
public static void addStudent(String studentId, String name, String className)
throws IOException {
String line = String.format("%s,%s,%s%n", studentId, name, className);
Files.writeString(STUDENTS_FILE, line, StandardOpenOption.APPEND);
System.out.println("Student added: " + name);
}
public static void recordGrade(String studentId, String subject, int grade)
throws IOException {
String date = java.time.LocalDate.now().toString();
String line = String.format("%s,%s,%d,%s%n", studentId, subject, grade, date);
Files.writeString(GRADES_FILE, line, StandardOpenOption.APPEND);
System.out.println("Grade recorded: " + subject + " = " + grade);
}
public static void printStudents() throws IOException {
System.out.println("\n=== STUDENT ROSTER ===");
Files.lines(STUDENTS_FILE)
.skip(1) // Skip header
.forEach(line -> {
String[] parts = line.split(",");
if (parts.length >= 3) {
System.out.printf("%-10s %-20s %s%n",
parts[0], parts[1], parts[2]);
}
});
}
public static void printStudentGrades(String studentId) throws IOException {
System.out.println("\n=== GRADES for " + studentId + " ===");
Files.lines(GRADES_FILE)
.skip(1)
.filter(line -> line.startsWith(studentId + ","))
.forEach(line -> {
String[] parts = line.split(",");
if (parts.length >= 4) {
System.out.printf("%-15s %s (%s)%n",
parts[1], parts[2], parts[3]);
}
});
}
public static double calculateAverage(String studentId) throws IOException {
return Files.lines(GRADES_FILE)
.skip(1)
.filter(line -> line.startsWith(studentId + ","))
.mapToInt(line -> {
String[] parts = line.split(",");
return Integer.parseInt(parts[2]);
})
.average()
.orElse(0);
}
public static void backupData() throws IOException {
Path backupDir = DATA_DIR.resolve("backup_" +
java.time.LocalDate.now().toString());
Files.createDirectories(backupDir);
Files.copy(STUDENTS_FILE,
backupDir.resolve("students.csv"),
StandardCopyOption.REPLACE_EXISTING);
Files.copy(GRADES_FILE,
backupDir.resolve("grades.csv"),
StandardCopyOption.REPLACE_EXISTING);
System.out.println("Backup created: " + backupDir);
}
public static void main(String[] args) {
try {
// Initialize
initialize();
// Add students
addStudent("STU001", "John Smith", "3A");
addStudent("STU002", "Laura White", "3A");
addStudent("STU003", "Joseph Green", "3B");
// Record grades
recordGrade("STU001", "Mathematics", 85);
recordGrade("STU001", "English", 82);
recordGrade("STU001", "Computer Science", 92);
recordGrade("STU002", "Mathematics", 92);
recordGrade("STU002", "English", 88);
// Print report
printStudents();
printStudentGrades("STU001");
// Calculate average
double average = calculateAverage("STU001");
System.out.printf("%nAverage STU001: %.2f%n", average);
// Backup
backupData();
} catch (IOException e) {
System.err.println("I/O Error: " + e.getMessage());
}
}
}
Best Practices
Rules for Efficient I/O
- Use try-with-resources: Guaranteed automatic closing
- Use BufferedReader/Writer: Better performance
- Prefer NIO.2: More modern and clean API
- Handle exceptions: IOException is very common
- Specify encoding: Use StandardCharsets.UTF_8
- Use Files.lines() for large files: Lazy stream
- Always close streams: Avoid memory leaks
- Check existence before operating: Files.exists()
Conclusion
I/O in Java provides powerful tools for working with files and streams. The NIO.2 API is the recommended approach for modern code.
Key Points to Remember
- java.io: Classic streams, bytes and characters
- BufferedReader/Writer: Better performance for text
- NIO.2 Path: Modern path representation
- NIO.2 Files: Static utilities for common operations
- Serializable: For saving Java objects
- StandardOpenOption: Precise control over file opening
In the next article we'll explore functional programming in Java: lambdas, Stream API and Optional.
Related deep dive: 07 - Collections Framework: List, Set e Map.
Related deep dive: 08 - Gestione degli Errori ed Eccezioni.
Frequently asked questions
What are the two main I/O APIs offered by Java?
The two main I/O APIs in Java are package (stream-based) and New I/O (buffer-based).
Which API is recommended for modern code in Java?
NIO.2 API is the recommended approach for modern code.
What does Serialization allow you to do with Java objects?
Serialization allows you to save Java objects to files and read them back later.
When was the NIO.2 API introduced in Java?
The NIO.2 API was introduced in Java 7.
Commenti
Caricamento commenti...
Sign in to comment