CodeGym /Các khóa học /JAVA 25 SELF /Cơ bản về java.io và java.nio: khác biệt, tiến hóa của AP...

Cơ bản về java.io và java.nio: khác biệt, tiến hóa của API

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Java đã học cách làm việc với tệp như thế nào

Java và tệp: bắt đầu như thế nào

Ngày xưa, vào năm 1996 xa xôi, các nhà phát triển ngôn ngữ đã đề xuất cách đầu tiên để làm việc với tệp — gói java.io. Trong đó xuất hiện các lớp như File, FileInputStream, FileOutputStream, Reader, Writer và những lớp khác. Các lớp này cho phép thực hiện nhiều thao tác với tệp: kiểm tra tệp có tồn tại không, kích thước, ngày sửa đổi và làm việc với thư mục.

Nhưng, như thường thấy với những phiên bản đầu tiên, mọi thứ chưa hoàn hảo — nhiều thứ có thể làm thuận tiện và an toàn hơn.

Hạn chế của API cũ (java.io)

  • File — không phải tệp, mà là “nhãn”. Lớp File không biết đọc hay ghi nội dung. Nó mô tả đường dẫn và metadata. Để đọc/ghi cần các luồng riêng: FileInputStream/FileOutputStream hoặc Reader/Writer.
  • Làm việc với đường dẫn — đau đầu. Ghép đường dẫn thủ công kiểu "C:\\Users\\" + user + "\\Documents" thường hỏng khi chuyển giữa Windows và Linux.
  • Không hỗ trợ liên kết tượng trưng. API cũ không hiểu symbolic link và có thể xử lý không chính xác.
  • Hỗ trợ thuộc tính yếu. Khó lấy quyền truy cập, chủ sở hữu, cờ “ẩn” v.v.
  • Kém hiệu quả với tệp lớn. Các luồng cổ điển không cung cấp kịch bản tiện và nhanh cho khối dữ liệu lớn, thiếu I/O bất đồng bộ.

Sự ra đời của java.nio.file (NIO.2, Java 7)

Trong Java 7 xuất hiện gói mới: java.nio.file (thường gọi là NIO.2). Nó mang lại:

  • Path — trừu tượng hiện đại về đường dẫn (bao gồm cả đường dẫn bên trong ZIP, đám mây và hệ thống tệp mạng).
  • Files — tiện ích tĩnh để đọc, ghi, sao chép, xóa.
  • FileSystem — làm việc không chỉ với đĩa cục bộ mà còn với các hệ thống tệp khác.
  • Hỗ trợ liên kết tượng trưng, thuộc tính mở rộng, quyền truy cập.
  • I/O bất đồng bộ và hiệu năng được cải thiện cho dữ liệu lớn.
  • Làm việc thuận tiện với thư mục và API dạng stream.

NIO là viết tắt của “New Input/Output”, và dù đã hơn mười năm tuổi, đây vẫn là tiêu chuẩn hiện đại cho Java.

2. So sánh cách tiếp cận: java.io vs java.nio.file

Lớp File (java.io)

  • Biểu diễn đường dẫn tới tệp hoặc thư mục và metadata của chúng.
  • Không thể đọc/ghi nội dung tệp.
  • Cho phép biết sự tồn tại, kích thước, loại (tệp/thư mục), đường dẫn tuyệt đối, v.v.
import java.io.File;

public class ExampleIO {
    public static void main(String[] args) {
        File file = new File("example.txt");
        if (file.exists()) {
            System.out.println("Tệp tồn tại!");
            System.out.println("Kích thước: " + file.length() + " byte");
        }
    }
}

Lớp Path (java.nio.file)

  • Trừu tượng hiện đại về đường dẫn, kết hợp và chuẩn hóa một cách tự nhiên.
  • Có thể biểu diễn đường dẫn cục bộ, lưu trữ nén và mạng.
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ExampleNIOPath {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        if (Files.exists(path)) {
            System.out.println("Đã tìm thấy tệp!");
            System.out.println("Kích thước: " + Files.size(path) + " byte");
        }
    }
}

Lớp Files (java.nio.file)

  • Tập hợp phương thức tĩnh để đọc/ghi toàn bộ hoặc theo phần.
  • Sao chép, xóa, di chuyển tệp.
  • Tạo/xóa thư mục.
  • Truy cập thuộc tính mở rộng.
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class ExampleNIOFiles {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        List<String> lines = Files.readAllLines(path);
        for (String line : lines) {
            System.out.println(line);
        }
    }
}

Bảng so sánh

Chức năng
java.io.File
java.nio.file.Path + Files
Biểu diễn đường dẫn
Đọc/ghi nội dung Không Có (qua Files)
Làm việc với đường dẫn Bất tiện Thuận tiện (resolve, normalize)
Liên kết tượng trưng Không
Thuộc tính tệp Hạn chế Mở rộng
I/O bất đồng bộ Không Có (NIO.2)
Khả năng đa nền tảng Một phần Xuất sắc

3. Khi nào dùng cái gì?

