Java: Gói java.nio


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Tổng quan

Gói API Input/Output mới (New Input/Output - NIO) của Java được giới thiệu vào năm 2002 với J2SE 1.4 để nâng cao các tác vụ xử lý input/output trong phát triển ứng dụng Java. Mặc dù, nó không được sử dụng đến mức tốt nhất của nó, Java SE 7 đã giới thiệu thêm các API New Input/Output (NIO.2).

Mục tiêu chính của cả NIO và NIO.2 vẫn giống nhau, đó là nâng cao các tác vụ xử lý I/O trong phát triển ứng dụng Java. Việc sử dụng nó có thể cắt giảm thời gian cần thiết cho các hoạt động I/O thông thường nhất định.

Lưu ý - Cả NIO và NIO.2 đều rất phức tạp để làm việc

Cả hai API NIO và NIO.2 đều hiển thị các điểm nhập hệ điều hành (OS) cấp thấp hơn. Chúng cũng cung cấp khả năng kiểm soát I/O tốt hơn. Một khía cạnh khác của NIO là sự chú ý đến tính biểu hiện của ứng dụng.

NIO phụ thuộc vào nền tảng. Khả năng nâng cao hiệu suất ứng dụng của nó phụ thuộc vào những điều sau:

  • OS (hệ điều hành)
  • JVM cụ thể
  • Đặc điểm lưu trữ hàng loạt (Mass)
  • Dữ liệu
  • Ngữ cảnh ảo hóa Máy chủ lưu trữ

Sau đây là các tính năng chính của các API NIO:

♦ Các bảng mã và các Bộ giải mã và Mã hóa được Liên kết của chúng: Chúng dịch dữ liệu giữa các byte và các ký tự Unicode. Bộ mã API được định nghĩa trong gói java.nio.charset. Gói ký tự linh hoạt hơn phương thức getBytes(), dễ triển khai hơn và mang lại hiệu suất vượt trội.

♦ Bộ đệm (Buffer): Đây là những vùng chứa dữ liệu. Các lớp đệm được định nghĩa trong gói java.nio và được sử dụng bởi tất cả các API NIO.

♦ Các loại kênh khác nhauCác kênh này đại diện cho các kết nối đến các thực thể có thể thực hiện các hoạt động I/O. Kênh là các tệp và socket trừu tượng và hỗ trợ I/O không đồng bộ.

♦ Bộ chọn và Khóa lựa chọn: Những thứ này cùng với các kênh có thể lựa chọn xác định một cơ sở I/O đa kênh, không chặn. I/O không chặn dựa trên sự kiện có nghĩa là một bộ chọn được xác định cho một kênh I/O và sau đó quá trình xử lý sẽ xảy ra. Khi một sự kiện diễn ra chẳng hạn như sự xuất hiện của đầu vào, trên bộ chọn, bộ chọn sẽ đánh thức và thực thi. Điều này có thể được thực hiện bằng cách sử dụng một chủ đề. API kênh và bộ chọn được xác định trong gói java.nio.channels.

Lưu ý - Mỗi gói con có gói con nhà cung cấp dịch vụ (SPI) riêng giúp mở rộng triển khai mặc định của nền tảng.  Nó cũng giúp xây dựng các triển khai thay thế.

Gói java.nio.file và gói java.nio.file.attribute hỗ trợ tệp I/O. Chúng cũng giúp truy cập vào hệ thống tệp mặc định.

Hệ thống file, đường dẫn và file

Hệ thống tệp lưu trữ và sắp xếp các tệp trên phương tiện, thường là ổ cứng. Các tập tin như vậy có thể được lấy ra một cách dễ dàng. Mọi tệp đều có một đường dẫn thông qua hệ thống tệp.

Hệ thống file

Thông thường, các tệp được lưu trữ trong cấu trúc phân cấp, nơi có một nút gốc. Dưới nút gốc, tồn tại các tệp và thư mục. Mỗi thư mục có thể chứa tệp và thư mục con, có thể chứa tệp và thư mục con, v.v. Không có giới hạn đối với cấu trúc phân cấp. Hệ thống file có thể có một hoặc nhiều thư mục gốc. Hệ thống file có các đặc điểm khác nhau đối với dấu phân cách đường dẫn.

Trong NIO.2, các phiên bản của đối tượng java.nio.file.Path đại diện cho vị trí tương đối hoặc tuyệt đối của tệp hoặc thư mục.

Lưu ý - Trước JDK 7, lớp java.io.File đại diện cho các tệp.

Đường dẫn

Mọi tệp được xác định thông qua đường dẫn của nó. Nói cách khác, đường dẫn chỉ định một vị trí duy nhất trong hệ thống file. Nó bắt đầu từ nút gốc. Microsoft Windows hỗ trợ nhiều nút gốc. Mỗi nút gốc ánh xạ tới một ổ đĩa, chẳng hạn như D:\. Hệ điều hành Solaris hỗ trợ một nút gốc duy nhất. Nó được thể hiện bằng ký tự gạch chéo, /.

Hệ thống file bao gồm các đặc điểm khác nhau cho dấu phân cách đường dẫn. Ký tự phân tách tên thư mục được gọi là dấu phân cách. Điều này dành riêng cho hệ thống file. Ví dụ: Microsoft Windows sử dụng dấu gạch chéo trái (\) và Hệ điều hành Solaris sử dụng dấu gạch chéo phải (/).

Hình dưới đây minh họa điều này.

Path

Dựa trên hình trên, hãy xem xét những điều sau:

Trong Hệ điều hành Solaris, đường dẫn cho tệp dailySalesReport sẽ được biểu diễn dưới dạng /home/user/dailySalesReport.

Trong Microsoft Windows, đường dẫn cho dailySalesReport sẽ được biểu diễn dưới dạng C:\home\user\dailySalesReport.

Một đường dẫn có thể là tương đối hoặc tuyệt đối. Một đường dẫn tuyệt đối bao gồm phần tử gốc và danh sách thư mục đầy đủ để định vị tệp. Ví dụ: /home/user/dailySalesReport là một đường dẫn tuyệt đối. Trong đó, tất cả thông tin cần thiết để định vị tệp trong chuỗi đường dẫn.

Đường dẫn tương đối không bao gồm danh sách thư mục đầy đủ. Ví dụ: user/dailySalesReport. Nó cần được sử dụng với một đường dẫn khác để truy cập tệp. Nếu không có thêm thông tin, một chương trình sẽ không thể định vị tệp.

Tệp

Trước JDK 7, lớp java.io.File được sử dụng để thực hiện tất cả các hoạt động tệp và thư mục. NIO.2 bao gồm gói và các lớp mới sau:

  • java.nio.file.Path: Điều này sử dụng một đường dẫn phụ thuộc vào hệ thống để định vị một tệp hoặc một thư mục.
  • Java.nio.file.Files: Điều này sử dụng một đối tượng Path để thực hiện các thao tác trên tệp và thư mục.
  • java.nio.file.FileSystem: Điều này cung cấp giao diện cho hệ thống tệp. Điều này cũng giúp tạo một đối tượng Path và các đối tượng khác để truy cập vào hệ thống tệp.

Lưu ý - Tất cả các phương thức truy cập vào hệ thống tệp đều ném IOException hoặc một lớp con.

Sự khác biệt giữa NIO.2 và java.io.File là cách hệ thống tệp được truy cập. Trong lớp java.io.File các phương thức để thao tác thông tin đường dẫn nằm trong cùng một lớp cũng chứa các phương thức để đọc và ghi các tệp và thư mục.

Với NIO.2 thì hai quá trình này đã được tách biệt. Trong NIO.2, interface Path giúp tạo và kiểm soát các đường dẫn, còn lớp Files thực thi các hoạt động trên tệp và thư mục. Lớp Files chỉ hoạt động trên các đối tượng Path. Các phương thức của lớp Files hoạt động trực tiếp trên hệ thống tệp sẽ ném ra một IOException.

Liên kết tượng trưng

Bên cạnh các thư mục hoặc tệp, một số hệ thống tệp nhất định hỗ trợ các liên kết tượng trưng. Các liên kết này còn được gọi là liên kết tượng trưng hoặc một liên kết mềm.

Một liên kết tượng trưng là một tham chiếu đến một tệp khác và minh bạch đối với các ứng dụng và người dùng. Các hoạt động trên liên kết tượng trưng được tự động chuyển hướng đến mục tiêu của liên kết. Ở đây, đích là tệp hoặc thư mục được trỏ tới. Hình 6.6 hiển thị liên kết tượng trưng.

Hình sau đây thể hiện Liên kết tượng trưng.

Liên kết tượng trưng (symbolic link)

Lưu ý - Khi một liên kết tượng trưng bị xóa hoặc đổi tên, liên kết sẽ bị xóa hoặc đổi tên.  Điều này không ảnh hưởng đến mục tiêu của nó.

Trong hình trên ta cần lưu ý những điều sau:

  • statuslogFile là một liên kết tượng trưng đến dir/logs/homestatuslogfile.
  • homestatuslogfile là đích của liên kết tượng trưng.

Đọc hoặc ghi vào một liên kết tượng trưng tương đương với việc đọc hoặc ghi vào bất kỳ tệp hoặc thư mục nào khác.

Hành động thay thế vị trí thực tế trong hệ thống tệp cho liên kết tượng trưng được gọi là giải quyết liên kết. Ví dụ, giải quyết statuslogFile sẽ cho dir/logs/homestatulogfile.

Nếu một liên kết tượng trưng được tạo một cách bất cẩn, có khả năng mục tiêu của một liên kết trỏ đến liên kết ban đầu. Đây được gọi là tham chiếu vòng tròn. Ví dụ, thư mục a trỏ đến thư mục b. Điều này lần lượt trỏ đến thư mục c. Thư mục c này chứa một thư mục con trỏ đến thư mục a.

Sự kiện như vậy có thể gây ra các vấn đề đặc biệt khi một chương trình đang điều hướng một cách đệ quy cấu trúc thư mục.

Lưu ý - Tham chiếu vòng sẽ không làm cho chương trình lặp lại vô hạn.

Interface Path

Đối tượng interface java.nio.file.Path có thể giúp định vị tệp trong hệ thống tệp. Thông thường, giao diện đại diện cho một đường dẫn tệp phụ thuộc vào hệ thống. Nói cách khác, nó cung cấp điểm vào cho việc thao tác tệp và thư mục.

Path có thứ bậc. Nó bao gồm một chuỗi các phần tử tên tệp và thư mục. Chúng được phân tách bằng dấu phân cách. Sau đây là các tính năng của Path:

  • Có thể có một thành phần gốc. Điều này đại diện cho một hệ thống phân cấp tệp.
  • Tên của tệp hoặc thư mục là phần tử tên nằm ở cực xa gốc của hệ thống phân cấp thư mục.
  • Các phần tử tên khác bao gồm tên thư mục.

Path có thể đại diện cho những điều sau:

  • Một root
  • Root và một chuỗi tên
  • Một hoặc nhiều phần tử tên

Path trống nếu nó chỉ bao gồm một phần tử tên trống.

Lưu ý - Truy cập tệp bằng đường dẫn trống tương tự như truy cập thư mục mặc định của hệ thống tệp.

Gói java.nio.file cũng cung cấp một lớp trợ giúp có tên là Paths. Lớp này là static và final và có phương thức getDefault().

Sau đây là một số phương thức của interface Path có thể được nhóm lại dựa trên các chức năng chung của chúng:

♦ Để truy cập các thành phần đường dẫn hoặc dãy con của các phần tử tên của nó, các phương thức getFileName(), getParent(), getRoot() và subpath() có thể được sử dụng.

♦ Để kết hợp các đường dẫn, interface Path định nghĩa các phương thức resolve() và resolveSibling() có thể được sử dụng.

♦ Để xây dựng một đường dẫn tương đối giữa hai đường dẫn, phương thức relativize() có thể được sử dụng.

♦ Để so sánh và kiểm tra đường dẫn, có thể sử dụng các phương thức startWith() và endWith().

Lưu ý - Một thư mục được định vị bởi một đường dẫn có thể là WatchService và các mục nhập trong thư mục có thể được xem.

Để có được một đối tượng Path, hãy lấy một thể hiện của hệ thống tệp mặc định. Tiếp theo, gọi phương thức getPath(). Ví dụ sau minh họa điều này.

FileSystem fs = FileSystem.getDeafult();
Path pathObj = fsObj.getPath("C:/Java/Hello.txt");

Ví dụ trên thể hiện trình cung cấp mặc định tạo ra một đối tượng để triển khai lớp. Cùng một đối tượng xử lý tất cả các thao tác được thực hiện trên một tệp hoặc một thư mục trong hệ thống tệp.

Các đối tượng đường dẫn khi đã được tạo không thể thay đổi. Nói cách khác, chúng là bất biến.

Đối với các hoạt động Path, nếu hệ thống tệp mặc định được sử dụng thì tiện ích Path sẽ được sử dụng. Hệ thống tệp mặc định đề cập đến hệ thống tệp mà JVM đang chạy. Đây là phương pháp ngắn hơn. Đối với các hệ thống tệp khác (không phải hệ thống tệp mặc định), bạn hãy lấy một thể của hệ thống tệp và xây dựng các đối tượng Path để thực hiện các hoạt động Path.

Lưu ý - Đối tượng đường dẫn tương tự như đối tượng chuỗi.

Ví dụ sau thể hiện việc sử dụng interface Path.

package solutions;

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

public class PathApplication {
  public static void main(String[] args) {
    Path pathObj = Paths.get("input.txt");
    System.out.printf("FileName is: %s%n", pathObj.getFileName());
    System.out.printf("Parent is: %s%n", pathObj.getParent());
    System.out.printf("Name count is: %d%n", pathObj.getNameCount());
    System.out.printf("Root directory is: %s%n", pathObj.getRoot());
    System.out.printf("Is Absolute: %s%n", pathObj.isAbsolute());
  }
}

Dưới đây là kết quả thực thi ví dụ trên:

FileName is: input.txt
Parent is: null
Name count is: 1
Root directory is: null
Is Absolute: false

♦ subpath()

Một phần của đường dẫn có thể được lấy bằng phương thức subpath().

Cú pháp :

Path subpath(int startindex, int endIndex);

trong đó,

startindex - đại diện cho giá trị bắt đầu

endIndex - đại diện cho giá trị endIndex - 1

Ví dụ sau trình bày việc sử dụng phương thức subpath().

Path pathObj = Paths.get("C:/Java/Hello.txt");
Path pathObj1 = pathObj.subpath(0, 2);

Trong ví dụ trên, tên phần tử gần gốc nhất có giá trị chỉ số là 0. Phần tử xa gốc nhất có giá trị chỉ số là đếm chỉ số - 1. Do đó, đầu ra của ví dụ sẽ là Java/Hello.txt.

♦ resolve()

Phương thức resolve() được sử dụng để kết hợp hai đường dẫn. Nó chấp nhận một đường dẫn một phần và nối đường dẫn một phần vào đường dẫn ban đầu. Ví dụ sau sử dụng phương thức resolve() để kết hợp hai đường dẫn.

Path pathObj = Paths.get("/Java/test");
pathObj.resolve("bar");

Output của ví dụ trên sẽ là /Java/test/bar.

Chuyển một đường dẫn tuyệt đối đến phương thức resolve() sẽ trả về đường dẫn đã truyền. Ví dụ sau sẽ trả về /Java/test:

Paths.get("bar").resolve("/Java/test");

♦ relativize()

Phương thức relativize() giúp tạo một đường dẫn từ một vị trí trong hệ thống tệp đến một vị trí khác. Ví dụ sau minh họa điều này.

Path pObj = Paths.get("user");
Path p1Obj = Paths.get("sales");
Path pTop = pObj.relativize(p1Obj);

Output sẽ là /sales.

Lưu ý - Đường dẫn mới tương đối với đường dẫn ban đầu.  Đường dẫn bắt nguồn từ đường dẫn ban đầu và kết thúc tại vị trí được xác định bởi đường dẫn truyền vào.

Làm việc với các liên kết

Interface Path nhận biết liên kết và mọi phương pháp đều phát hiện những việc cần làm hoặc cung cấp một tùy chọn để định cấu hình hành vi khi gặp liên kết tượng trưng.

Trong khi một số hệ thống tệp nhất định hỗ trợ liên kết tượng trưng, ​​một số hệ thống hỗ trợ liên kết cứng. Liên kết cứng khác với liên kết tượng trưng. Sau đây là một số điểm xác định một liên kết cứng:

  • Nó không được phép trên các thư mục và không được phép vượt qua các phân vùng hoặc khối lượng.
  • Khó tìm vì nó hoạt động như một tệp thông thường.
  • Nó nên bao gồm mục tiêu của liên kết.

Các thuộc tính của liên kết cứng giống với tệp gốc, chẳng hạn như quyền đối với tệp.

Lưu ý - Các phương thức Path hoạt động liền mạch với các liên kết cứng.

Mọi phương thức Path đều biết cách xử lý một liên kết tượng trưng.

Theo dõi hệ thống tệp và thay đổi thư mục

Các phương thức tĩnh trong lớp java.nio.file.Files thực hiện các chức năng chính cho các đối tượng Path. Các phương thức trong lớp có thể xác định và tự động quản lý các liên kết tượng trưng.

Sau đây là các hoạt động File khác nhau:

♦ Sao chép tệp hoặc thư mục

Để làm như vậy, có thể sử dụng phương thức copy(Path, Path, CopyOption ...). Hãy xem xét những điều sau khi sao chép:

  • Nếu tệp đích tồn tại, thì tùy chọn REPLACE_EXISTING phải được chỉ định. Nếu không, bản sao không thành công.
  • Khi sao chép một thư mục, các tệp bên trong thư mục sẽ không được sao chép.
  • Khi sao chép một liên kết tượng trưng, thì đích của liên kết được sao chép. Để chỉ sao chép liên kết, hãy sử dụng tùy chọn NOFOLLOW_LINKS hoặc REPLACE_EXISTING.

Phương thức này hỗ trợ các tùy chọn sau:

  • COPY_ATTRIBUTES: Thao tác này sao chép các thuộc tính tệp của tệp vào tệp đích. Thuộc tính tệp bao gồm hệ thống tệp. Chúng cũng phụ thuộc vào nền tảng. Ngoại lệ, thời gian sửa đổi lần cuối được hỗ trợ trên các nền tảng và được sao chép vào tệp đích.
  • NOFOLLOW_LINKS: Điều này được sử dụng khi các liên kết tượng trưng không được tuân theo. Chỉ liên kết được sao chép nếu tệp được sao chép là một liên kết tượng trưng. Mục tiêu của liên kết không được sao chép.
  • REPLACE_EXISTING: Thao tác này sao chép tệp ngay cả khi tệp đích tồn tại. Nếu mục tiêu là một liên kết tượng trưng thì ​​liên kết được sao chép. Đích của của liên kết không được sao chép. Nếu đích là một thư mục không trống thì việc copy không thành công và ngoại lệ FileAlreadyExistsException được ném.

Ví dụ sau cho ta cách sử dụng phương thức copy().

import static java.nio.file.StandardCopyOption.*;
...
Files.copy(source, target, REPLACE_EXISTING);

Ngoài ra còn có các phương thức trong lớp Files để sao chép giữa tệp và luồng. Sau đây là các phương thức như vậy:

copy(InputStream in, Path p, CopyOptions ... options): Thao tác này sao chép tất cả các byte từ một luồng đầu vào vào một tệp.

trong đó,

in - đại diện cho luồng đầu vào để đọc từ

p - đại diện cho đường dẫn đến tệp

options - chỉ định cách sao chép sẽ được thực hiện

Phương thức này ném IOException, FileAlreadyExistsException, DirectoryNo- tEmptyException, UnSupportedOperationException và SecurityException.

♦ copy(Path source, OutputStream out): Thao tác này sao chép tất cả các byte từ tệp tới output stream.

trong đó,

source - đại diện cho đường dẫn đến tệp

out - đại diện cho luồng đầu ra để ghi vào

Phương thức sẽ ném IOException và SecurityException.

Ví dụ sau sao chép một stream đến một đường dẫn.

package solutions;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class CopyStreamApplication {

  public static void main(String[] args) {
    Path pathObj = Paths.get("output.txt");
    URI uriObj = URI.create("https://v1study.com");
    try (InputStream inObj = uriObj.toURL().openStream()) {
      Files.copy(inObj, pathObj, StandardCopyOption.REPLACE_EXISTING);
    } catch (
      final MalformedURLException e) {
      System.out.println("Exception" + e);
    } catch (
      IOException e) {
      System.out.println("Exception" + e);
    }
  }
}

Trong ví dụ trên, nội dung của trang Web (https://v1study.com/) sẽ được sao chép vào một tệp văn bản được tạo trong thư mục được chỉ định.

Kết quả là trong file output.txt sẽ chứa toàn bộ dữ liệu đọc được từ trang https://v1study.com.

♦ Di chuyển tệp hoặc thư mục

Để di chuyển file hoặc thư mục thì ta sử dụng phương thức move(Path, Path, CopyOption ...). Nếu tệp đích tồn tại thì tùy chọn REPLACE_EXISTING sẽ được sử dụng, nếu không thì việc di chuyển không thành công. Phương thức nhận một đối số varargs.

Thư mục trống có thể được di chuyển. Nếu thư mục không trống, nó có thể được di chuyển mà không cần di chuyển các nội dung.

Phương thức move(Path source, Path target, CopyOption... options) hỗ trợ các enums StandardCopyOption sau:

  • REPLACE_EXISTING: Điều này thay thế tệp đích ngay cả khi tệp đích tồn tại cho một thư mục không trống. Nếu đích là một liên kết tượng trưng thì ​​chỉ liên kết tượng trưng được thay thế còn đích của liên kết không được thay thế.
  • ATOMIC_MOVE: Thao tác này di chuyển các thư mục và tệp dưới dạng hoạt động của hệ thống tệp đơn nguyên. Sử dụng tùy chọn này để di chuyển tệp vào một thư mục. Bất kỳ quá trình nào sau đó với thư mục sẽ truy cập một tệp hoàn chỉnh.

Lưu ý - Một ngoại lệ được đưa ra nếu hệ thống tệp không hỗ trợ di chuyển đơn nguyên.

Cú pháp sau thể hiện phương thức move().

Cú pháp:

import static java.nio.file.StandardCopyOption.*;
...
Files.move(source, target, REPLACE_EXISTING);

trong đó,

source - đại diện cho đường dẫn đến tệp để di chuyển

target - đại diện cho đường dẫn đến tệp đích

options - chỉ định cách di chuyển nên được thực hiện

Sau đây là các hướng dẫn cho việc di chuyển:

  • Nếu đường dẫn đích là một thư mục và thư mục trống, việc di chuyển sẽ thành công nếu REPLACE_EXISTING được đặt
  • Nếu thư mục đích không tồn tại thì việc di chuyển sẽ thành công
  • Nếu thư mục đích đang tồn tại nhưng không trống, thì ngoại lệ DirectoryNotEmptyException được ném
  • Nếu nguồn là tệp, thư mục đích tồn tại và REPLACE_EXISTING được thiết lập thì việc di chuyển sẽ đổi tên tệp thành tên thư mục dự định

Lưu ý - Trên hệ thống UNIX, thông thường, khi một thư mục được di chuyển trong cùng một phân vùng, nó sẽ được đổi tên. Trong trường hợp này, phương thức move(Path, Path, CopyOption...) hoạt động ngay cả khi thư mục chứa tệp.

♦ Kiểm tra tệp hoặc thư mục

Để làm được điều này hệ thống tệp phải được truy cập bằng các phương thức của Files để xác định xem Path có tồn tại không. Các phương thức trong lớp Path hoạt động trên thể hiện Path.

Sau đây là các phương thức Files để kiểm tra sự tồn tại của thể hiện Path:

  • exists(Path, LinkOption...opt): Kiểm tra xem tệp có tồn tại hay không. Theo mặc định, nó sử dụng các liên kết tượng trưng.
  • notExists(Path, LinkOption...): kiểm tra xem tệp có tồn tại hay không. Files.exists(path) không tương đương với Files.notExists(path). Khi kiểm tra sự tồn tại của tệp, một trong những điều sau đây là kết quả có thể xảy ra:
    • Tệp được xác minh là không tồn tại.
    • Tệp được xác minh là tồn tại.
    • Không thể xác minh sự tồn tại của tệp. Điều này xảy ra nếu cả hai tồn tại và không trả về false.
    • Trạng thái của tệp không xác định. Điều này xảy ra khi chương trình không có quyền truy cập vào tệp.

Để kiểm tra xem chương trình có thể truy cập một tệp hay không thì các phương thức isReadable(Path), isWritable(Path) và isExecutable(Path) có thể được sử dụng. Ví dụ sau thể hiện điều này.

Path file = ...;
boolean isRegularExecutableFile=Files.isRegularFile(file)&Files.isReadable(file)&Files.isExecutable(file);

Lưu ý - Kết quả của các thử nghiệm này không đáng tin cậy.

Một hệ thống tệp có thể sử dụng hai đường dẫn khác nhau để định vị cùng một tệp. Điều này xảy ra khi hệ thống tệp sử dụng các liên kết tượng trưng. Phương thức isSameFile(Path,Path) có thể giúp so sánh hai đường dẫn để kiểm tra xem chúng có định vị cùng một tệp trên hệ thống tệp hay không. Ví dụ sau minh họa điều này.

Path p1 = ...;
Path p2 = ...;
if (Files.isSameFile(p1, p2)) {
// Logic when the paths locate the same file

♦ Xóa tệp hoặc thư mục

Phương thức delete(Path) có thể được sử dụng để xóa tệp, thư mục hoặc liên kết. Việc xóa sẽ không thành công nếu thư mục không trống. Phương thức ném ra một ngoại lệ nếu việc xóa không thành công. Nếu tệp không tồn tại, một NoSuchFileException sẽ được ném ra.

Để xác định lý do cho lỗi, có thể bắt gặp ngoại lệ sau như được thể hiện trong ví dụ dưới.

try{
  Files.delete(path);
  }catch(NoSuchFileException x){
  System.err.format("%s:no such" + " file or directory%n",path);
  }catch(DirectoryNotEmptyException x){System.err.format("%s not empty%n",path);
  }catch(IOException x){
// File permission problems are caught here. System.err.println(x);
}

Phương thức deleteIfExists(Path) dùng để xóa tệp. Ngoài ra, nếu tệp không tồn tại thì sẽ không có ngoại lệ nào được ném ra. Điều này rất hữu ích khi có nhiều luồng cần xóa tệp.

♦ Liệt kê Nội dung Thư mục

Để làm được điều này hãy sử dụng lớp DirectoryStream lặp qua tất cả các tệp và thư mục từ bất kỳ thư mục Path nào. Hãy xem xét những điều sau:

  • DirectoryIteratorException được ném nếu có lỗi I/O trong khi lặp lại các mục trong thư mục được chỉ định.
  • PatternSyntaxException được ném khi mẫu không hợp lệ.

Ví dụ sau hiển thị việc sử dụng lớp DirectoryStream.

package solutions;

import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;

public class ListDirApplication {
  public static void main(String[] args) {
    Path pathObj = Paths.get("input.txt");
    try (DirectoryStream<Path> dirStreamObj = Files.newDirectoryStream(pathObj, " *.java")) {
      for (Iterator<Path> itrObj = dirStreamObj.iterator(); itrObj.hasNext(); ) {
        Path fileObj = itrObj.next();
        System.out.println(fileObj.getFileName());
      }
    } catch (IOException | DirectoryIteratorException ex) {
      // IOException can never be thrown by the iteration.
      // In this snippet, itObj can only be thrown by newDirectoryStream.
      System.err.println(ex.getMessage());
    }
  }
}

Hình dưới đây thể hiện kết quả.

Output của ListDirApplication

♦ Tạo và đọc thư mục

Phương thức createDirectory(Path dir) được sử dụng để tạo một thư mục mới.

Phương thức createDirectories() có thể được sử dụng để tạo các thư mục từ trên xuống dưới. Ví dụ sau minh họa điều này.

Files.createDirectories(Paths.get("C:/Java/test/example"));

Khi được thực thi thì lệnh trên sẽ tạo ra các thư mục Java, test và example theo thứ tự đã cho. Thư mục test sẽ được tạo bên trong thư mục Java và thư mục example sẽ được tạo bên trong thư muc test.

Để tạo tệp thì ta sử dụng phương thức createFile. Hãy xem xét cú pháp sau:

Cú pháp :

Files.createFile(Path dir)

♦ Đọc và Ghi từ tệp

Để đọc từ tệp, hãy sử dụng phương thức readAllBytes hoặc ReadAllLines sẽ đọc toàn bộ nội dung của tệp.

Lưu ý - Kết quả của các thử nghiệm này không đáng tin cậy.

Ví dụ sau cho thấy việc sử dụng phương thức ReadAllLines.

Path pathObj = ….;
byte[] fileArray;
fileArray = Files.readAllBytes(file);

Để ghi byte hoặc dòng vào tệp, có thể sử dụng các phương thức sau:

  • write(Path p, byte[] b, OpenOption…options): Phương thức ghi các byte vào một tệp.

trong đó,

p - chỉ định đường dẫn đến tệp

b - chỉ định mảng byte với các byte để ghi

option - chỉ định chế độ mở tệp

Một số enums OpenOptions tiêu chuẩn được hỗ trợ như sau:

  1. WRITE - Mở tệp để truy cập ghi
  2. APPEND - Thêm dữ liệu vào cuối tệp và được sử dụng với các tùy chọn WRITE hoặc CREATE
  3. TRUNCATE_EXIST - Cắt bớt tệp
  4. CREATE_NEW - Tạo một tệp mới
  5. CREATE - Tạo tệp mới nếu tệp không tồn tại hoặc mở tệp
  6. DELETE_ON_CLOSE - Xóa tệp khi luồng đóng

Phương thức sẽ ném IOException, UnsupportedOperationException và Securi-tyException.

  • write(Path p, Iterable<extends CharSequence> lines, CharSet ch, OpenOption… options): Phương thức này ghi các dòng văn bản vào một tệp.

trong đó,

p - chỉ định đường dẫn đến tệp

lines - chỉ định một đối tượng để lặp qua các chuỗi ký tự

ch - chỉ định bộ ký tự được sử dụng để mã hóa

options - chỉ định chế độ mở tệp

Cú pháp sau thể hiện phương thức ghi byte vào tệp.

Cú pháp:

Path file = ...;
byte[]buf=...;
Files.write(file,buf);

Lưu ý - Kênh I/O di chuyển dữ liệu trong bộ đệm. Nó bỏ qua một số lớp nhất định cản trở luồng I/O.

♦ Đọc tệp bằng cách sử dụng Buffered Stream I/O

Phương thức newBufferedReader(Path, Charset) mở một tệp để đọc. Điều này trả về một đối tượng BufferedReader giúp đọc văn bản từ tệp một cách hiệu quả. Ví dụ sau trình bày việc sử dụng phương thức newBufferedReader().

package solutions;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class BufferedIOApplication {
  public static void main(String[] args) {
    Path pathObj = Paths.get("input.txt");
    Charset charset = Charset.forName("US-ASCII");
    try (BufferedReader buffReadObj = Files.newBufferedReader(pathObj, charset)) {
      String lineObj = null;
      while ((lineObj = buffReadObj.readLine()) != null) {
        System.out.println(lineObj);
      }
    } catch (IOException e) {
      System.err.format("IOException: %s%n", e);
    }
  }
}

Hình dưới đây thể hiện kết quả của ví dụ trên.

Output-NIO.2

♦ Ghi tệp bằng cách sử dụng Buffered Stream I/O

Phương thức newBufferedWriter(Path, Charset, OpenOption ...) có thể được sử dụng để ghi vào tệp bằng cách sử dụng BufferedWriter.

Ví dụ sau sử dụng phương thức newBufferedWriter(Path, Charset, OpenOption ...) để tạo một tệp được mã hóa bằng "US-ASCII".

public class BufferedIOApplication {
  public static void main(String[] args) {
    Charset charset = Charset.forName("US - ASCII");
    String strObj = "Learn Java Programming";
    Path pathObj1 = Paths.get("C:/Java / JavaPg.txt");
    try (BufferedWriter writer = Files.newBufferedWriter(pathObj1, charset)) {
      writer.write(strObj, 0, strObj.length());
    } catch (IOException ex) {
      System.err.format("IOException: %s % n", ex);
    }
  }
}

Hình sau hiển thị kết quả.

Buffered Stream I/O

♦ Truy cập một tệp một cách ngẫu nhiên

Các tệp có thể được truy cập không tuần tự hoặc ngẫu nhiên bằng interface SeekableByteChannel. Để truy cập ngẫu nhiên một tệp, hãy thực hiện như sau:

  • Mở tập tin.
  • Tìm vị trí cụ thể.
  • Đọc từ tệp hoặc ghi vào tệp.

Interface SeekableByteChannel mở rộng kênh I/O và bao gồm nhiều phương thức khác nhau để thiết lập vị trí. Dữ liệu sau đó có thể được đọc từ vị trí hoặc được ghi vào nó.

Interface này bao gồm các phương thức sau:

  • read(ByteBuffer): Phương thức này đọc các byte vào bộ đệm từ kênh.
  • write(ByteBuffer): Phương thức này ghi các byte từ bộ đệm vào kênh.
  • truncate(long): Phương thức này cắt ngắn tệp được kết nối với kênh. Phương thức này cũng có thể được sử dụng để cắt bớt bất kỳ thực thể nào khác.
  • position(): Phương thức này trả về vị trí hiện tại của kênh.
  • position(long): Phương thức này thiết lập vị trí của kênh.

Khi các tệp được đọc và ghi bằng Channel I/O thì Path.newByteChannelmethods sẽ trả về một thể hiện của SeekableByteChannel. Để truy cập các tính năng nâng cao, chẳng hạn như khóa một vùng của tệp, kênh có thể được truyền sang một FileChannel.

Ví dụ sau thể hiện việc sử dụng các phương thức của tệp truy cập ngẫu nhiên.

import java.io.FileNotFoundException;
import java.io.IOException; 
import java.nio.ByteBuffer; 
import java.nio.channels.FileChannel; 
import java.nio.file.Path; 
import java.nio.file.Paths; 
import static java.nio.file.StandardOpenOption.*;

public class RandomAccessApplication {
  public static void main(String[] args) throws FileNotFoundException {
    String strObj = "I love Java programming!\n";
    byte byteDataObj[] = strObj.getBytes();
    ByteBuffer bufObj = ByteBuffer.wrap(byteDataObj);
    ByteBuffer bufCopyObj = ByteBuffer.allocate(20);
    Path pathObj = Paths.get("C:/Java/NewHello.txt");
    //creates a new file if it is not existing
    try (FileChannel fcObj = (FileChannel.open(pathObj, CREATE, READ, WRITE))) {
      //reads the first 20 bytes of the file int nreadChar;
      do {
        nreadChar = fcObj.read(bufCopyObj);
        System.out.println(nreadChar);
      } while (nreadChar != -1 && bufCopyObj.hasRemaining());
      // writes the string at the beginning of the file. fcObj.position(0);
      while (bufObj.hasRemaining()) {
        fcObj.write(bufObj);
      }
      bufObj.rewind();
      // Moves to the end of the file and copies the first 20 bytes to
      // the end of the file.
      long length = fcObj.size();
      fcObj.position(length - 1);
      //flips the buffer and sets the limit to the current position bufCopyObj.flip();
      while (bufCopyObj.hasRemaining()) {
        fcObj.write(bufCopyObj);
      }
      while (bufObj.hasRemaining()) {
        fcObj.write(bufObj);
      }
    } catch (IOException ex) {
      System.out.println("I/O Exception: " + ex.getMessage());
    }
  }
}

Trong ví dụ trên, hãy lưu ý những điều sau:

  • SeekableByteChannel được trả về được truyền vào FileChannel.
  • 20 Byte được đọc từ đầu tệp và chuỗi "Tôi yêu lập trình Java!" được viết tại vị trí đó.
  • Vị trí hiện tại trong tệp được di chuyển đến cuối và 20 Byte từ đầu được thêm vào.
  • Tiếp theo, chuỗi "Tôi yêu lập trình Java!" được thêm vào.
  • Cuối cùng, kênh trên tệp bị đóng.
Hình sau thể hiện kết quả đầu ra trong một tệp văn bản.

RandomAccessApplication

Theo dõi hệ thống tệp bằng WatchService và PathMatcher

Lớp FileSystem cung cấp interface cho hệ thống tệp và tạo các đối tượng truy cập tệp và các đối tượng khác trong hệ thống tệp. Nó bao gồm một số loại đối tượng sau đây:

  • getUserPrincipalLookupService(): Phương thức này trả về UserPrincipalLookupService để tra cứu người dùng hoặc nhóm theo tên.
  • newWatchService(): Phương thức này tạo một WatchService theo dõi các đối tượng để thay đổi.
  • getPath(): Phương thức này chuyển đổi một chuỗi đường dẫn phụ thuộc vào hệ thống và trả về một đối tượng Path để định vị và truy cập một tệp.
  • getPathMatcher(): Phương thức này tạo một PathMatcher thực hiện các hoạt động so khớp trên các path.
  • getFileStores(): Phương thức này trả về một trình lặp trên các kho lưu trữ tệp bên dưới.

Một hệ thống tệp có thể có một hệ thống phân cấp tệp duy nhất hoặc một số cấu trúc phân cấp tệp riêng biệt. Một hệ thống phân cấp duy nhất của tệp bao gồm một thư mục gốc cấp cao nhất.

Trong một số phân cấp tệp riêng biệt, mỗi phân cấp có thể có thư mục gốc cấp cao nhất của riêng nó. Phương thức getRootDirectories có thể được sử dụng để lặp qua các thư mục gốc trong hệ thống tệp. file-stores sẽ lưu trữ các tập tin trong một hệ thống tập tin.

Hệ thống tệp có thể bao gồm một hoặc nhiều kho lưu trữ tệp. Các kho lưu trữ tệp này hỗ trợ các tính năng khác nhau và các thuộc tính tệp.

Gọi phương thức close để đóng hệ thống tệp. Bất kỳ nỗ lực nào để truy cập các đối tượng trong hệ thống tệp đã đóng sẽ ném ClosedFileSystemException.

Lưu ý - Không thể đóng hệ thống tệp do nhà cung cấp mặc định tạo.

Hệ thống tệp có thể cung cấp quyền truy cập chỉ đọc hoặc đọc-ghi vào hệ thống tệp. Bất kỳ nỗ lực nào để ghi vào tệp lưu trữ bằng cách sử dụng một đối tượng có hệ thống tệp chỉ đọc sẽ ném ra ReadOnlyFileSystemException.

WatchService

Watchservice theo dõi các đối tượng đã đăng ký cho các thay đổi và các sự kiện. Điều này có thể được tùy chỉnh thành API cấp thấp hoặc API cấp cao. Nhiều người dùng đồng thời có thể sử dụng một dịch vụ đồng hồ.

Người quản lý tệp có thể sử dụng dịch vụ đồng hồ để kiểm tra thư mục để tìm các thay đổi, chẳng hạn như thời điểm tệp được tạo hoặc xóa. Bằng cách này, trình quản lý tệp có thể cập nhật danh sách tệp khi tệp được tạo hoặc xóa.

Phương thức register() được gọi để đăng ký đối tượng Watchable với dịch vụ theo dõi. Phương thức này trả về một WatchKey để đại diện cho việc đăng ký. Khi một thay đổi hoặc sự kiện đối với một đối tượng xảy ra, khóa sẽ được báo hiệu hoặc xếp hàng đợi cho dịch vụ đồng hồ. Điều này giúp người dùng truy xuất khóa và xử lý các sự kiện khi phương thức poll​​() hoặc take() được gọi. Sau khi các sự kiện được xử lý, người dùng sẽ gọi phương thức reset() của khóa. Điều này sẽ đặt lại khóa. Khóa sau đó được báo hiệu và xếp hàng lại với các sự kiện tiếp theo.

Để hủy đăng ký với dịch vụ đồng hồ, phương thức cancel() của khóa được gọi. Tuy nhiên, nó vẫn nằm trong hàng đợi cho đến khi nó được lấy ra. Ví dụ: nếu hệ thống tệp không thể truy cập được nữa và khóa bị hủy theo cách này, giá trị trả về từ phương thức reset() cho biết khóa có hợp lệ hay không.

Phương thức reset() của khóa chỉ được gọi sau khi các sự kiện của nó được xử lý. Điều này đảm bảo rằng chỉ có một người dùng xử lý các sự kiện cho một đối tượng cụ thể vào bất kỳ lúc nào. Phương thức close() có thể được gọi để đóng dịch vụ. Nếu một luồng đang đợi để lấy khóa, thì ClosedWatchServiceException sẽ được ném. Nói cách khác, nó sẽ đóng dịch vụ đang gây ra bất kỳ luồng nào đang chờ truy xuất khóa.

Nếu một sự kiện chỉ ra rằng một tệp trong thư mục đã xem được sửa đổi, thì điều này không đảm bảo rằng chương trình đã sửa đổi tệp đã hoàn thành. Lưu ý rằng có thể có các chương trình khác có thể đang cập nhật tệp. Cần đảm bảo rằng có sự phối hợp thích hợp với các chương trình này về khả năng tiếp cận. Nó cũng có thể khóa các vùng của một tệp không cho các chương trình khác truy cập. Lớp FileChannel định nghĩa các phương thức khác nhau có thể giúp làm như vậy.

Bảng sau đây mô tả một số phương thức nhất định của interface WatchService.

Các phương thức FileChannelClass

Mô tả

WatchKey poll(long timeout, TimeUnit unit)

Phương thức này lấy và loại bỏ khóa đồng hồ tiếp theo. Điều này có thể đợi cho đến thời gian chờ được chỉ định.

WatchKey take()

Phương thức này truy xuất và xóa khóa đồng hồ tiếp theo. Điều này sẽ chờ nếu chưa có.

void close()

Điều này đóng dịch vụ đồng hồ.

Interface PathMatcher

Để tìm một tệp, người ta sẽ tìm kiếm thư mục. Người ta có thể sử dụng công cụ tìm kiếm hoặc interface PathMatcher có phương thức đối sánh xác định xem đối tượng Path có khớp với một chuỗi được chỉ định hay không. Interface PathMatcher được thực hiện bởi các đối tượng để phù hợp với các hoạt động trên đường dẫn. Sau đây là cú pháp cho chuỗi syntaxAndPattern:

Cú pháp:

Syntax:pattern

trong đó,

syntax: có thể là glob hoặc regex

Bảng sau đây mô tả các pattern nhất định.

Pattern

Mô tả

.*. {java, class}

Điều này đại diện cho tệp kết thúc bằng phần mở rộng .java hoặc .class.

Client.?

Điều này đại diện cho tên tệp bắt đầu bằng Client và một ký tự đơn mở rộng.

C:\\*

Điều này đại diện cho c:\test hoặc c:\Java trên nền tảng Windows.

*.java

Điều này thể hiện tên tệp kết thúc bằng phần mở rộng là .java.

*.*

Điều này đại diện cho một tệp chứa một dấu chấm.

Bảng sau mô tả các quy tắc được sử dụng để giải thích các pattern glob.

Pattern glob

Mô tả

*

Điều này đại diện cho một ký tự duy nhất hoặc không có gì.

**

Điều này đại diện cho không có gì hoặc nhiều ký tự.

?

Điều này đại diện cho chỉ một ký tự.

\

Điều này thể hiện các ký tự thoát có thể được sử dụng như ký tự đặc biệt.

[] Điều này thể hiện một ký tự duy nhất của tên trong dấu ngoặc vuông. Ví dụ: [abc] phải khớp với a hoặc b hoặc c.
- Được sử dụng để chỉ định một phạm vi.

Các ký tự *,? và \ trong dấu ngoặc vuông khớp với nhau.

Lưu ý - Dấu chấm đứng đầu trong tên tệp được coi là một ký tự thông thường khi thực hiện phép toán đối sánh (match).

Các ký tự {} là một nhóm các mẫu con trong đó nhóm khớp với nhau nếu có bất kỳ mẫu con nào trong nhóm khớp. Dấu phẩy được sử dụng để phân tách mẫu con và các nhóm không thể được lồng vào nhau.

Để tìm một tệp, người dùng sẽ tìm kiếm một cách đệ quy một thư mục. Interface java.nio.PathMatcher có phương thức đối sánh để xác định xem đối tượng Path có khớp với chuỗi tìm kiếm được chỉ định hay không. Các phương thức gốc của FileSystem có thể được sử dụng để truy xuất thể hiện PathMatcher.

Để đi bộ qua một cây tệp thì interface FileVisitor cần được triển khai. Interface này chỉ định hành vi trong quá trình truyền tải. Các điểm chính trong quy trình truyền tải bao gồm thời điểm truy cập tệp, trước khi truy cập thư mục, sau khi truy cập thư mục hoặc khi xảy ra lỗi. Các phương thức tương ứng với các tình huống này như sau:

  • preVisitDirectory() - được gọi trước khi truy cập một thư mục
  • postVisitDirectory() - được gọi sau khi tất cả các mục trong thư mục được truy cập
  • visitFile() - được gọi khi một tệp được truy cập
  • visitFileFailed() - được gọi khi không thể truy cập tệp

Lớp SimpleFileVisitor thực thi interface FileVisitor và ghi đè tất cả các phương thức của lớp này. Lớp này truy cập tất cả các tệp và khi gặp lỗi, nó sẽ tạo ra IOError. Lớp này có thể được mở rộng và chỉ những phương thức bắt buộc được yêu cầu mới được ghi đè.

Ví dụ sau thể hiện việc sử dụng PatternMatcher và lớp SimpleFileVisitor để tìm kiếm tệp dựa trên một mẫu.

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;

import static java.nio.file.FileVisitResult.*;

import java.nio.file.FileVisitResult;
import java.nio.file.attribute.BasicFileAttributes;

class Finder extends SimpleFileVisitor<Path> {

  private Path file;
  private PathMatcher matcher;
  private int num;

  public Finder(Path path, PathMatcher matcher) {
    file = path;
    this.matcher = matcher;
  }

  private void find(Path file) {
    Path name = file.getFileName();
    if (name != null && matcher.matches(name)) {
      num++;
      System.out.println(file);
    }
  }

  void done() {
    System.out.println("Matched: " + num);
  }

  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attr) {
    find(file);
    return CONTINUE;
  }

  // Invoke the pattern matching
  // method on each directory.
  @Override
  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
    find(dir);
    return CONTINUE;
  }

  @Override
  public FileVisitResult visitFileFailed(Path file, IOException exc) {
    System.err.println(exc);
    return CONTINUE;
  }
}

public class PathMatcherApplication {

  public static void main(String[] args) throws IOException {
    Path pathObj;
    pathObj = Paths.get("D:/resources");
    PathMatcher matcherObj = FileSystems.getDefault().getPathMatcher("glob:" + "*.java");
    Finder finder = new Finder(pathObj, matcherObj);
    try {
      Files.walkFileTree(pathObj, finder);
    } catch (IOException ex) {
      System.out.println(ex);
    }
  }
}

Trong ví dụ trên, lớp Finder được sử dụng để đi bộ trên cây và tìm kiếm các kết quả phù hợp giữa tệp và tệp được chỉ định bởi phương thức visitFile(). Lớp này mở rộng lớp SimpleFileVisitor để nó có thể được truyền cho phương thức walkFileTree(). Lớp này sẽ gọi phương thức đối sánh trên mỗi tệp được truy cập trong cây.

Interface FileVisitor có các phương thức được gọi khi mỗi nút trong cây tệp được truy cập. Có một lớp SimpleFileVisitor dễ triển khai hơn vì nó thực thi tất cả các phương thức của interface FileVisitor. Trong interface FileVisitor, phương thức preVisitDirectory() được gọi trên lớp được truyền cho phương thức walkFileTree(). Nếu phương thức preVisitDirectory() trả về FileVisitResult.CONTINUE, thì nút tiếp theo sẽ được khám phá. Việc duyệt cây tệp hoàn tất khi tất cả các tệp đã được truy cập hoặc khi phương thức truy cập trả về TERMINATE. Điều này dẫn đến việc truyền tải bị chấm dứt và lỗi được chuyển đến trình gọi của phương thức này.

Khi phương thức walkFileTree() gặp một tệp, nó sẽ cố gắng đọc BasicFileAttributes và phương thức visitFile() được gọi với các thuộc tính File. Phương thức visitFileFailed() sẽ được gọi khi không thể đọc thuộc tính tệp do ngoại lệ I/O. Phương thức postVisitDirectory() được gọi sau khi tất cả các nút con trong các nút đã đạt đến.

Trong PathMatcherApplication, Path được lấy cho thư mục được chỉ định. Sau đó, thể hiện của PathMatcher được tạo bằng một biểu thức chính quy bằng cách sử dụng phương thức gốc của FileSystems. Hình sau thể hiện kết quả của ví dụ trên.

FileVisitor

» Tiếp: Lớp Files và các thuộc tính
« Trước: Các lớp InflaterInputStream và InflaterOutputStream
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!