1. Ôn tập: File vs Path vs Files
File xưa cũ nhưng thân thuộc
Vâng... Mã legacy trong các dự án Java không hề ít. Vì vậy hãy cùng nhắc lại về cựu binh Java — lớp File. Nó xuất hiện ngay từ phiên bản đầu tiên của ngôn ngữ và cho phép làm việc với tệp và thư mục: kiểm tra tệp có tồn tại hay không, lấy tên, kích thước, ngày sửa đổi, tạo và xóa tệp/thư mục. Như chúng ta đã biết, nó có nhiều hạn chế: một số phương thức hoạt động thiếu trực quan, không phải thao tác nào cũng được hỗ trợ đa nền tảng, và một số phương thức không ném exception mà chỉ trả về false (không phải lúc nào cũng thuận tiện cho việc gỡ lỗi).
import java.io.File;
File file = new File("example.txt");
System.out.println("Tệp có tồn tại không? " + file.exists());
System.out.println("Có phải là tệp không? " + file.isFile());
System.out.println("Có phải là thư mục không? " + file.isDirectory());
Path và Files: góc nhìn hiện đại (Java 7+)
Từ Java 7 xuất hiện gói java.nio.file, cung cấp các công cụ mạnh mẽ và linh hoạt hơn. Hai nhân vật chính:
- Path — một interface mô tả đường dẫn tới tệp hoặc thư mục. Có thể xem nó như “chuỗi thông minh”: biết phân tách, ghép, phân tích và so sánh đường dẫn.
- Files — lớp tiện ích với rất nhiều phương thức tĩnh để làm việc với tệp và thư mục: tạo, xóa, sao chép, đọc, ghi, lấy thông tin.
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("example.txt");
System.out.println("Đường dẫn: " + path);
Điều thú vị: lớp Path không chỉ là một chuỗi! Nó hiểu về dấu phân tách thư mục, biết xử lý đường dẫn tương đối và tuyệt đối, và hoạt động tốt như nhau trên Windows và Linux.
Bảng so sánh File và Path/Files
| Tiêu chí | File (API cũ) | Path + Files (API hiện đại) |
|---|---|---|
| Cú pháp | Phong cách OOP | Kết hợp Path + các phương thức tĩnh của Files |
| Tính đa nền tảng | Một phần | Rất tốt, có xét đến HĐH |
| Ngoại lệ | Không phải lúc nào cũng có | Gần như luôn có |
| Tính năng mới | Không | Có (thuộc tính, quyền, liên kết, v.v.) |
| Khuyến nghị | Không khuyến khích | Có |
2. Lấy thông tin về tệp/thư mục
Hãy tìm hiểu cách biết những điều quan trọng nhất về tệp hoặc thư mục: nó có tồn tại hay không, loại (tệp hay thư mục), kích thước, tên, đường dẫn và thư mục cha.
Kiểm tra sự tồn tại
Qua File
File file = new File("example.txt");
if (file.exists()) {
System.out.println("Tệp tồn tại!");
} else {
System.out.println("Không tìm thấy tệp.");
}
Qua Path và Files
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("example.txt");
if (Files.exists(path)) {
System.out.println("Tệp tồn tại!");
} else {
System.out.println("Không tìm thấy tệp.");
}
Mẹo: Với các dự án mới, luôn sử dụng Path và Files. Điều này an toàn hơn, tiện hơn và đa nền tảng.
Kiểm tra loại: tệp hay thư mục
File
if (file.isFile()) {
System.out.println("Đây là tệp.");
} else if (file.isDirectory()) {
System.out.println("Đây là thư mục.");
}
Path + Files
if (Files.isRegularFile(path)) {
System.out.println("Đây là tệp.");
} else if (Files.isDirectory(path)) {
System.out.println("Đây là thư mục.");
}
Lấy kích thước tệp
File
long size = file.length();
System.out.println("Kích thước tệp: " + size + " byte");
Path + Files
try {
long size = Files.size(path);
System.out.println("Kích thước tệp: " + size + " byte");
} catch (IOException e) {
System.out.println("Lỗi khi lấy kích thước tệp: " + e.getMessage());
}
Lưu ý: Với thư mục, phương thức length() luôn trả về 0, còn Files.size(path) sẽ ném IOException. Kích thước của một thư mục là tổng kích thước của tất cả các tệp bên trong (chi tiết sẽ có ở các bài sau).
Lấy đường dẫn tuyệt đối và tương đối
File
System.out.println("Đường dẫn tương đối: " + file.getPath());
System.out.println("Đường dẫn tuyệt đối: " + file.getAbsolutePath());
Path
System.out.println("Đường dẫn tương đối: " + path);
System.out.println("Đường dẫn tuyệt đối: " + path.toAbsolutePath());
Lấy tên tệp và thư mục cha
File
System.out.println("Tên tệp: " + file.getName());
System.out.println("Thư mục cha: " + file.getParent());
Path
System.out.println("Tên tệp: " + path.getFileName());
System.out.println("Thư mục cha: " + path.getParent());
Mẹo hay: Path có thể làm việc không chỉ với tệp mà còn với thư mục, với đường dẫn tương đối và tuyệt đối, và dễ dàng tách đường dẫn thành các phần.
3. Lấy thuộc tính tệp
Đôi khi bạn muốn biết không chỉ kích thước và tên tệp mà còn những điều thú vị hơn: ngày tạo, ngày sửa đổi gần nhất, quyền truy cập.
Ngày tạo, ngày sửa đổi gần nhất
File (bị hạn chế)
long lastModified = file.lastModified();
System.out.println("Ngày sửa đổi gần nhất: " + new java.util.Date(lastModified));
Nhược điểm: Không thể biết ngày tạo tệp thông qua File!
Path + Files
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
try {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
FileTime creationTime = attrs.creationTime();
FileTime lastModifiedTime = attrs.lastModifiedTime();
System.out.println("Ngày tạo: " + creationTime);
System.out.println("Ngày sửa đổi gần nhất: " + lastModifiedTime);
} catch (IOException e) {
System.out.println("Lỗi khi lấy thuộc tính: " + e.getMessage());
}
Kiểm tra quyền truy cập
File
System.out.println("Có thể đọc? " + file.canRead());
System.out.println("Có thể ghi? " + file.canWrite());
System.out.println("Có thể thực thi? " + file.canExecute());
Path + Files
System.out.println("Có thể đọc? " + Files.isReadable(path));
System.out.println("Có thể ghi? " + Files.isWritable(path));
System.out.println("Có thể thực thi? " + Files.isExecutable(path));
Điều thú vị: Trên Windows quyền truy cập bị hạn chế, còn trên các hệ thống Unix bạn có thể lấy và thay đổi nhiều quyền hơn (ví dụ, qua các thuộc tính POSIX).
4. Lỗi thường gặp khi lấy thông tin về tệp
Lỗi №1: Dùng File khi cần Path.
Nhiều người mới vẫn dùng File vì nó có vẻ đơn giản hoặc “trên mạng ai cũng làm thế”. Nhưng với Path và Files, bạn có thể làm tất cả những điều tương tự, nhưng đáng tin cậy và hiện đại hơn.
Lỗi №2: Chỉ kiểm tra loại đối tượng qua File.
Nếu bạn chỉ dùng file.isFile() và file.isDirectory(), bạn có thể bỏ sót các tệp “đặc biệt” (liên kết tượng trưng, thiết bị). Trong API hiện đại có Files.isRegularFile(path), Files.isDirectory(path) và các kiểm tra khác.
Lỗi №3: Bỏ qua exception.
Các phương thức Files.size(path) và Files.readAttributes(...) ném IOException. Nếu không xử lý exception, chương trình sẽ sập ngay khi gặp lỗi truy cập đầu tiên hoặc khi tệp biến mất trong lúc chạy.
Lỗi №4: Mong đợi có thể lấy kích thước thư mục trực tiếp.
Với thư mục, phương thức Files.size(path) sẽ ném exception. Để biết kích thước thư mục, cần đệ quy cộng kích thước của tất cả các tệp bên trong.
Lỗi №5: Kiểm tra quyền truy cập chưa đầy đủ.
Ngay cả khi tệp tồn tại, bạn có thể không có quyền đọc hoặc ghi. Luôn kiểm tra quyền trước khi thao tác: Files.isReadable(path), Files.isWritable(path), Files.isExecutable(path).
GO TO FULL VERSION