API cũ (java.io): khi nào cần?

  • Để hỗ trợ mã legacy. Nếu dự án bắt đầu trước Java 7 và dùng File, FileInputStream, FileOutputStream ở khắp nơi, có thể bạn sẽ phải giữ tương thích.
  • Trong dự án mới — không khuyến nghị. Dùng “cụ già” File ngày nay chẳng khác gì xem video từ băng VHS.

API mới (java.nio.file): tiêu chuẩn hiện đại

  • Luôn ưu tiên PathFiles trong các dự án mới.
  • Đơn giản hơn, an toàn hơn, mạnh mẽ hơn và tích hợp tốt với stream, lambda và try-with-resources.

Ghi nhớ nhanh

  • Đọc, ghi, sao chép, xóa? — qua Files.
  • Làm việc với đường dẫn (ghép, chuẩn hóa)? — qua Path.
  • Kích thước, ngày, quyền truy cập?Files.getAttribute() và các phương thức liên quan.
  • Duyệt thư mục, đệ quy?Files.walk, Files.list.

4. Ví dụ: mã sẽ trông ra sao với API cũ và mới

Ví dụ 1: Kiểm tra sự tồn tại của tệp

Cách cũ:

import java.io.File;

File file = new File("data.txt");
if (file.exists()) {
    System.out.println("Đã tìm thấy tệp!");
}

Cách mới:

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

Path path = Paths.get("data.txt");
if (Files.exists(path)) {
    System.out.println("Đã tìm thấy tệp!");
}

Ví dụ 2: Đọc tất cả các dòng của tệp

Cách cũ:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

File file = new File("data.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
reader.close();

Cách mới:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

Path path = Paths.get("data.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
    System.out.println(line);
}

Nhận xét: Cách mới ngắn gọn và an toàn hơn; với try-with-resources việc quản lý tài nguyên còn đơn giản hơn.

Ví dụ 3: Ghi một chuỗi vào tệp

Cách cũ:

import java.io.FileWriter;

FileWriter writer = new FileWriter("output.txt");
writer.write("Xin chào, tệp!");
writer.close();

Cách mới:

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

Path path = Paths.get("output.txt");
Files.write(path, "Xin chào, tệp!".getBytes());

5. Những điểm hữu ích

Tại sao Java quyết định làm lại mọi thứ?

  • Bảo mật và độ tin cậy. Ít lỗi hơn với việc đóng tài nguyên và xử lý đường dẫn.
  • Đa nền tảng. Các lớp thống nhất cho Windows, Linux và cả kho lưu trữ ZIP.
  • Dễ mở rộng. Dễ bổ sung hỗ trợ hệ thống tệp đám mây và mạng.
  • Hiệu năng. Hành vi tốt hơn trên tệp lớn và các thao tác bất đồng bộ.
  • Tương thích với hệ sinh thái Java hiện đại. Lambda, stream, try-with-resources.

Chuyển từ API cũ sang API mới như thế nào?

Hầu như luôn có thể chuyển đổi File thành Path và ngược lại:

File file = new File("data.txt");
Path path = file.toPath();
File file2 = path.toFile();

Làm gì khi gặp mã cũ?

  • Đừng hoảng; có thể làm mọi thứ đơn giản và hiện đại hơn.
  • Nếu có thể — viết lại sang java.nio.file.
  • Nếu không thể — dùng cầu nối (toPath(), toFile()) và di trú dần dần.

6. Ví dụ kết thúc: ứng dụng mini

Ví dụ: Kiểm tra tệp có tồn tại không và in kích thước của nó.

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

public class FileInfo {
    public static void main(String[] args) {
        Path path = Paths.get("test.txt");
        if (Files.exists(path)) {
            try {
                long size = Files.size(path);
                System.out.println("Đã tìm thấy tệp. Kích thước: " + size + " byte");
            } catch (Exception e) {
                System.out.println("Lỗi khi lấy kích thước tệp: " + e.getMessage());
            }
        } else {
            System.out.println("Không tìm thấy tệp!");
        }
    }
}

Chúng ta đã dùng gì ở đây:

  • Path — biểu diễn đường dẫn.
  • Files.exists() — kiểm tra tồn tại.
  • Files.size() — lấy kích thước.

7. Những lỗi thường gặp khi làm việc với API tệp

Lỗi №1: Mong rằng File có thể đọc hoặc ghi nội dung. Thực ra File chỉ là “nhãn”. Để đọc/ghi hãy dùng FileInputStream/FileOutputStream (API cũ) hoặc tiện ích Files (API mới).

Lỗi №2: Tự nối đường dẫn bằng + hoặc dấu gạch chéo. Điều này dẫn đến lỗi trên các hệ điều hành khác nhau. Hãy dùng Path.resolve() hoặc Paths.get(...) với nhiều đối số.

Lỗi №3: Quên đóng luồng. Trong API cũ, đây là nguyên nhân phổ biến gây rò rỉ tài nguyên. Trong API mới, nhiều thao tác qua Files không tạo luồng; nơi cần luồng — hãy dùng try-with-resources.

Lỗi №4: Dùng API cũ trong dự án mới. Nếu bắt đầu dự án mới — hãy chọn java.nio.file. Chỉ nên quay về java.io khi cần tương thích với mã legacy.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION