0

[Java Backend Zero to Hello] BÀI 2.8: I/O & FILE

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 2: Java Core Nâng Cao | Bài 23/86


BÀI 2.8: I/O & FILE

Mục tiêu

  • Đọc/ghi file với Java I/O
  • Sử dụng NIO.2 (Java 7+)
  • Thao tác với Path và Files
  • Serialization

1. HỆ THỐNG I/O

1.1 java.io (Cổ điển)

  • Stream-based
  • Blocking I/O
  • Phù hợp với file nhỏ

1.2 java.nio (New I/O)

  • Buffer-based
  • Non-blocking I/O
  • Hiệu năng cao hơn

1.3 java.nio.file (NIO.2 - Java 7+)

  • API hiện đại cho file
  • Hỗ trợ filesystem operations

2. JAVA.IO CƠ BẢN

2.1 Byte Stream

Đọc/ghi dữ liệu nhị phân.

import java.io.*;

// Đọc file
try (FileInputStream fis = new FileInputStream("data.bin");
     BufferedInputStream bis = new BufferedInputStream(fis)) {

    int byteData;
    while ((byteData = bis.read()) != -1) {
        System.out.print((char) byteData);
    }
}

// Ghi file
try (FileOutputStream fos = new FileOutputStream("output.bin");
     BufferedOutputStream bos = new BufferedOutputStream(fos)) {

    String data = "Hello World";
    bos.write(data.getBytes());
}

2.2 Character Stream

Đọc/ghi văn bản (tự xử lý encoding).

// Đọc file text
try (FileReader fr = new FileReader("data.txt");
     BufferedReader br = new BufferedReader(fr)) {

    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

// Ghi file text
try (FileWriter fw = new FileWriter("output.txt");
     BufferedWriter bw = new BufferedWriter(fw)) {

    bw.write("Hello World");
    bw.newLine();
    bw.write("Dòng 2");
}

2.3 PrintWriter

try (PrintWriter pw = new PrintWriter(new FileWriter("output.txt"))) {
    pw.println("Hello");
    pw.printf("Tuổi: %d%n", 25);
    pw.printf("Tên: %s%n", "An");
}

3. NIO.2 (JAVA 7+)

3.1 Path

import java.nio.file.Path;
import java.nio.file.Paths;

Path path1 = Paths.get("data.txt");
Path path2 = Paths.get("C:", "Users", "Admin", "data.txt");
Path path3 = Paths.get("C:\\Users\\Admin\\data.txt");

// Thông tin
path1.getFileName();      // data.txt
path1.getParent();        // null (file ở root)
path1.getRoot();          // null
path1.toAbsolutePath();   // Đường dẫn tuyệt đối
path1.toString();         // data.txt

3.2 Files - Đọc/Ghi

import java.nio.file.Files;
import java.nio.file.StandardOpenOption;

// Đọc tất cả
byte[] bytes = Files.readAllBytes(Paths.get("data.bin"));
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
String content = Files.readString(Paths.get("data.txt"));  // Java 11+

// Ghi
Files.write(Paths.get("output.txt"), "Hello".getBytes());
Files.write(Paths.get("output.txt"), lines);
Files.writeString(Paths.get("output.txt"), "Hello World");  // Java 11+

// Append
Files.write(Paths.get("log.txt"), "New line\n".getBytes(),
    StandardOpenOption.APPEND);

3.3 Files - Thao tác

// Copy
Files.copy(Paths.get("source.txt"), Paths.get("dest.txt"),
    StandardCopyOption.REPLACE_EXISTING);

// Move
Files.move(Paths.get("source.txt"), Paths.get("dest.txt"),
    StandardCopyOption.REPLACE_EXISTING);

// Delete
Files.delete(Paths.get("file.txt"));        // Ném exception nếu không tồn tại
Files.deleteIfExists(Paths.get("file.txt")); // Không ném exception

// Tạo
Files.createFile(Paths.get("new.txt"));
Files.createDirectory(Paths.get("newdir"));
Files.createDirectories(Paths.get("a/b/c"));  // Tạo cả cây thư mục

// Thông tin
boolean exists = Files.exists(Paths.get("file.txt"));
boolean isDir = Files.isDirectory(Paths.get("dir"));
long size = Files.size(Paths.get("file.txt"));
FileTime lastModified = Files.getLastModifiedTime(Paths.get("file.txt"));

3.4 Directory Stream

try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get("."))) {
    for (Path entry : stream) {
        System.out.println(entry.getFileName());
    }
}

// Filter
try (DirectoryStream<Path> stream = Files.newDirectoryStream(
        Paths.get("."), "*.txt")) {
    for (Path entry : stream) {
        System.out.println(entry.getFileName());
    }
}

3.5 Walk - Duyệt cây thư mục

// Duyệt tất cả file
Stream<Path> walk = Files.walk(Paths.get("."));
walk.filter(Files::isRegularFile)
    .forEach(System.out::println);

// Tìm file
try (Stream<Path> walk = Files.walk(Paths.get("."))) {
    List<Path> javaFiles = walk
        .filter(p -> p.toString().endsWith(".java"))
        .collect(Collectors.toList());
}

4. SERIALIZATION

Chuyển object thành byte stream để lưu trữ hoặc truyền qua mạng.

4.1 Serializable

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private transient String password;  // Không serialize

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

4.2 Serialize

User user = new User("An", 25);

try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("user.ser"))) {
    oos.writeObject(user);
}

4.3 Deserialize

try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("user.ser"))) {
    User user = (User) ois.readObject();
    System.out.println(user.getName());
}

5. ĐỌC/GHI CSV

// Đọc CSV
List<String[]> data = Files.readAllLines(Paths.get("data.csv"))
    .stream()
    .map(line -> line.split(","))
    .collect(Collectors.toList());

// Ghi CSV
List<String> lines = users.stream()
    .map(u -> u.getId() + "," + u.getName() + "," + u.getEmail())
    .collect(Collectors.toList());
Files.write(Paths.get("users.csv"), lines);

6. ĐỌC/GHI JSON (với thư viện)

Trong thực tế, dùng Jackson hoặc Gson:

// Với Jackson
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("user.json"), User.class);
mapper.writeValue(new File("user.json"), user);

7. BÀI TẬP THỰC HÀNH

Bài 1: Đọc file log

public List<String> readLogFile(String path) throws IOException {
    return Files.readAllLines(Paths.get(path));
}

public List<String> filterErrors(String path) throws IOException {
    return Files.lines(Paths.get(path))
        .filter(line -> line.contains("ERROR"))
        .collect(Collectors.toList());
}

Bài 2: Copy thư mục

public void copyDirectory(Path source, Path target) throws IOException {
    Files.walk(source).forEach(src -> {
        Path dest = target.resolve(source.relativize(src));
        try {
            Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    });
}

Bài 3: Tìm file lớn nhất

public Optional<Path> findLargestFile(Path dir) throws IOException {
    try (Stream<Path> walk = Files.walk(dir)) {
        return walk
            .filter(Files::isRegularFile)
            .max(Comparator.comparingLong(p -> {
                try {
                    return Files.size(p);
                } catch (IOException e) {
                    return 0L;
                }
            }));
    }
}

Bài 4: Word count

public Map<String, Long> wordCount(Path file) throws IOException {
    return Files.lines(file)
        .flatMap(line -> Arrays.stream(line.split("\\s+")))
        .collect(Collectors.groupingBy(
            String::toLowerCase,
            Collectors.counting()
        ));
}

8. TÓM TẮT

API Mô tả
FileInputStream / FileOutputStream Byte stream
FileReader / FileWriter Character stream
BufferedReader / BufferedWriter Buffered (hiệu quả hơn)
Path / Paths Đường dẫn (NIO.2)
Files Thao tác file (NIO.2)
Serializable Tuần tự hóa object
ObjectInputStream / ObjectOutputStream Serialize/Deserialize

Bài tiếp theo: 2.9 Annotation & Reflection


🧭 Điều Hướng Series

⬅️ Bài trước: BÀI 2.7: EXECUTOR FRAMEWORK

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: BÀI 2.9: ANNOTATION & REFLECTION


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí