Stream (luồng) và xử lý File trong Java
- 12-11-2023
- Toanngo92
- 0 Comments
Hầu hết các chương trình ứng dụng hoạt động với dữ liệu được lưu trữ trong các tệp cục bộ hoặc đến từ máy tính qua mạng. Java làm việc với luồng dữ liệu. Một luồng là một chuỗi dữ liệu. Nó cũng có thể được định nghĩa là một thực thể logic tạo ra hoặc tiêu thụ thông tin. Một luồng dữ liệu là một kênh thông qua đó dữ liệu di chuyển từ nguồn đến đích. Nguồn hoặc đích này có thể là thiết bị đầu vào hoặc đầu ra, phương tiện lưu trữ hoặc máy tính trên mạng. Một bộ nhớ lưu trữ dữ liệu vật lý được ánh xạ thành một luồng logic và sau đó, một chương trình Java đọc dữ liệu từ luồng này theo chuỗi – một byte sau một byte, hoặc ký tự sau ký tự. Nói cách khác, một tệp vật lý có thể được đọc bằng cách sử dụng các loại luồng khác nhau, ví dụ: FileTnputStream, hoặc Fi leReader. Java sử dụng các luồng như vậy để thực hiện các hoạt động đầu vào và đầu ra khác nhau.
Sự cần thiết của Các Lớp và Giao diện Stream
Trong Java, luồng là bắt buộc để thực hiện tất cả các hoạt động đầu vào/đầu ra (I/O). Một luồng đầu vào nhận dữ liệu từ một nguồn vào chương trình và một luồng đầu ra gửi dữ liệu đến một đích từ chương trình.
Do đó, các lớp và giao diện luồng giúp hữu ích trong các tác vụ:
- Đọc dữ liệu từ một luồng
- Ghi đầu ra vào một luồng.
- Quản lý tệp đĩa
- Chia sẻ dữ liệu với mạng máy tính
Mục lục
Các Lớp và Giao diện Luồng
Luồng đầu/ra tiêu chuẩn trong Java được biểu diễn bởi ba trường của lớp System:
- in
Luồng đầu vào tiêu chuẩn được sử dụng để đọc ký tự dữ liệu. Luồng này phản hồi đến đầu vào bàn phím hoặc bất kỳ nguồn đầu vào nào được chỉ định bởi môi trường hoặc người dùng. Nó đã được định nghĩa như sau:
public static final InputStream in;
- out
Luồng đầu ra tiêu chuẩn được sử dụng để hiển thị đầu ra trên màn hình hoặc bất kỳ phương tiện đầu ra nào khác. Nó đã được định nghĩa như sau:
public static final PrintStream out;
- err
Đây là luồng lỗi tiêu chuẩn. Theo mặc định, đây là console của người dùng. Nó đã được định nghĩa như sau:
public static final PrintStream err;
Để đọc hoặc ghi dữ liệu bằng cách sử dụng các luồng Đầu vào/Đầu ra, cần thực hiện các bước sau:
- Mở một luồng trỏ vào một nguồn dữ liệu cụ thể: một tệp, một socket, URL, và nhiều hơn nữa.
- Đọc hoặc ghi dữ liệu từ/đến luồng này.
- Đóng luồng.
InputStream và OutputStream là các lớp trừu tượng và được sử dụng để đọc và ghi các chuỗi byte không có cấu trúc. Các luồng đầu vào và đầu ra khác là các lớp con của các lớp trừu tượng này và được sử dụng để đọc và ghi vào tệp. Các loại luồng byte khác nhau có thể được sử dụng có thể sử dụng chung vì chúng kế thừa cấu trúc của lớp Input/OutputStream. Để đọc hoặc ghi byte, phải sử dụng một lớp con của lớp InputStream hoặc OutputStream tương ứng.
Luồng Phản ứng (Reactive Stream)
Lập trình phản ứng là một mô hình lập trình, tương tự như cách lập trình hướng đối tượng hoặc lập trình hàm là các mô hình lập trình.
Theo Chương trình phản ứng (một hướng dẫn xây dựng kiến trúc hiện đại, linh hoạt và có quy mô lớn), bất kỳ ứng dụng phản ứng nào phải tuân theo bốn đặc điểm chính:
- Responsive: Hệ thống tập trung vào việc cung cấp thời gian phản hồi nhanh chóng và nhất quán và đáp ứng một cách kịp thời.
- Resilient: Hệ thống là có khả năng phục hồi và vẫn giữ được tính phản ứng ngay cả khi gặp sự cố.
- Elastic: Hệ thống là linh hoạt và có thể điều chỉnh tùy theo công việc biến đổi.
- Message-driven: Hệ thống phụ thuộc vào các hoạt động điều khiển bất đồng bộ dựa trên tin nhắn.
Trong Java, Reactive Streams cung cấp một API chung để triển khai lập trình phản ứng.
RxJava và Akka Streams là hai phiên bản phổ biến của Reactive Streams.
RxJava
RxJava giúp thực hiện multithreading trong ứng dụng. RxJava là một trong những API được sử dụng cho Java. RxJava là một hiện thực Java của Reactive Extensions, là một thư viện cho các chương trình không đồng bộ và dựa trên sự kiện. Điều này được thực hiện bằng cách sử dụng mẫu Iterator, quan sát và lập trình hàm.
Trong thế giới Rx (lập trình phản ứng), có hai loại chính:
- Observables: Được gọi là các đối tượng phát ra một chuỗi sự kiện hoặc dữ liệu,
- Observers/Subscribers: Được gọi là các đối tượng thực hiện trên dữ liệu được phát ra.
Akka Concepts
Akka là một thư viện mã nguồn mở giúp phát triển dễ dàng các ứng dụng đồng thời và phân tán với Java, Akka là cả một nguyên tắc thiết kế và một nền tảng đồng thời.
Akka sử dụng mô hình Actor, mô hình/thiết kế trong đó “một actor” đại diện cho một thực thể tính toán độc lập. Actor tương tác chỉ thông qua các tin nhắn không đồng bộ và không tương tác thông qua các cuộc gọi phương thức trực tiếp. Không có phương thức nào có thể được gọi trên các đối tượng bên ngoài, nhưng việc truyền các actor dưới dạng đối tượng là có thể. Trong hầu hết các trường hợp, một trường hợp lớp được sử dụng cho điều này.
Hỗ trợ Reactive Streams được giới thiệu trong Java 9 thông qua java.util.concurrent.Flow API.
Luồng Phản ứng Java 9
Reactive Streams liên quan đến việc xử lý luồng theo cách không đồng bộ. Nó yêu cầu một nhà xuất bản để xuất bản luồng dữ liệu và một người đăng ký để tiêu thụ dữ liệu. Đôi khi dữ liệu được chuyển đổi giữa nhà xuất bản(publisher) và người đăng ký (subcriber). Cuối cùng, bộ xử lý chuyển đổi dữ liệu nhận được từ nhà xuất bản để người đăng ký hiểu được. Do đó, có thể có một loạt các bộ xử lý.
Triển khai Luồng Phản ứng bằng Flow API
Java Flow API từ phiên bản 9 trở đi triển khai đặc tả Reactive Streams. Sự kết hợp giữa mẫu Iterator và Observer tạo nên Flow API. Nếu một ứng dụng rút các mục từ nguồn, đó được gọi là mô hình rút theo Iterator. Nếu nguồn đẩy một mục đến ứng dụng, đó được gọi là mô hình đẩy theo Observer. Trong quá trình đăng ký, người đăng ký Java Flow API có thể yêu cầu N mục. Sau đó, các mục được đẩy đến người đăng ký cho đến khi tất cả các mục được thực hiện hoặc một lỗi xuất hiện.
Các lớp và giao diện của Flow API
Dưới đây là các lớp và giao diện của Flow API:
java.util.concurrent.Flow
Lớp này của Flow API là lớp chính và bao gồm các giao diện quan trọng của Flow API được lồng trong lớp này. Nó là một lớp cuối cùng và không thể được mở rộng.
java.util.concurrent.Flow.Publisher
Đây là một giao diện chức năng và mọi nhà xuất bản phải triển khai phương thức subscribe () để thêm người đăng ký để nhận thông điệp.
java.util.concurrent.Flow.Subscriber
Mỗi người đăng ký phải thực hiện giao diện này. Các phương thức của người đăng ký được thực hiện theo thứ tự tuần tự nghiêm ngặt. Bốn phương thức trong giao diện này được mô tả trong Bảng dưới.
Phương thức | Mô tả |
---|---|
subscribe | Như người đăng ký, đăng ký để nhận thông điệp, phương thức này được khởi tạo bởi nhà xuất bản. Để bắt đầu nhận các mục từ bộ xử lý, subscription.request() được gọi trong cài đặt của phương thức này, nơi subscription là một trường hợp của giao diện Flow.Subscription. |
onContext | Sau khi nhận được một mục từ một nhà xuất bản, logic nghiệp vụ được triển khai để xử lý luồng. Phương thức onContext được triển khai để yêu cầu thêm dữ liệu từ nhà xuất bản. |
onError | Khi xảy ra lỗi không thể phục hồi, phương thức onError được khởi chạy. Phương thức này cho phép các công việc dọn dẹp, chẳng hạn như kết thúc/đóng kết nối cơ sở dữ liệu trước khi kết thúc nhiệm vụ. |
onComplete | Sau khi sản xuất tất cả các mục, nhà xuất bản sẽ được đóng. Sau đó, cuối cùng, phương thức onComplete được khởi chạy. Nó cũng được sử dụng để gửi thông báo khi luồng được xử lý một cách hiệu quả. |
java.util.concurrent.Flow.Subscription
Người đăng ký sử dụng giao diện này để đăng ký vào một nhà xuất bản. Giao diện này xây dựng các liên kết không đồng bộ không chặn giữa một nhà xuất bản và người đăng ký. Nó cung cấp một phương thức request để người đăng ký yêu cầu các mục từ nhà xuất bản. Nó cũng cung cấp một phương thức hủy bỏ để kết thúc việc đăng ký.
java.util.concurrent.Flow.Processor
Giao diện này giúp biến đổi tin nhắn giữa nhà xuất bản và người đăng ký và mở rộng đến cả hai.
java.util.concurrent.SubmissionPublisher
Nó sử dụng khung Executor để phát hành các mục được gửi đến người đăng ký hiện tại một cách không đồng bộ cho đến khi nó được đóng. Ví dụ, người ta nên sử dụng lớp này trong các ví dụ luồng phản ứng để thêm một người đăng ký và sau đó, gửi các mục.
Ghi chú: Phát triển bằng cách sử dụng luồng Phản ứng được khuyến nghị chỉ trong các ứng dụng Java tiên tiến và phức tạp.
takeWhile(), dropWhile(), ofNullable(), và iterate với Condition
Trong Java, có thể thực hiện các hoạt động toàn diện với mẫu đối tượng khi sử dụng luồng (streams). Các phương thức sau đây làm tinh chỉnh luồng:
takeWhile(Predicate Interface)
Cú pháp:
default Stream<T> takeWhile(Predicate<? super T> predicate)
Phương thức takeWhile nhận giá trị cho đến khi điều kiện trả về false. Phương thức takeWhile chọn tiền tố dài nhất của các phần tử từ một luồng phù hợp với điều kiện đã cho trong một luồng được sắp xếp.
Ví dụ:
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
import java.util.stream.Stream;
/**
*
* @author toan1
*/
public class ExampletakeWhile {
public static void main(String[] args) {
Stream.of("H", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", ", ", "f", "r", "i", "e", "n", "d", "?", " ", "e", "n", "d", " ", "n", "e", "x", "t")
.takeWhile(s -> !s.equals("end"))
.forEach(System.out::print);
}
}
dropWhile(Predicate Interface)
Phương thức dropWhile loại bỏ toàn bộ dải giá trị ban đầu cho đến khi điều kiện trả về true. Trong một luồng được sắp xếp, phương thức này trả về các phần tử còn lại sau khi loại bỏ tiền tố dài nhất của các phần tử phù hợp với điều kiện đã cho.
Cú pháp:
default Stream<T> dropWhile(Predicate<? super T> predicate)
Ví dụ:
import java.util.stream.Stream;
public class Tester {
public static void main(String[] args) {
Stream.of("g", "h", "i", "", "k", "1").dropWhile(s -> !s.isEmpty())
.forEach(System.out::print);
System.out.println();
Stream.of("g", "\n", "m", "i", "x", "9", "k", "e", "", "1").dropWhile(s -> !s.isEmpty())
.forEach(System.out::print);
}
}
iterate()
Phương thức lặp dừng vòng lặp khi vị từ hasNext hiển thị giá trị là false.
Cú pháp:
static <I> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
Ví dụ:
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
/**
*
* @author toan1
*/
import java.util.stream.IntStream;
public class ExampleIterate {
public static void main(String[] args) {
IntStream.iterate(4, x -> x < 11, x -> x + 4).forEach(System.out::println);
}
}
Output:
4
8
ofNullable()
Phương thức ofNullable ngăn chặn NullPointerException và phù hợp với các luồng kiểm tra giá trị null. Nếu đối tượng không phải là null, phương thức này trả về một Stream có thứ tự bao gồm một phần tử duy nhất; ngược lại trả về một Stream trống.
Cú pháp:
static <T> Stream<T> ofNullable(T value)
Ví dụ:
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
import java.util.stream.Stream;
/**
*
* @author toan1
*/
public class ExampleOfNullable {
public static void main(String[] args) {
long count = Stream.ofNullable(100).count();
System.out.println(count);
count = Stream.ofNullable(null).count();
System.out.println(count);
}
}
Output:
1
0
Lớp File và Các Phương thức của lớp File
Khác với các lớp khác làm việc với luồng (stream), lớp File tương tác trực tiếp với các tệp và hệ thống tệp. Các tệp được đặt tên bằng cách sử dụng quy ước đặt tên tệp của hệ điều hành máy chủ. Những quy ước này được đóng gói bằng cách sử dụng các hằng số của lớp File.
Một đường dẫn có thể là tuyệt đối hoặc tương đối. Trong một đường dẫn tuyệt đối, không cần thông tin bổ sung nào để xác định tệp cần thiết vì đường dẫn là hoàn chỉnh. Trong một đường dẫn tương đối, thông tin được thu thập từ một đường dẫn khác. Các lớp trong gói java.io giải quyết đường dẫn tương đối đối với thư mục người dùng hiện tại, được đặt bởi thuộc tính hệ thống user.dir.
Lưu ý: Điều này thường là thư mục mà JVM được gọi.
getParent() là phương thức được sử dụng để có được thư mục cha của một đường dẫn trừu tượng. Điều này bao gồm tiền tố của đường dẫn và mỗi tên trong chuỗi tên của đường dẫn. Lưu ý rằng tên cuối cùng không được bao gồm. Đường dẫn tuyệt đối của mỗi thư mục là một tổ tiên của bất kỳ đối tượng File nào có đường dẫn trừu tượng tuyệt đối. Ví dụ, “/use” là một tổ tiên của thư mục được chỉ định bằng đường dẫn /usr/local/bin.
Lưu ý: Trong đường dẫn, /usr/local/bin là một tổ tiên của thư mục đường dẫn /usr/local/bin.
Khái niệm tiền tố được sử dụng để xử lý thư mục gốc trên các nền tảng như UNIX. Bảng 5.2 mô tả tiền tố mà mỗi nền tảng sử dụng.
Nền tảng | Tiền tố | Mô tả |
---|---|---|
UNIX | “/” | là tiền tố cho một đường dẫn tuyệt đối. Không có tiền tố cho đường dẫn tương đối. Đường dẫn trừu tượng chỉ định thư mục gốc có tiền tố “/” và một chuỗi tên trống. |
Microsoft Windows | : | Tiền tố của đường dẫn chứa chỉ định ổ đĩa bao gồm một chữ cái ổ đĩa theo sau là “:”. Điều này được theo sau bởi “\” nếu đường dẫn là tuyệt đối. Trong đường dẫn tương đối, không có tiền tố khi không có ổ đĩa được chỉ định. |
Khi các đối tượng của lớp File được tạo, đường dẫn trừu tượng được biểu diễn bởi một đối tượng File không bao giờ thay đổi. Nói cách khác, đối tượng của lớp File là không thay đổi.
Lớp File đóng gói quyền truy cập thông tin về một tệp hoặc thư mục. Nó lưu trữ đường dẫn và tên của một thư mục hoặc tệp. Tất cả các hoạt động thông thường với tệp và thư mục được thực hiện bằng cách sử dụng các phương thức truy cập do
lớp File cung cấp. Các phương thức của lớp này cho phép tạo, xóa và đổi tên tệp, cung cấp quyền truy cập đến đường dẫn của tệp, xác định xem đối tượng nào là tệp hay thư mục và kiểm tra quyền truy cập đọc và ghi.
Các phương thức thư mục trong lớp File cho phép tạo, xóa, đổi tên và liệt kê các thư mục.
Các giao diện và lớp được định nghĩa bởi gói java.nio.file giúp máy ảo Java truy cập tệp tin hệ thống và thuộc tính của tệp. Phương thức toPath() giúp có được một Path sử dụng đường dẫn trừu tượng. Một đối tượng File sử dụng đường dẫn này để định vị một tệp.
Để chẩn đoán lỗi khi một hoạt động trên tệp thất bại, Path có thể được sử dụng với lớp Files.
Lưu ý: Khi Path được sử dụng với lớp Files, có sự truy cập hào phóng đến các ngoại lệ, hoạt động tệp bổ sung và thuộc tính tệp.
Các hàm khởi tạo của lớp File như sau:
File(String dirPath) Hàm tạo File(String dirPath) tạo một đối tượng File với đường dẫn của tệp được chỉ định bởi biến chuỗi dirPath thành một đường dẫn trừu tượng. Nếu chuỗi trống, kết quả là một đường dẫn trừu tượng trống. Chữ ký của nó như sau:
public File(String dirPath)
File(String parent, String child) Hàm khởi tạo File(String parent, String child) tạo một đối tượng File với đường dẫn của tệp được chỉ định bởi các biến chuỗi parent và child. Nếu chuỗi cha là null, thì một thể hiện mới của File được tạo ra bằng cách sử dụng chuỗi đường dẫn con cung cấp. Hàm tạo này sẽ hoạt động giống như hàm tạo File một đối số với chuỗi đường dẫn con được cung cấp. Ngược lại, chuỗi đường dẫn cha được xem xét là trỏ đến một thư mục, và chuỗi đường dẫn con được xem xét là trỏ đến một thư mục hoặc tệp. Nếu chuỗi đường dẫn con là tuyệt đối, nó được chuyển đổi thành đường dẫn tương đối theo cách phụ thuộc vào hệ thống. Chữ ký của nó như sau:
public File(String parent, String child)
File(File fileObj, String fileName)
Hàm khởi tạo File(File fileObj, String fileName) tạo một đối tượng File mới từ một đối tượng File khác được chỉ định bởi biến fileObj, và tên tệp được chỉ định bởi biến chuỗi fileName. Nếu cha là null, thì một thể hiện mới của File được tạo ra bằng cách sử dụng chuỗi đường dẫn con cung cấp. Hàm tạo của File một đối số với chuỗi đường dẫn con được gọi và một thể hiện mới của File được tạo ra. Nếu điều này không xảy ra, đường dẫn trừu tượng cha được coi là trỏ đến một thư mục và chuỗi đường dẫn con được xem xét là trỏ đến một thư mục hoặc tệp. Chuỗi đường dẫn con được chuyển đổi thành đường dẫn tương đối theo cách phụ thuộc vào hệ thống nếu nó là tuyệt đối. Nếu cha là đường dẫn trừu tượng trống, một thể hiện mới của File được tạo ra. Điều này xảy ra bằng cách chuyển đổi con thành một đường dẫn trừu tượng và giải quyết kết quả so với một thư mục mặc định phụ thuộc vào hệ thống. Nếu không có điều này xảy ra, mỗi chuỗi đường dẫn được chuyển đổi thành một đường dẫn trừu tượng và đường dẫn trừu tượng con được giải quyết đối với cha.
Chữ ký của nó như sau:
public File(File fileObj, String fileName)
File(URL urlObj)
Hàm khởi tạo File(URL urlObj) chuyển đổi URL được chỉ định thành một đường dẫn trừu tượng và tạo một thể hiện mới của File. Đối với môi trường của URL là phụ thuộc hệ thống. Sự chuyển đổi bởi hàm tạo này cũng phụ thuộc vào hệ thống. Chữ ký của nó như sau:
public File(URL urlObj)
Ví dụ:
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
/**
*
* @author toan1
*/
import java.io.File;
import java.io.IOException;
public class CreateFileExample {
public static void main(String[] args) {
// Specify the path for the new file
String filePath = "D:\\\\test.txt";
// Create a File object representing the new file
File newFile = new File(filePath);
// File newFile = new File(folderPath,filePath); // overloaded method
try {
// Check if the file already exists
if (newFile.createNewFile()) {
System.out.println("File created successfully: " + newFile.getAbsolutePath());
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
System.err.println("Error creating the file: " + e.getMessage());
}
}
}
Các phương thức trong lớp File
Các phương thức trong lớp File giúplàm việc với tệp trên hệ thống tệp. Một số phương thức bao gồm:
- renameTo(File newName) Phương thức renameTo(File newName) sẽ đặt tên cho đối tượng File hiện tại bằng tên mới được chỉ định bởi biến newName.
public boolean renameTo(File newName)
- delete() Phương thức delete() xóa tệp được biểu diễn bởi đường dẫn trừu tượng.
public boolean delete()
- exists() Phương thức exists() kiểm tra sự tồn tại của tệp hoặc thư mục được chỉ định bởi đường dẫn trừu tượng này.
public boolean exists()
- getPath() Phương thức getPath() chuyển đổi đường dẫn trừu tượng thành một chuỗi đường dẫn.
public String getPath()
- isFile() Phương thức isFile() kiểm tra xem tệp được chỉ định bởi đường dẫn trừu tượng này có phải là một tệp thông thường không.
public boolean isFile()
- createNewFile() Phương thức createNewFile() tạo một tệp trống mới có tên là đường dẫn cho tệp này. Chỉ tạo khi tệp có tên tương tự không tồn tại.
public boolean createNewFile() throws IOException
- mkdir() Phương thức mkdir() tạo thư mục có tên được chỉ định bởi đường dẫn trừu tượng này.
public boolean mkdir()
- toPath() Phương thức toPath() trả về một đối tượng java.nio.file.Path được xây dựng từ đường dẫn trừu tượng.
Path toPath()
- toURI() Phương thức toURI() xây dựng một URI tệp. Tệp này đại diện cho đường dẫn trừu tượng này.
URI toURI()
Ví dụ:
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
/**
*
* @author toan1
*/
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
public class FileMethodsExample {
public static void main(String[] args) {
// Specify the path for the file
String filePath = "D:\\\\test.txt"; /// D:\\test.txt
// Create a File object representing the file
File file = new File(filePath);
// 1. Rename the file
File newFileName = new File("D:\\new_example.txt");
boolean isRenamed = file.renameTo(newFileName);
System.out.println("File renamed: " + isRenamed);
// 2. Create a new file
try {
boolean isNewFileCreated = newFileName.createNewFile();
System.out.println("New file created: " + isNewFileCreated);
} catch (IOException e) {
System.err.println("Error creating the new file: " + e.getMessage());
}
// 3. Check if the file exists
boolean fileExists = newFileName.exists();
System.out.println("File exists: " + fileExists);
// 4. Get the path of the file
String filePathString = newFileName.getPath();
System.out.println("File path: " + filePathString);
// 5. Check if it is a regular file
boolean isRegularFile = newFileName.isFile();
System.out.println("Is a regular file: " + isRegularFile);
// 6. Create a directory
File directory = new File("D:\\new_directory");
boolean isDirectoryCreated = directory.mkdir();
System.out.println("Directory created: " + isDirectoryCreated);
// 7. Convert to Path
Path path = newFileName.toPath();
System.out.println("Path: " + path);
// 8. Convert to URI
URI uri = newFileName.toURI();
System.out.println("URI: " + uri);
}
}
Ví dụ sử dụng lớp triển khai từ interface FilenameFilter để lọc file với các đuôi mở rộng xác định:
Tạo lớp FileFilter
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
/**
*
* @author toan1
*/
import java.io.File;
import java.io.FilenameFilter;
class FileFilter implements FilenameFilter {
String exts;
public FileFilter(String ext) {
this.exts = ext;
}
public boolean accept(File dir, String fName) {
return fName.endsWith(exts);
}
}
Tạo lớp DirList
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
import java.io.File;
import java.io.FilenameFilter;
/**
*
* @author toan1
*/
public class DirList {
public static void main(String[] args) {
String dirname = "D:/resources";
File f1 = new File(dirname);
FilenameFilter filter = new FileFilter(".java");
String[] fileList = f1.list(filter);
if (fileList != null) {
System.out.println("Number of files found: " + fileList.length);
System.out.println("Names of the files are: ");
for (String fileName : fileList) {
System.out.println("- " + fileName);
}
} else {
System.out.println("Directory not found or is not a directory.");
}
}
}
Lớp FileDescriptor
Lớp FileDescriptor cung cấp truy cập đến các bản mô tả tệp mà hệ điều hành duy trì khi tệp và thư mục được truy cập. Trong việc sử dụng thực tế, một bản mô tả tệp được sử dụng để tạo FileInputStream hoặc FileOutputStream để chứa nó. Bản mô tả tệp không nên được tạo ra độc lập bởi ứng dụng vì chúng liên quan đến hệ điều hành.
Lớp FileDescriptor có các trường công khai sau:
static final FileDescriptor err
Trường tĩnh FileDescriptor err hoạt động như một cánh cửa đến luồng lỗi tiêu chuẩn.
static final FileDescriptor in
Trường tĩnh FileDescriptor in hoạt động như một cánh cửa đến luồng nhập tiêu chuẩn.
static final FileDescriptor out
Trường tĩnh FileDescriptor out hoạt động như một cánh cửa đến luồng xuất tiêu chuẩn.
Constructor và Phương thức của FileDescriptor
Các constructor và phương thức của lớp FileDescriptor như sau:
FileDescriptor()
Constructor tạo ra một đối tượng FileDescriptor không hợp lệ.
public FileDescriptor()
sync()
Phương thức sync() xóa bộ đệm hệ thống và ghi nội dung mà chúng chứa xuống phần cứng thực tế.
public void sync() throws SyncFailedException
valid()
Phương thức valid() kiểm tra xem bản mô tả tệp có hợp lệ không. Do bản mô tả tệp liên quan đến các tệp mở, chúng trở thành không hợp lệ khi tệp được đóng.
public boolean valid()
Ví dụ:
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileDescriptorSyncExample {
public static void main(String[] args) {
try {
// Creating a FileOutputStream with a specific file
FileOutputStream fos = new FileOutputStream("example.txt");
// Getting the FileDescriptor associated with the FileOutputStream
FileDescriptor fd = fos.getFD();
// Writing data to the file
fos.write("Hello, FileDescriptor!".getBytes());
fos.flush();
// Calling sync to ensure data is physically written to the disk
fd.sync();
// Closing the FileOutputStream
fos.close();
// Check if the file descriptor is valid
System.out.println("Is FileDescriptor valid? " + fd.valid());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Giao diện DataInput và DataOutput
Luồng dữ liệu hỗ trợ đầu ra/nhập của các kiểu dữ liệu nguyên thủy và giá trị chuỗi. Các luồng dữ liệu triển khai giao diện DataInput hoặc DataOutput.
Giao diện DataInput có các phương thức để:
Đọc các byte từ một luồng nhị phân và chuyển đổi dữ liệu thành bất kỳ kiểu nguyên thủy Java nào.
Chuyển đổi dữ liệu từ định dạng Java Unicode sửa đổi (UTF)-8 thành chuỗi.
UTF-8 là một định dạng đặc biệt để mã hóa giá trị Unicode 16 bit.
Lưu ý: UTF-8 giả định rằng trong hầu hết các trường hợp, tám bit cao của một Unicode sẽ là không và tối ưu hóa tương ứng.
Giao diện DataOutput có các phương thức để:
Chuyển đổi dữ liệu hiện có trong kiểu nguyên thủy của Java thành một loạt byte và ghi chúng vào một luồng nhị phân,
Chuyển đổi dữ liệu chuỗi thành định dạng UTF-8 được sửa đổi bởi Java và ghi vào một luồng.
Phương thức của Giao diện DataInput
Giao diện DataInput có một số phương thức để đọc đầu vào, chẳng hạn như dữ liệu nhị phân từ luồng đầu vào và xây dựng lại dữ liệu từ byte thành bất kỳ kiểu nguyên thủy Java nào. Một IOException sẽ được kích hoạt nếu các phương thức không thể đọc bất kỳ byte nào từ luồng hoặc nếu luồng đầu vào đã đóng.
Các phương thức trong giao diện DataInput như sau:
readBoolean()
Phương thức readBoolean() đọc một byte đầu vào từ một luồng và trả về true nếu byte không phải là không và false nếu ngược lại.
boolean readBoolean() throws IOException
readByte()
Phương thức readByte() đọc một byte từ một luồng, là giá trị có dấu trong khoảng từ -128 đến 127.
byte readByte() throws IOException
readInt()
Phương thức readInt() đọc bốn byte từ một luồng và trả về giá trị int của các byte đã đọc.
int readInt() throws IOException
readDouble()
Phương thức readDouble() đọc 8 byte từ một luồng và trả về giá trị double của các byte đã đọc.
double readDouble() throws IOException
readChar()
Phương thức readChar() đọc hai byte từ một luồng và trả về giá trị char.
char readChar() throws IOException
readLine()
Phương thức readLine() đọc một dòng văn bản từ luồng đầu vào. Nó đọc một byte mỗi lần và sau đó chuyển đổi byte thành một ký tự và tiếp tục đọc cho đến khi nó gặp kết thúc dòng hoặc kết thúc tệp. Các ký tự sau đó được trả về dưới dạng một chuỗi.
String readLine() throws IOException
readUTF()
Phương thức readUTF() đọc một dòng văn bản trong định dạng UTF-8 được sửa đổi từ một luồng.
String readUTF() throws IOException
Ví dụ sử dụng lớp DataInputStream được triển khai từ giao diện DataInput:
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
/**
*
* @author toan1
*/
import java.io.*;
public class DataInputStreamDemo {
public static void main(String[] args) {
try {
DataInputStream dis = new DataInputStream(System.in);
System.out.println("Enter a double value: ");
double doubleValue = dis.readDouble();
// dis.readDouble();
// dis.readLine();
System.out.println("Double Value: " + doubleValue);
System.out.println("Enter an integer value: ");
int intValue = dis.readInt();
// dis.readLine();
System.out.println("Integer Value: " + intValue);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Phương thức của giao diện DataOutput
Giao diện DataOutput có nhiều phương thức để ghi đầu ra, chẳng hạn như dữ liệu nhị phân vào luồng đầu ra.
Một IOException có thể được kích hoạt nếu byte không thể được ghi vào luồng.
Các phương thức quan trọng trong giao diện này như sau:
writeBoolean(boolean b)
Phương thức writeBoolean(boolean b) ghi giá trị boolean được đưa vào làm tham số vào một luồng đầu ra. Nếu đối số có giá trị là true, thì sẽ được ghi 1, ngược lại, giá trị được ghi.
public void writeBoolean(boolean b) throws IOException
writeByte(int value)
Phương thức writeByte(int value) ghi giá trị byte của số nguyên được đưa vào làm tham số vào một luồng đầu ra.
public void writeByte(int value) throws IOException
writeInt(int value)
Phương thức writeInt(int value) ghi bốn byte biểu diễn giá trị số nguyên được đưa vào làm tham số vào một luồng đầu ra.
public void writeInt(int value) throws IOException
writeDouble(double value)
Phương thức writeDouble(double value) ghi tám byte biểu diễn giá trị số thực double được đưa vào làm tham số vào một luồng đầu ra.
public void writeDouble(double value) throws IOException
writeChar(int value)
Phương thức writeChar(int value) ghi giá trị char của số nguyên được đưa vào làm tham số vào một luồng.
public void writeChar(int value) throws IOException
writeChars(String value)
Phương thức writeChars(String value) ghi chuỗi được đưa vào làm tham số vào một luồng.
public void writeChars(String s) throws IOException
writeUTF(String value)
Phương thức writeUTF(String value) ghi một chuỗi dưới dạng UTF-8 được sửa đổi được đưa vào làm tham số vào một luồng.
public void writeUTF(String str) throws IOException
Đoạn mã dưới hiển thị việc sử dụng giao diện DataOutput thông qua lớp DataOutputStream, lớp này thực hiện giao diện này. Mã giả định rằng đã có một thể hiện outStream đã được tạo và khởi tạo.
import java.io.*;
public class ExampleDataOutputStream {
public static void main(String[] args) {
try {
// Assume outStream is already created and initialized
DataOutput outStream = new DataOutputStream(new FileOutputStream("example.dat"));
// Writing data using DataOutput methods
outStream.writeBoolean(true);
outStream.writeByte(65);
outStream.writeInt(12345);
outStream.writeDouble(3.14);
outStream.writeChar('A');
outStream.writeChars("Hello");
outStream.writeUTF("Java");
// Close the stream when done
if (outStream instanceof Closeable) {
((Closeable) outStream).close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Gói (Package) java.io
Một luồng (stream) đại diện cho nhiều nguồn và đích, như tệp trên đĩa và mảng trong bộ nhớ. Đó là một chuỗi dữ liệu. Một luồng I/O đại diện cho một nguồn đầu vào hoặc một đích đầu ra.
Các luồng hỗ trợ nhiều hình thức dữ liệu, như các byte đơn giản, kiểu dữ liệu nguyên thủy, ký tự cục bộ và các loại dữ liệu khác. Một số luồng cho phép dữ liệu trôi qua và một số luồng biến đổi dữ liệu một cách hữu ích.
Tuy nhiên, tất cả các luồng đều cung cấp một mô hình đơn giản để chương trình sử dụng chúng.
Một chương trình sử dụng một luồng đầu vào để đọc dữ liệu từ một nguồn. Nó đọc một mục mỗi lần.
Một chương trình sử dụng một luồng đầu ra để khi dữ liệu tới nguồn. Nó viết mộ mục mỗi lần
Chú ý: Các chương trình sử dụng byte streams để thực hiện đầu vào và đầu ra của byte 8-bit. Tất cả các lớp byte stream đều xuất phát từ InputStream và OutputStream.
Có nhiều lớp byte stream. Chúng hoạt động theo cùng một cách nhưng khác nhau về cách xây dựng.
Đoạn mã dưới mô tả cách byte streams hoạt động bằng cách sử dụng lớp FileInputStream và lớp FileOutputStream.
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package stream;
/**
*
* @author toan1
*/
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamApp {
public static void main(String[] args) throws IOException {
FileInputStream inObj = null;
FileOutputStream outObj = null;
try {
inObj = new FileInputStream("D:\\\\myavatar.jpg");
outObj = new FileOutputStream("D:\\\\myavatarout.jpg");
int ch;
while ((ch = inObj.read()) != -1) {
outObj.write(ch);
}
} finally {
if (inObj != null) {
inObj.close();
}
if (outObj != null) {
outObj.close();
}
}
}
}
Trong Đoạn mã trên, phương thức read() đọc một ký tự và trả về một giá trị int. Điều này cho phép phương thức read() chỉ ra rằng đã đến cuối luồng bằng cách trả về một giá trị là -1. Bằng cách sử dụng phương pháp này, toàn bộ nội dung của Hello.txt được đọc và sau đó, sao chép vào outagain.txt. Lưu ý rằng đường dẫn cho outagain.txt không được chỉ định. Điều này có nghĩa là nó sẽ được tạo trong thư mục hiện tại, tức là thư mục chứa tệp java này.
Khi một luồng không còn cần thiết nữa, quan trọng là phải đóng luồng, như được hiển thị ở đây trong khối finally. Điều này giúp tránh rò rỉ tài nguyên.
Nền tảng Java sử dụng quy ước Unicode để lưu trữ giá trị ký tự. I/O dòng ký tự chuyển đổi định dạng này vào và ra khỏi bộ ký tự địa phương.
Lưu ý rằng trong các vùng địa phương phương Tây, bộ ký tự địa phương thường là một siêu tập hợp 8-bit của ASCII.
Đối với hầu hết các ứng dụng, đầu vào và đầu ra được thực hiện bằng các lớp dòng tự động dịch từ và sang bộ ký tự địa phương. Một chương trình sử dụng các lớp dòng ký tự sẽ thích ứng với bộ ký tự địa phương và sẵn sàng cho quốc tế hóa. Tất cả các lớp dòng ký tự được xuất phát từ các lớp Reader và Writer. Có các lớp dòng ký tự chuyên biệt trong các hoạt động I/O tệp như FileReader và FileWwriter.
Đoạn mã dưới thể hiện việc đọc và ghi các luồng ký tự bằng cách sử dụng lớp FileReader và FileWriter:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamApp {
public static void main(String[] args) throws IOException {
FileReader inObjStream = null;
FileWriter outObjStream = null;
try {
inObjStream = new FileReader("c:/java/hello.txt");
outObjStream = new FileWriter("c:/java/charoutputagain.txt");
int ch;
while ((ch = inObjStream.read()) != -1) {
outObjStream.write(ch);
}
} finally {
if (inObjStream != null) {
inObjStream.close();
}
if (outObjStream != null) {
outObjStream.close();
}
}
}
}
Các luồng ký tự hoạt động như các lớp bọc cho luồng byte. Luồng ký tự quản lý việc chuyển đổi giữa ký tự và byte và sử dụng luồng byte để thực hiện các hoạt động I/O vật lý. Ví dụ, FileWriter sử dụng lớp FileOutputStream để ghi dữ liệu.
Khi không có lớp luồng ký tự đã đóng gói sẵn nào được yêu cầu, các luồng chuyển đổi từ byte sang ký tự, InputStreamReader và OutputStreamWriter, được sử dụng để tạo ra các luồng ký tự.
I/O ký tự thường xảy ra ở đơn vị lớn hơn so với từng ký tự một, chẳng hạn như một dòng bao gồm một chuỗi ký tự với một ký tự kết thúc dòng ở cuối.
Ký tự kết thúc dòng có thể là bất kỳ trong số các dạng sau:
- Chuỗi kết thúc dòng và xuống dòng “\r\n”
- Một ký tự trả về dòng duy nhất (“\r”)
- Một ký tự xuống dòng duy nhất (“\n”)
Các phương thức BufferedReader.readLine() và PrintWriter.println() giúp đầu vào và đầu ra của một dòng mỗi lần. Phương thức readLine() trả về một dòng văn bản với dấu kết thúc dòng. Phương thức println() đưa ra mỗi dòng trên một dòng mới khi nó thêm ký tự kết thúc dòng cho hệ điều hành hiện tại. Lưu ý rằng các ký tự kết thúc dòng trong tệp đầu vào và đầu ra có thể khác nhau.
Lớp InputStream và Các Lớp Con
Lớp InputStream
là một lớp trừu tượng xác định cách các luồng nhận dữ liệu và là lớp cha của tất cả các lớp luồng. Lớp này bao gồm các phương thức để đọc byte hoặc mảng byte, đánh dấu vị trí trong luồng, xác định số byte đã đọc hoặc có sẵn để đọc, và nhiều hơn nữa. Nó được sử dụng để đọc dữ liệu từ một luồng nhập.
Hình dưới mô tả cấu trúc lớp InputStream
Các phương thức lớp InputStream
Lớp InputStream cung cấp một số phương thức để quản lý việc đọc dữ liệu từ một luồng. Một số phương thức này bao gồm:
- read(): Phương thức này đọc byte tiếp theo từ luồng nhập và trả về một giá trị int trong khoảng từ 0 đến 255. Phương thức trả về -1 khi đến cuối tệp.
public abstract int read() throws IOException
- available(): Phương thức available() trả về số byte có thể đọc mà không bị chặn, kết quả là chỉ số số byte có sẵn.
public int available() throws IOException
- close(): Phương thức close() đóng luồng nhập, giải phóng tài nguyên hệ thống liên quan đến luồng.
public void close() throws IOException
- mark(int readlimit): Phương thức mark(int readlimit) đánh dấu vị trí hiện tại trong luồng và sẽ duy trì đến khi số byte được chỉ định bởi biến readlimit được đọc. Cuộc gọi đến phương thức reset() đặt lại con trỏ đến vị trí đã đánh dấu cuối cùng.
public void mark(int readlimit)
- skip(long n): Phương thức skip(long n) bỏ qua n byte dữ liệu khi đọc từ luồng nhập.
public long skip(long n) throws IOException
- reset(): Phương thức reset() đặt lại con trỏ đọc về vị trí đã đánh dấu trước đó trong luồng.
public void reset() throws IOException
Lớp FileInputStream
Đối tượng luồng tệp có thể được tạo bằng cách truyền tên của tệp, hoặc một đối tượng File, hoặc một đối tượng FileDescriptor tương ứng. Lớp FileInputStream được sử dụng để đọc các byte từ một tệp. Khi một đối tượng của lớp FileInputStream được tạo, nó cũng được mở để đọc. Lớp FileInputStream ghi đè tất cả các phương thức của lớp InputStream ngoại trừ các phương thức mark() và reset(). Phương thức reset() sẽ sinh ra một ngoại lệ IOException.
Các hàm khởi tạo phổ biến của lớp này bao gồm:
- FileInputStream(String sObj): Tạo một đối tượng InputStream có thể được sử dụng để đọc các byte từ một tệp. Tham số sObj lưu trữ đường dẫn đầy đủ của một tệp.
public FileInputStream(String sObj) throws FileNotFoundException
- FileInputStream(File fileObj): Tạo một đối tượng InputStream có thể được sử dụng để đọc các byte từ một tệp, với fileObj là một đối tượng File.
public FileInputStream(File fileObj) throws FileNotFoundException
- FileInputStream(FileDescriptor fdObj): Tạo một đối tượng FileInputStream sử dụng đối tượng FileDescriptor có thể được sử dụng để biểu diễn một kết nối hiện tại đến tệp có sẵn trong hệ thống tệp. Đối tượng FileDescriptor là fdObj.
public FileInputStream(FileDescriptor fdObj) throws FileNotFoundException
Đoạn mã dưới mô tả cách tạo đối tượng FileInputStream.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamExample {
public static void main(String[] args) {
// Specify the path of the file to be read
String filePath = "D:\\example.txt";
// Use try-with-resources to automatically close the FileInputStream
try (FileInputStream fis = new FileInputStream(filePath)) {
// Read bytes from the file
int byteData;
while ((byteData = fis.read()) != -1) {
// Process each byte (you can perform any operation here)
System.out.print((char) byteData);
}
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.err.println("Error reading the file: " + e.getMessage());
}
}
}
Đoạn mã dưới minh họa cách tạo một đối tượng FileInputStream
import java.io.FileInputStream;
import java.io.IOException;
public class Frstream {
public static void main(String[] args) {
try (FileInputStream inStream = new FileInputStream("D:/resources/Client.java")) {
int ch;
while ((ch = inStream.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
Lớp ByteArrayInputStream
Lớp ByteArrayInputStream (ByteArrayInputStream) chứa một bộ đệm lưu trữ các byte được đọc từ luồng. Lớp ByteArrayInputStream sử dụng một mảng byte làm nguồn. Lớp ByteArrayInputStream có một bộ đếm nội bộ, giữ theo dõi byte tiếp theo sẽ được đọc. Lớp này không hỗ trợ bất kỳ phương thức mới nào, chỉ ghi đè các phương thức của lớp InputStream như read(), skip(), available(), và reset().
Các thuộc tính quan trọng của lớp ByteArrayInputStream:
- protected byte[] buf: Đây là một mảng byte được cung cấp bởi người tạo luồng.
- protected int count: Đây là chỉ mục lớn hơn ký tự hợp lệ cuối cùng trong bộ đệm luồng đầu vào.
- protected int mark: Đây là vị trí hiện tại được đánh dấu trong luồng.
- protected int pos: Đây là chỉ mục của ký tự tiếp theo sẽ được đọc từ bộ đệm luồng đầu vào.
Các hàm khởi tạo của lớp này bao gồm:
- ByteArrayInputStream(byte[] b): Tạo một ByteArrayInputStream với một mảng byte b làm nguồn đầu vào.
- ByteArrayInputStream(byte[] b, int start, int num): Tạo một ByteArrayInputStream với một mảng byte b làm nguồn đầu vào, bắt đầu từ ký tự tại chỉ mục được chỉ định bởi start và có độ dài là num.
Đoạn mã dưới mô tả cách sử dụng lớp ByteArrayInputStream.
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class ByteArrayInputStreamExample {
public static void main(String[] args) {
try {
// Tạo một mảng byte
byte[] byteArray = "Hello, World!".getBytes();
// Tạo đối tượng ByteArrayInputStream từ mảng byte
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
// Đọc byte từ ByteArrayInputStream
int data;
while ((data = byteArrayInputStream.read()) != -1) {
System.out.print((char) data);
}
// Đóng luồng
byteArrayInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Trong ví dụ này, chương trình tạo một mảng byte từ chuỗi “Hello, World!” và sau đó sử dụng ByteArrayInputStream để đọc từng byte từ mảng và in ra màn hình.
Lớp OutputStream và Các Lớp Con
Lớp OutputStream là một lớp trừu tượng định nghĩa cách mà byte hoặc mảng byte được ghi vào luồng. ByteArrayOutputStream và FileOutputStream là các lớp con của lớp OutputStream.
Các Phương thức trong Lớp OutputStream
Có một số phương thức trong lớp OutputStream được sử dụng để ghi byte dữ liệu vào một luồng. Tất cả các phương thức của lớp này đều ném ra một ngoại lệ IOException.
Một số phương thức của lớp này bao gồm:
- write(int b): Phương thức này ghi byte đã chỉ định từ số nguyên được đưa vào làm tham số vào một luồng đầu ra. public abstract void write(int b) throws IOException.
- write(byte[] b): Phương thức này ghi một mảng byte được đưa vào làm tham số vào một luồng đầu ra. Số byte sẽ bằng độ dài của mảng byte. public void write(byte[] b) throws IOException.
- write(byte[] b, int off, int len): Phương thức này ghi các byte từ một mảng byte được đưa vào làm tham số, bắt đầu từ offset off đến len byte, vào một luồng đầu ra. Số byte được ghi vào luồng sẽ bằng giá trị được chỉ định trong len. public void write(byte[] b, int off, int len) throws IOException.
- flush(): Phương thức flush() làm trống luồng. Dữ liệu đệm được ghi vào luồng đầu ra. Việc đẩy giúp đẩy dữ liệu đệm để được ghi vào đích đầu ra dự kiến. Nó đảm bảo chỉ có những byte được đệm được chuyển đến hệ điều hành để ghi. Không có đảm bảo rằng các byte sẽ được thực sự ghi vào thiết bị vật lý. public void flush() throws IOException.
- close(): Phương thức close() đóng luồng đầu ra. Phương thức sẽ giải phóng tất cả các nguồn tài nguyên liên quan đến luồng đầu ra. public void close() throws IOException.
Lớp FileOutputStream
Lớp FileOutputStream tạo ra một OutputStream được sử dụng để ghi byte vào một tệp tin. FileOutputStream có thể hoặc không tạo ra tệp tin trước khi mở nó để ghi, điều này phụ thuộc vào nền tảng cụ thể.
Một số nền tảng chỉ cho phép một đối tượng ghi tệp tin mở một tệp tin để ghi. Do đó, nếu tệp tin đã được mở, các constructor trong lớp này sẽ thất bại (một FileNotFoundException sẽ được ném ra) chỉ khi một tệp tin chỉ được mở để đọc. Các constructor phổ biến của lớp này bao gồm:
- FileOutputStream(String filename): Phương thức này tạo ra một đối tượng luồng ghi tệp tin đầu ra được sử dụng để ghi byte vào một tệp tin. Ở đây, filename là tên đầy đủ của một tệp tin. public FileOutputStream(String filename) throws FileNotFoundException.
- FileOutputStream(File name): Phương thức này tạo ra một đối tượng FileOutputStream được sử dụng để ghi byte vào một tệp tin. Ở đây, name là một đối tượng File mô tả tệp tin. public FileOutputStream(File name) throws FileNotFoundException.
- FileOutputStream(String filename, boolean flag): Phương thức này tạo ra một đối tượng FileOutputStream được sử dụng để ghi byte vào một tệp tin. filename là một đối tượng File. Nếu flag là true, tệp tin sẽ được mở ở chế độ nối (append mode). public FileOutputStream(String filename, boolean flag) throws FileNotFoundException.
- FileOutputStream(File name, boolean flag): Phương thức này tạo ra một đối tượng FileOutputStream được sử dụng để ghi byte vào một tệp tin. name là một đối tượng File. Nếu flag là true, tệp tin sẽ được mở ở chế độ nối (append mode). public FileOutputStream(File name, boolean flag) throws FileNotFoundException.
Ví dụ FileOutputStream:
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamExample {
public static void main(String[] args) {
// Specify the path of the file to be written
String filePath = "C:\\path\\to\\your\\outputFile.txt";
// Use try-with-resources to automatically close the FileOutputStream
try (FileOutputStream fos = new FileOutputStream(filePath)) {
// Data to be written to the file
String content = "Hello, FileOutputStream!";
// Convert the content string to bytes
byte[] contentBytes = content.getBytes();
// Write the bytes to the file
fos.write(contentBytes);
System.out.println("Data has been written to the file.");
} catch (IOException e) {
System.err.println("Error writing to the file: " + e.getMessage());
}
}
}
Lớp ByteArrayOutputStream
Lớp ByteArrayOutputStream tạo ra một luồng đầu ra trong đó dữ liệu được ghi bằng cách sử dụng một mảng byte. Nó cho phép mảng đầu ra mở rộng kích thước để chứa dữ liệu mới được ghi.
Lớp ByteArrayOutputStream định nghĩa hai constructor:
- ByteArrayOutputStream(): Constructor này khởi tạo một đối tượng mới của ByteArrayOutputStream và đặt kích thước của bộ đệm thành kích thước mặc định là 32 byte. public ByteArrayOutputStream().
- ByteArrayOutputStream(int size): Constructor này khởi tạo một đối tượng mới của ByteArrayOutputStream và đặt kích thước của bộ đệm thành kích thước được chỉ định. public ByteArrayOutputStream(int size).
Các Phương thức trong Lớp ByteArrayOutputStream
Lớp ByteArrayOutputStream kế thừa tất cả các phương thức của lớp OutputStream. Các phương thức của lớp này cho phép truy xuất hoặc chuyển đổi dữ liệu. Các phương thức của lớp này có thể được gọi ngay cả sau khi luồng đầu ra đã được đóng và sẽ không tạo ra IOException.
Một số phương thức trong lớp ByteArrayOutputStream bao gồm:
- reset(): Phương thức này xóa tất cả các byte đã được ghi cho đến nay bằng cách đặt giá trị về 0. public void reset().
- size(): Phương thức này trả về số byte đã được ghi vào bộ đệm. public int size().
- toByteArray(): Phương thức này tạo ra một mảng byte mới được cấp phát chứa các byte đã được ghi vào luồng đến thời điểm hiện tại. public byte[] toByteArray().
- writeTo(OutputStream out): Phương thức writeTo(OutputStream out) ghi tất cả các byte đã được ghi vào luồng từ bộ đệm nội bộ vào luồng đầu ra được chỉ định. public void writeTo(OutputStream out) throws IOException.
- toString(): Phương thức toString() chuyển đổi nội dung của mảng byte thành một chuỗi. Phương thức này chuyển đổi các byte thành các ký tự theo bảng mã ký tự mặc định của nền tảng. public String toString().
Ví dụ dưới mô tả việc sử dụng lớp ByteArrayOutputStream.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayOutputStreamExample {
public static void main(String[] args) {
// Create a ByteArrayOutputStream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Data to be written to the stream
String content = "Hello, ByteArrayOutputStream!";
try {
// Convert the content string to bytes
byte[] contentBytes = content.getBytes();
// Write the bytes to the ByteArrayOutputStream
baos.write(contentBytes);
// Additional data can be written to the same stream
baos.write(", More Data!".getBytes());
// Get the contents of the ByteArrayOutputStream as a byte array
byte[] resultBytes = baos.toByteArray();
// Convert the byte array to a string and print it
String resultString = new String(resultBytes);
System.out.println("Result: " + resultString);
} catch (IOException e) {
System.err.println("Error writing to the ByteArrayOutputStream: " + e.getMessage());
} finally {
// Close the ByteArrayOutputStream (not necessary in this case, but good practice)
try {
baos.close();
} catch (IOException e) {
System.err.println("Error closing the ByteArrayOutputStream: " + e.getMessage());
}
}
}
}
Lọc luồng (Filter Stream)
Lớp FilterInputStream cung cấp chức năng bổ sung bằng cách sử dụng một luồng đầu vào làm nguồn dữ liệu cơ bản của nó. Lớp FilterOutputStream tạo luồng trên các luồng đầu ra hiện tại. Chúng có thể biến đổi dữ liệu trên đường đi hoặc cung cấp chức năng bổ sung.
Lớp FilterInputStream
Nó cũng có thể biến đổi dữ liệu trên đường đi. Lớp này ghi đè tất cả các phương thức của lớp InputStream để chuyển tất cả các yêu cầu đến luồng đầu vào chứa. Các lớp con cũng có thể ghi đè một số phương thức và có thể cung cấp các phương thức và trường bổ sung.
Dưới đây là các trường và constructor cho lớp java.io.FilterInputStream:
- protected InputStream in: Luồng đầu vào này phải được lọc.
- protected FilterInputStream(InputStream in): Tạo một FilterInputStream. Đối số được gán cho trường in và có thể được gọi lại bất kỳ lúc nào.
Dưới đây là các phương thức của lớp này:
- mark(int readlimit): Phương thức này xác định vị trí hiện tại trong luồng đầu vào. void mark(int readlimit).
- markSupported(): Phương thức này kiểm tra xem luồng đầu vào có hỗ trợ các phương thức mark và reset hay không. boolean markSupported().
- read(): Phương thức này đọc byte tiếp theo từ luồng đầu vào. int read().
- available(): Phương thức này trả về một ước lượng về số byte có thể được đọc hoặc bỏ qua từ luồng đầu vào. int available().
- close(): Phương thức này đóng luồng đầu vào và giải phóng bất kỳ tài nguyên hệ thống liên quan đến luồng. void close().
- read(byte[] b): Phương thức này đọc byte.length byte dữ liệu từ luồng đầu vào vào một mảng byte. int read(byte[] b).
- reset(): Phương thức này đặt lại con trỏ đến vị trí trong luồng khi phương thức mark được gọi lần cuối trên luồng đầu vào. void reset().
- skip(long n): Phương thức này bỏ qua và loại bỏ n byte dữ liệu từ luồng đầu vào. long skip(long n).
- read(byte[] b, int off, int len): Phương thức này đọc len byte dữ liệu từ luồng đầu vào vào một mảng byte, bắt đầu từ vị trí đã chỉ định bởi off. int read(byte[] b, int off, int len).
Ví dụ sử dụng lớp FilterInputStream:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FilterInputApplication {
public static void main(String[] args) throws IOException {
InputStream inputObj = null;
FilterInputStream filterInputObj = null;
try {
// Creates input stream objects
inputObj = new FileInputStream("C:/Java/Hello.txt");
filterInputObj = new BufferedInputStream(inputObj);
// Reads and prints from filter input stream
System.out.println((char) filterInputObj.read());
System.out.println((char) filterInputObj.read());
// Invokes mark at this position
filterInputObj.mark(0);
System.out.println("Mark() invoked");
System.out.println((char) filterInputObj.read());
System.out.println((char) filterInputObj.read());
} catch (IOException e) {
// Prints if any I/O error occurs
e.printStackTrace();
} finally {
// Releases system resources associated with the stream
if (inputObj != null) {
inputObj.close();
}
if (filterInputObj != null) {
filterInputObj.close();
}
}
}
}
Lớp FilterOutputStream
Lớp FilterOutputStream ghi đè tất cả các phương thức của lớp OutputStream mà chuyển tất cả các yêu cầu đến luồng đầu ra cơ bản. Các lớp con của FilterOutputStream cũng có thể ghi đè một số phương thức và cung cấp các phương thức và trường bổ sung.
Lớp java.io.FilterOutputStream bao gồm trường protected OutputStream out, đây là luồng đầu ra cần được lọc. FilterOutputStream (OutputStream out) là constructor của lớp này. Nó tạo ra một luồng đầu ra bộ lọc tồn tại qua luồng đầu ra đã được xác định.
Ví dụ sử dụng lớp FilterOutputStream:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class FilterOutputApplication {
public static void main(String[] args) throws IOException {
OutputStream outputStreamObj = null;
FilterOutputStream filterOutputStreamObj = null;
FileInputStream filterInputStreamObj = null;
byte[] bufObj = {81, 82, 83, 84, 85};
int i = 0;
char c;
// Encloses the creation of stream objects within try-catch block
try {
// Creates output stream objects
outputStreamObj = new FileOutputStream("C:/Java/test.txt");
filterOutputStreamObj = new FilterOutputStream(outputStreamObj);
// Writes to the output stream from bufObj
filterOutputStreamObj.write(bufObj);
// Forces the byte contents to be written to the stream
filterOutputStreamObj.flush();
// Creates an input stream object
filterInputStreamObj = new FileInputStream("C:/Java/test.txt");
while ((i = filterInputStreamObj.read()) != -1) {
// Converts integer to character
c = (char) i;
// Prints the character read
System.out.println("Character read after conversion is: " + c);
}
} catch (IOException e) {
// Checks for any I/O errors
System.out.println("Close() is invoked prior to write()");
} finally {
// Releases system resources associated with the stream
if (outputStreamObj != null) {
outputStreamObj.close();
}
if (filterOutputStreamObj != null) {
filterOutputStreamObj.close();
}
if (filterInputStreamObj != null) {
filterInputStreamObj.close();
}
}
}
}
Luồng đệm (Buffered Stream)
Bộ đệm là một khu vực lưu trữ tạm thời cho dữ liệu. Bằng cách lưu trữ dữ liệu trong bộ đệm, thời gian được tiết kiệm vì dữ liệu được nhận ngay từ bộ đệm thay vì quay lại nguồn gốc của dữ liệu.
Trong Java, sử dụng đầu vào và đầu ra đệm để tạm thời lưu trữ dữ liệu được đọc từ hoặc được ghi vào một luồng. Điều này giúp chương trình đọc hoặc ghi lượng dữ liệu nhỏ mà không ảnh hưởng xấu đến hiệu suất của hệ thống. Bộ đệm cho phép bỏ qua, đánh dấu và thiết lập lại luồng.
Bộ lọc hoạt động trên bộ đệm, nằm giữa chương trình và đích của luồng được đệm.
Lớp BufferedInputStream
Lớp BufferedInputStream cho phép người lập trình bọc bất kỳ lớp InputStream nào thành một luồng được đệm. BufferedInputStream hoạt động như một bộ nhớ đệm cho đầu vào bằng cách tạo ra mảng byte được sử dụng cho việc đọc trong tương lai. Cách đơn giản nhất để đọc dữ liệu từ một đối tượng của lớp BufferedInputStream là gọi phương thức read() của nó. Lớp BufferedInputStream cũng hỗ trợ các phương thức mark() và reset().
Phương thức markSupported() sẽ trả về true nếu nó được hỗ trợ.
Lớp BufferedInputStream định nghĩa hai constructor:
- BufferedInputStream(InputStream in): Constructor này tạo một luồng đầu vào được đệm cho một đối tượng InputStream cụ thể. Kích thước mặc định của bộ đệm là 2048 byte.
public BufferedInputStream(InputStream in)
- BufferedInputStream(InputStream in, int size): Constructor này tạo một luồng đầu vào được đệm với kích thước cụ thể cho đối tượng InputStream đã chỉ định.
public BufferedInputStream(InputStream in, int size)
Một số phương thức của lớp này được liệt kê trong bảng dưới
Phương thức | Mô tả |
---|---|
int available() | Phương thức trả về số byte đầu vào có sẵn để đọc. |
void mark(int readlimit) | Phương thức đặt một đánh dấu tại vị trí hiện tại trong luồng đầu vào. |
int read() | Phương thức đọc dữ liệu từ luồng đầu vào cơ bản. Nó ném một ngoại lệ nếu có lỗi I/O. |
int read(byte[] b) | Phương thức đọc byte vào mảng byte được chỉ định từ vị trí bắt đầu. Nó ném ngoại lệ I/O nếu có lỗi. |
void reset() | Phương thức đặt lại con trỏ trong luồng đến điểm mà phương thức mark được gọi lần cuối. |
Ví dụ:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class BufferedInputStreamExample {
public static void main(String[] args) {
InputStream inputObj = null;
BufferedInputStream bufferedInputObj = null;
try {
// Creates input stream objects
inputObj = new FileInputStream("C:/Java/Hello.txt");
bufferedInputObj = new BufferedInputStream(inputObj);
// Reads and prints from buffered input stream
int bytesRead;
while ((bytesRead = bufferedInputObj.read()) != -1) {
// Process each byte (you can perform any operation here)
System.out.print((char) bytesRead);
}
} catch (IOException e) {
// Prints if any I/O error occurs
e.printStackTrace();
} finally {
// Releases system resources associated with the streams
try {
if (inputObj != null) {
inputObj.close();
}
if (bufferedInputObj != null) {
bufferedInputObj.close();
}
} catch (IOException e) {
System.err.println("Error closing the streams: " + e.getMessage());
}
}
}
}
Lớp BufferedOutputStream
BufferedOutputStream tạo ra một bộ đệm được sử dụng cho một luồng đầu ra. Nó cung cấp cùng lợi ích về hiệu suất như BufferedInputStream. Khái niệm chính vẫn giống nhau, tức là thay vì mỗi lần đi đến hệ điều hành để ghi một byte, nó được lưu vào bộ đệm. Nó giống như OutputStream, ngoại trừ việc phương thức flush() đảm bảo rằng dữ liệu trong bộ đệm được ghi vào thiết bị đầu ra vật lý thực sự.
Các constructor của lớp này như sau:
- BufferedOutputStream(OutputStream os): Constructor này tạo ra một luồng đầu ra được đệm cho đối tượng OutputStream cụ thể với kích thước mặc định là 512 byte.
public BufferedOutputStream(OutputStream os)
- BufferedOutputStream(OutputStream os, int size): Constructor này tạo ra một luồng đầu ra được đệm với kích thước cụ thể cho đối tượng OutputStream đã chỉ định.
public BufferedOutputStream(OutputStream os, int size)
Bảng dưới liệt kê các phương thức của lớp BufferedOutputStream.
Phương thức | Mô tả |
---|---|
void flush() | Phương thức này đẩy bộ đệm của luồng đầu ra để đảm bảo dữ liệu được ghi vào thiết bị đầu ra thực tế. |
Ví dụ:
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedOutputStreamExample {
public static void main(String[] args) {
String filePath = "output.txt";
try (FileOutputStream fileOutputStream = new FileOutputStream(filePath);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) {
// Data to be written to the file
String data = "Hello, BufferedOutputStream!";
// Convert the string to bytes and write to the buffered output stream
bufferedOutputStream.write(data.getBytes());
System.out.println("Data has been written to the file.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Luồng ký tự (Character Streams)
Các lớp luồng byte cung cấp các phương thức để xử lý bất kỳ loại thao tác I/O nào ngoại trừ ký tự Unicode. Luồng ký tự cung cấp chức năng để xử lý các thao tác đầu vào/đầu ra dựa trên ký tự. Chúng hỗ trợ ký tự Unicode và có thể được quốc tế hóa.
Reader và Writer là các lớp trừu tượng ở đầu của thứ bậc lớp mà hỗ trợ đọc và viết các luồng ký tự Unicode. Tất cả các lớp luồng ký tự được xuất phát từ Reader và Writer.
Hình dưới mô tả các lớp luồng ký tự
Các Lớp Reader
Lớp Reader là một lớp trừu tượng được sử dụng để đọc các luồng ký tự. Các lớp con của lớp này ghi đè một số phương thức trong lớp này để tăng hiệu suất và chức năng của chúng. Tất cả các phương thức của lớp này đều ném một ngoại lệ IOException. Phương thức read() trả về -1 khi gặp cuối tệp.
Có hai constructor cho lớp Reader. Chúng là như sau:
- Reader(): Constructor này tạo ra một đọc ký tự và đồng bộ hóa độc giả và phần quan trọng của độc giả mới.
protected Reader()
- Reader(Object lock): Constructor này tạo ra một đọc ký tự và đồng bộ hóa độc giả mới với đối tượng được chỉ định.
protected Reader(Object lock)
Bảng dưới liệt kê một số phương thức của lớp Reader.
Phương thức | Mô tả |
---|---|
int read() | Phương thức đọc một ký tự duy nhất. |
int read(char[] buffer, int offset, int count) | Phương thức đọc ký tự vào một phần của mảng và trả về số ký tự đã đọc. |
int read(char[] buffer) | Phương thức đọc ký tự vào một mảng và trả về số lượng ký tự đã đọc. |
long skip(long count) | Phương thức bỏ qua một số lượng ký tự cụ thể. |
boolean ready() | Phương thức trả về true nếu độc giả đã sẵn sàng để đọc. |
void close() | Phương thức đóng luồng đầu vào và giải phóng tài nguyên hệ thống. |
boolean markSupported() | Phương thức thông báo xem luồng có hỗ trợ phương thức mark() không. |
void mark(int readAheadLimit) | Phương thức đánh dấu vị trí hiện tại trong luồng. |
void reset() | Phương thức đặt lại luồng. |
Các Lớp Writer
Lớp Writer là một lớp trừu tượng và hỗ trợ việc viết các ký tự vào luồng thông qua các phương thức có thể được ghi đè bởi các lớp con của nó. Các phương thức của lớp java.io.Writer giống như các phương thức của lớp java.io.OutputStream. Tất cả các phương thức của lớp này đều ném một ngoại lệ IOException trong trường hợp có lỗi.
Có hai constructor cho lớp Writer. Chúng là như sau:
- Writer(): Constructor này tạo ra một bộ viết ký tự mới mà các phần quan trọng của nó sẽ đồng bộ hóa trên bộ viết chính nó.
protected Writer()
- Writer(Object lock): Constructor này tạo ra một bộ viết ký tự mới mà các phần quan trọng của nó sẽ đồng bộ hóa trên đối tượng được chỉ định.
protected Writer(Object lock)
Bảng dưới liệt kê một số phương thức của lớp Writer.
Phương thức | Mô tả |
---|---|
void write(int c) | Phương thức viết một ký tự. |
void write(char[] text) | Phương thức viết một mảng đầy đủ các ký tự vào luồng đầu ra. |
void write(char[] text, int offset, int length) | Phương thức viết một phần của một mảng ký tự vào luồng đầu ra, bắt đầu từ vị trí offset. Biến length chỉ định số lượng ký tự cần viết. |
void write(String s) | Phương thức viết một chuỗi vào luồng đầu ra. |
void write(String s, int offset, int length) | Phương thức viết một phần của một chuỗi vào luồng đầu ra, bắt đầu từ vị trí offset. Biến length chỉ định số lượng ký tự cần viết. |
void flush() | Phương thức làm sạch luồng đầu ra để xóa bộ đệm. |
void close() | Phương thức đóng luồng đầu ra. |
Lớp PrintWriter
Lớp PrintWriter là một lớp dựa trên ký tự hữu ích cho việc xuất ra màn hình console. Nó thực hiện tất cả các phương thức in của lớp PrintStream và không có phương thức nào để viết các byte nguyên thô. Trong trường hợp đó, một chương trình sử dụng các luồng byte không mã hóa. Lớp PrintWriter khác biệt từ lớp PrintStream vì có thể xử lý đúng nhiều byte và các bộ ký tự khác.
Lớp này ghi đè phương thức write() của lớp Writer với sự khác biệt là không có phương thức nào ném bất kỳ IOException nào. Đầu ra được in kiểm tra lỗi bằng cách sử dụng phương thức checkError().
Lớp PrintWriter cũng hỗ trợ việc in các loại dữ liệu nguyên thủy, mảng ký tự, chuỗi và đối tượng. Nó cung cấp đầu ra được định dạng thông qua các phương thức print() và println(). Phương thức toString() sẽ cho phép in giá trị của các đối tượng. Các constructor cho lớp PrintWriter như sau:
- PrintWriter(OutputStream out): Constructor này tạo ra một PrintWriter mới từ một OutputStream hiện có. Nó không hỗ trợ tự động giải phóng(xả).
public PrintWriter(OutputStream out)
- PrintWriter(OutputStream out, boolean autoFlush): Constructor này tạo ra một PrintWriter mới từ một OutputStream hiện có. Một giá trị boolean là true sẽ cho phép println() hoặc printf() làm cho bộ đệm đầu ra được giải phóng.
public PrintWriter(OutputStream out, boolean autoFlush)
- PrintWriter(Writer out): Constructor này tạo ra một PrintWriter mới và không cho phép xả tự động.
public PrintWriter(Writer out)
- PrintWriter(Writer out, boolean autoFlush): Constructor này tạo ra một PrintWriter mới. Một giá trị boolean là true sẽ cho phép xả tự động của bộ đệm đầu ra.
public PrintWriter(Writer out, boolean autoFlush)
Ưu điểm chính của các phương thức print() và println() là bất kỳ đối tượng Java hoặc hằng số hoặc biến nào cũng có thể được in bằng cách truyền nó như một đối số. Nếu tùy chọn autoFlush được đặt thành true, thì việc xả tự động sẽ xảy ra khi phương thức println() được gọi. Phương thức print() không tự động xả luồng. Nếu không, cả hai phương thức này đều giống nhau.
Một số phương thức được nạp chồng của lớp này được liệt kê trong bảng
Phương thức | Mô tả |
---|---|
boolean checkError() | Phương thức xả luồng nếu nó mở và kiểm tra trạng thái lỗi. |
void print(boolean b) | Phương thức in giá trị boolean. |
void print(char c) | Phương thức được sử dụng để in một ký tự. |
void print(char[] s) | Phương thức được sử dụng để in một mảng các ký tự. |
void print(double d) | Phương thức in số thực kiểu double. |
void print(float f) | Phương thức in số thực kiểu float. |
void print(int i) | Phương thức được sử dụng để in một số nguyên. |
void print(long l) | Phương thức được sử dụng để in số nguyên dài. |
void print(Object obj) | Phương thức in một đối tượng và gọi phương thức toString() trên đối số. |
void print(String s) | Phương thức in một chuỗi. |
void println() | Phương thức kết thúc dòng hiện tại bằng cách sử dụng dấu phân cách dòng. |
void println(boolean x) | Phương thức in giá trị boolean và sau đó, kết thúc dòng. |
void flush() | Phương thức xả luồng đầu ra để xóa bộ đệm. |
void close() | Phương thức đóng luồng đầu ra. |
Lớp PrintWriter thực hiện và ghi đè phương thức trừu tượng write() từ lớp Writer. Sự khác biệt duy nhất là rằng không có phương thức write() nào của lớp PrintWriter ném một IOException vì nó được bắt trong lớp và một cờ lỗi được đặt.
Ví dụ:
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
PrintWriter pwObj = new PrintWriter(writer, true);
int tmp = 0;
char ch;
try {
while (tmp != -1) {
tmp = reader.read();
ch = (char) tmp;
pwObj.println("echo " + ch);
}
} catch (IOException e) {
System.out.println("IO error: " + e);
}
}
}
Lớp CharArrayReader
Lớp CharArrayReader là một lớp con của lớp Reader. Lớp này sử dụng một mảng ký tự làm nguồn văn bản để đọc. Lớp CharArrayReader có hai constructor và đọc chuỗi ký tự từ một mảng ký tự.
Các constructor của lớp này như sau:
CharArrayReader(char arr[])
Constructor CharArrayReader(char arr[]) tạo một CharArrayReader từ mảng ký tự được chỉ định, arr.
CharArrayReader(char arr[], int start, int num)
Constructor CharArrayReader(char arr[], int start, int num) tạo một CharArrayReader từ một phần của mảng ký tự, bắt đầu từ ký tự được chỉ định bởi chỉ mục start, và có độ dài num ký tự.
Một số phương thức của lớp này được liệt kê trong Bảng dưới
Phương thức | Mô tả |
---|---|
long skip(long n) | Phương thức bỏ qua n ký tự trước khi đọc. |
void mark(int num) | Phương thức đánh dấu vị trí hiện tại trong luồng. |
int read() | Phương thức đọc một ký tự. |
int read(char[] cbuf, int off, int len) | Phương thức đọc các ký tự vào mảng ký tự đã chỉ định từ vị trí off. |
void reset() | Phương thức đặt lại con trỏ trong luồng đến vị trí mà phương thức mark được gọi cuối cùng hoặc đến đầu luồng nếu nó chưa được đánh dấu. |
boolean ready() | Phương thức được sử dụng để xác nhận xem luồng này có sẵn để đọc không. |
void close() | Phương thức đóng luồng đầu vào. |
Ví dụ:
import java.io.CharArrayReader;
import java.io.IOException;
public class CharArrayReaderExample {
public static void main(String[] args) {
// Create a character array
char[] charArray = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd'};
// Create a CharArrayReader from the character array
try (CharArrayReader charArrayReader = new CharArrayReader(charArray)) {
// Read and print characters from the CharArrayReader
int charRead;
while ((charRead = charArrayReader.read()) != -1) {
System.out.print((char) charRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Lớp CharArrayWriter
Lớp CharArrayWriter là một lớp con của lớp Writer. Lớp này sử dụng một mảng ký tự trong đó các ký tự được ghi. Kích thước của mảng mở rộng theo cần. Các phương thức toCharArray(), toString(), và writeTo() có thể được sử dụng để truy xuất dữ liệu. Lớp CharArrayWriter kế thừa các phương thức được cung cấp bởi lớp Writer.
Các constructor của lớp này như sau:
CharArrayWriter()
Constructor CharArrayWriter() tạo một CharArrayWriter với một bộ đệm có kích thước mặc định là 32 ký tự.
CharArrayWriter(int num)
Constructor CharArrayWriter(int num) tạo một CharArrayWriter với một bộ đệm có kích thước được chỉ định bởi biến num.
Một số phương thức của lớp này được liệt kê trong Bảng dưới
Phương thức | Mô tả |
---|---|
void close() | Phương thức đóng luồng. |
void flush() | Phương thức xả luồng. |
void write(char c) | Phương thức ghi một ký tự vào mảng. |
void write(char[] cbuf, int off, int length) | Phương thức ghi các ký tự vào bộ đệm. |
void reset() | Phương thức đặt lại con trỏ trong bộ đệm đến vị trí mà phương thức mark được gọi cuối cùng hoặc đến đầu bộ đệm nếu nó chưa được đánh dấu. |
int size() | Phương thức trả về kích thước hiện tại của bộ đệm. |
char[] toCharArray() | Phương thức trả về một bản sao của dữ liệu. |
String toString() | Phương thức được sử dụng để chuyển đổi dữ liệu đầu vào thành chuỗi. |
void writeTo(Writer out) | Phương thức được sử dụng để ghi nội dung của bộ đệm vào một luồng ký tự. |
Ví dụ:
import java.io.CharArrayWriter;
import java.io.IOException;
public class CharArrayWriterExample {
public static void main(String[] args) {
// Create a CharArrayWriter
try (CharArrayWriter charArrayWriter = new CharArrayWriter()) {
// Write characters to the CharArrayWriter
charArrayWriter.write("Hello, ");
charArrayWriter.write("CharArrayWriter!");
// Get the content as a character array
char[] charArray = charArrayWriter.toCharArray();
// Print the content from the character array
System.out.println(charArray);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ví dụ 2:
import java.io.CharArrayWriter;
import java.io.IOException;
public class CharArrayWriterDemo {
public static void main(String[] args) throws IOException {
// Create a CharArrayWriter object which can hold 11 characters.
CharArrayWriter writer = new CharArrayWriter(11);
String str = "Hello Aptech";
// Write a portion of the string to CharArrayWriter.
writer.write(str, 6, str.length() - 6);
System.out.println("The CharArrayWriter buffer contains: " + writer.toString());
// Flush the contents of the CharArrayWriter.
writer.flush();
// Print out the contents of the CharArrayWriter buffer after flushing.
System.out.println("After flushing the CharArrayWriter, buffer contains: " +
new String(writer.toCharArray()));
// Reset the buffer.
writer.reset();
// Print out the contents of the CharArrayWriter buffer after resetting.
System.out.println("After reset, CharArrayWriter buffer contains: " + new String(writer.toCharArray()));
// Close the CharArrayWriter.
writer.close();
}
}
Serialization (Tuần tự hóa)
Sự bền vững (persistence) là quá trình lưu trữ dữ liệu vào một bộ nhớ lưu trữ cố định. Một đối tượng bền vững có thể được lưu trữ trên đĩa hoặc gửi đến máy khác để lưu trữ dữ liệu của nó. Ngược lại, một đối tượng không bền vững tồn tại miễn là JVM đang chạy. Java là một ngôn ngữ hướng đối tượng và do đó cung cấp các tiện ích để đọc và viết đối tượng. Serialization là quá trình đọc và viết đối tượng vào một luồng byte.
Một đối tượng triển khai giao diện Serializable sẽ lưu trạng thái của nó và khôi phục lại bằng cách sử dụng các tiện ích serialization và deserialization. Khi một lớp hoặc lớp cha của đối tượng Java thực hiện giao diện java.io.Serializable hoặc là subinterface của nó, java.io.Externalizable, đối tượng Java trở nên có thể tuần tự hóa. Giao diện java.io.Serializable không định nghĩa bất kỳ phương thức nào. Nó chỉ cho biết rằng lớp nên được xem xét để tuần tự hóa.
Nếu một lớp cha có thể tuần tự hóa, thì các lớp con của nó cũng có thể tuần tự hóa. Ngoại trừ duy nhất khi một biến là transient và static; trạng thái của nó không thể được lưu trữ bằng các tiện ích serialization. Khi dạng tuần tự của một đối tượng được chuyển đổi trở lại thành một bản sao của đối tượng, quá trình này được gọi là deserialization.
Khi một đối tượng được tuần tự hóa, tệp lớp không được ghi lại. Tuy nhiên, thông tin xác định lớp của nó được ghi lại trong luồng tuần tự hóa. Hệ thống deserialization chỉ định cách định vị và tải các tệp lớp cần thiết. Ví dụ, một ứng dụng Java có thể tải định nghĩa lớp bằng cách sử dụng thông tin được lưu trữ trong thư mục.
Việc tuần tự hóa là bắt buộc để triển khai Phương thức Gọi Phương thức từ xa (RMI), nơi một đối tượng Java trên một máy có thể gọi phương thức của một đối tượng khác có mặt trên một máy khác. Trong cuộc gọi phương thức từ xa này, máy nguồn có thể tuần tự hóa đối tượng và truyền nó, trong khi máy nhận sẽ deserialization đối tượng.
Nếu nhà cung cấp dịch vụ cơ sở hạ tầng hỗ trợ, một đối tượng có thể tuần tự hóa được lưu trữ trong thư mục.
Một ngoại lệ, NotSerializableException, được ném khi một trường có tham chiếu đối tượng chưa triển khai java.io.Serializable. Các trường được đánh dấu bằng từ khóa transient không nên được tuần tự hóa. Giá trị được lưu trữ trong các trường static không được tuần tự hóa. Khi một đối tượng được deserialization, các giá trị trong các trường static được đặt thành các giá trị được khai báo trong lớp và các giá trị trong các trường transient không phải static được đặt thành giá trị mặc định của chúng.
Một số thể hiện của lớp serialVersionUID được liên kết với một lớp có thể tuần tự hóa khi nó không rõ ràng khai báo số phiên bản. Số phiên bản được tính toán dựa trên các khía cạnh khác nhau của lớp. Một lớp có thể tuần tự hóa có thể khai báo số phiên bản của chính nó bằng cách khai báo một trường có tên là serialVersionUID của kiểu static, long và final.
Lớp ObjectOutputStream
Lớp ObjectOutputStream mở rộng từ lớp OutputStream và triển khai giao diện ObjectOutput. Nó ghi các loại dữ liệu nguyên thủy và đối tượng vào luồng đầu ra.
Các hàm khởi tạo của lớp này như sau:
ObjectOutputStream()
Hàm khởi tạo ObjectOutputStream() ngăn chặn các lớp con mà hoàn toàn cài đặt ObjectOutputStream từ cấp phát dữ liệu riêng tư chỉ được sử dụng bởi cài đặt này của ObjectOutputStream.
Chữ ký của nó như sau:
protected ObjectOutputStream() throws IOException, SecurityException
ObjectOutputStream(OutputStream out)
Hàm tạo ObjectOutputStream(OutputStream out) tạo ra một ObjectOutputStream ghi vào OutputStream được chỉ định.
Chữ ký của nó như sau:
public ObjectOutputStream(OutputStream out) throws IOException
Các Phương Thức trong Lớp ObjectOutputStream
Các phương thức trong lớp ObjectOutputStream giúp ghi đối tượng vào luồng đầu ra. Các phương thức trong lớp ObjectOutputStream bao gồm:
writeFloat(float value)
Phương thức writeFloat(float value) ghi giá trị kiểu float vào luồng đầu ra.
Chữ ký của nó như sau:
public void writeFloat(float value) throws IOException
writeObject(Object obj)
Phương thức writeObject(Object obj) ghi một đối tượng obj vào luồng đầu ra.
Chữ ký của nó như sau:
public final void writeObject(Object obj) throws IOException
defaultWriteObject()
Phương thức defaultWriteObject() ghi các trường không phải là static và không phải là transient vào luồng đầu ra.
Chữ ký của nó như sau:
public void defaultWriteObject() throws IOException
Ví dụ:
import java.io.*;
public class ObjectOutputStreamExample {
public static void main(String[] args) {
// Assume we have a Serializable class named MyClass
MyClass myObject = new MyClass(42, "Hello, ObjectOutputStream!");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("output.dat"))) {
// Writing primitive data types
oos.writeInt(123);
oos.writeDouble(3.14);
// Writing objects
oos.writeObject(myObject);
// Using writeFloat method
oos.writeFloat(1.23f);
// Using writeObject method
oos.writeObject(new AnotherClass(99, "Another object"));
// Using defaultWriteObject method (if applicable)
// myObject.defaultWriteObject(); // Uncomment if MyClass has defaultWriteObject method
System.out.println("ObjectOutputStream: Data written successfully.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
private int intValue;
private String stringValue;
public MyClass(int intValue, String stringValue) {
this.intValue = intValue;
this.stringValue = stringValue;
}
// If you want to use defaultWriteObject, uncomment the following method
// private void defaultWriteObject() throws IOException {
// // Custom serialization logic
// }
// Other methods, getters, setters...
}
class AnotherClass implements Serializable {
private static final long serialVersionUID = 1L;
private int anotherIntValue;
private String anotherStringValue;
public AnotherClass(int anotherIntValue, String anotherStringValue) {
this.anotherIntValue = anotherIntValue;
this.anotherStringValue = anotherStringValue;
}
// Other methods, getters, setters...
}
Lớp ObjectInputStream
Lớp ObjectInputStream mở rộng lớp InputStream và triển khai giao diện ObjectInput. Giao diện ObjectInput mở rộng giao diện DataInput và có các phương thức hỗ trợ việc tuần tự hóa đối tượng. Lớp ObjectInputStream chịu trách nhiệm đọc các đối tượng và kiểu dữ liệu nguyên thủy từ một luồng đầu vào cơ sở. Nó có phương thức readObject() để khôi phục một đối tượng chứa các trường không phải là static và không phải là transient.
Các constructor của lớp này như sau:
ObjectInputStream()
Constructor ObjectInputStream() giúp các lớp con tái hiện lớp ObjectInputStream để tránh phải cấp phát dữ liệu riêng tư được sử dụng bởi việc triển khai của ObjectInputStream. Chữ ký của nó như sau:
protected ObjectInputStream() throws IOException, SecurityException
ObjectInputStream(InputStream in)
Constructor ObjectInputStream(InputStream in) tạo ra một ObjectInputStream đọc từ InputStream đã chỉ định. Các đối tượng được tuần tự hóa được đọc từ luồng đầu vào in. Chữ ký của nó như sau:
public ObjectInputStream(InputStream in) throws IOException
Các Phương thức trong Lớp ObjectInputStream
Các phương thức trong lớp ObjectInputStream giúp đọc đối tượng từ luồng. Một số phương thức trong lớp ObjectInputStream là như sau:
readFloat()
Phương thức readFloat() đọc và trả về một số thực từ luồng đầu vào. Chữ ký của nó như sau:
public float readFloat() throws IOException
readBoolean()
Phương thức readBoolean() đọc và trả về một giá trị boolean từ luồng đầu vào. Chữ ký của nó như sau:
public boolean readBoolean() throws IOException
readByte()
Phương thức readByte() đọc và trả về một byte từ luồng đầu vào. Chữ ký của nó như sau:
public byte readByte() throws IOException
readChar()
Phương thức readChar() đọc và trả về một ký tự từ luồng đầu vào. Chữ ký của nó như sau:
public char readChar() throws IOException
readObject()
Phương thức readObject() đọc và trả về một đối tượng từ luồng đầu vào. Chữ ký của nó như sau:
public final Object readObject() throws IOException, ClassNotFoundException
Ví dụ:
import java.io.*;
class Person implements Serializable {
private static final long serialVersionUID = 1L; // Used for versioning during serialization
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person {name='" + name + "', age=" + age + "}";
}
}
public class ObjectStreamExample {
public static void main(String[] args) {
// Serialization Example
serializePerson("person.ser");
// Deserialization Example
Person deserializedPerson = deserializePerson("person.ser");
System.out.println("Deserialized Person: " + deserializedPerson);
}
private static void serializePerson(String filePath) {
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath))) {
// Create a Person object to serialize
Person personToSerialize = new Person("John Doe", 30);
// Write the Person object to the file
objectOutputStream.writeObject(personToSerialize);
System.out.println("Person serialized and saved to " + filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
private static Person deserializePerson(String filePath) {
Person deserializedPerson = null;
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filePath))) {
// Read the Person object from the file
deserializedPerson = (Person) objectInputStream.readObject();
System.out.println("Person deserialized from " + filePath);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return deserializedPerson;
}
}
Lớp Console
Java cung cấp lớp Console để cải thiện và đơn giản hóa quá trình phát triển các ứng dụng dòng lệnh. Lớp Console là một phần của gói java.io và có khả năng đọc văn bản từ terminal mà không hiển thị nó trên màn hình. Đối tượng Console cung cấp đầu vào và đầu ra của các luồng ký tự thông qua các lớp Reader và Writer của nó.
Lớp Console cung cấp các phương thức khác nhau để truy cập thiết bị dòng lệnh dựa trên ký tự. Không có hàm tạo công khai cho lớp Console. Để có một thể hiện của lớp Console, bạn cần gọi phương thức System.console(). Phương thức System.console() trả về đối tượng Console nếu nó khả dụng; nếu không, nó trả về null. Hiện tại, các phương thức của lớp Console chỉ có thể được gọi từ dòng lệnh và không thể từ Môi trường Phát triển Tích hợp (IDEs) như Eclipse, NetBeans, và như vậy.
Lớp Console cung cấp các phương thức để thực hiện các thao tác đầu vào và đầu ra trên các luồng ký tự. Phương thức readLine() đọc một dòng văn bản từ bảng điều khiển. Phương thức readPassword() đọc mật khẩu từ bảng điều khiển mà không hiển thị trên màn hình. Phương thức trả về một mảng ký tự và không phải là một đối tượng String để cho phép sửa đổi mật khẩu. Mật khẩu được xóa khỏi bộ nhớ khi không còn cần thiết nữa.
Bảng dưới liệt kê các phương thức khác nhau có sẵn trong lớp Console.
Phương thức | Mô tả |
---|---|
format(String fmt, Object… args) | Phương thức hiển thị dữ liệu được định dạng trên đầu ra của bảng điều khiển |
printf(String fmt, Object… args) | Phương thức hiển thị dữ liệu được định dạng trên đầu ra của bảng điều khiển một cách thuận tiện hơn |
reader() | Phương thức trả về một đối tượng java.io.Reader duy nhất liên kết với bảng điều khiển |
readLine() | Phương thức chấp nhận một dòng văn bản từ bảng điều khiển |
readLine(String fmt, Object… args) | Phương thức cung cấp đầu ra được định dạng và chấp nhận một dòng văn bản từ bảng điều khiển |
Ví dụ:
import java.io.Console;
import java.io.IOError;
public class ConsoleDemo {
public static void main(String[] args) {
Console cons = System.console();
if (cons == null) {
System.err.println("No console device is present!");
return;
}
try {
String username = cons.readLine("Enter your username: ");
char[] pwd = cons.readPassword("Enter your secret Password: ");
System.out.println("Username = " + username);
System.out.println("Password entered was = " + new String(pwd));
} catch (IOError ioe) {
cons.printf("I/O problem: %s%n", ioe.getMessage());
}
}
}
Các Lớp trong java.util.zip
Java SE 6 đã giới thiệu một số thay đổi trong các tệp JAR và ZIP. Trong các phiên bản trước của các tệp JAR, timestamp (ngày và giờ) của các tệp được giải nén thường được đặt là thời gian hiện tại thay vì thời gian được lưu trữ trong tệp nén. Trong Java SE 6, công cụ JAR đã được cải tiến để timestamp của các tệp được giải nén phù hợp với thời gian lưu trữ. Điều này là cần thiết vì các công cụ giải nén khác phụ thuộc vào thời gian lưu trữ chứ không phải thời gian hiện tại để giải nén các tệp.
Tên tệp ZIP hiện có thể dài hơn 256 ký tự trong các phiên bản gần đây của hệ điều hành Windows.
Sau khi sử dụng các tệp ZIP hàng ngày, các tệp quan trọng nên được lưu trữ và nén để tiết kiệm không gian đĩa quan trọng. Các tệp có thể được nén và giải nén bằng cách sử dụng các tiện ích phổ biến như WinRar và WinZip.
Trong gói java.util.zip, Java cung cấp các lớp có thể nén và giải nén tệp. Bảng 5.11 liệt kê một số lớp trong gói java.util.zip cùng với mô tả của chúng.
Tên Lớp | Mô Tả |
---|---|
CheckedInputStream | Duy trì checksum của dữ liệu đang được đọc |
CheckedOutputStream | Duy trì checksum của dữ liệu cần được ghi |
Deflater | Thực hiện việc nén dữ liệu |
DeflaterInputStream | Đọc dữ liệu nguồn và sau đó nén nó theo định dạng nén ‘deflate’ |
DeflaterOutputStream | Đọc dữ liệu nguồn, nén nó theo định dạng nén ‘deflate’ và sau đó ghi dữ liệu nén vào dòng đầu ra |
Inflater | Thực hiện việc giải nén dữ liệu |
InflaterInputStream | Đọc dữ liệu được nén và sau đó giải nén nó theo định dạng nén ‘deflate’ |
InflaterOutputStream | Đọc dữ liệu được nén, giải nén nó theo định dạng nén ‘deflate’ và sau đó ghi dữ liệu giải nén vào dòng đầu ra |
ZipInputStream | Thực hiện một bộ lọc dòng đầu vào để đọc các tệp trong định dạng tệp ZIP. Hỗ trợ cả các mục được nén và không được nén. |
ZipOutputStream | Đọc dữ liệu nguồn, nén nó theo định dạng tệp ZIP và ghi dữ liệu vào dòng đầu ra |
Ví dụ:
import java.io.*;
import java.util.zip.*;
public class ZipExample {
public static void main(String[] args) {
// Specify the source directory and the destination ZIP file
String sourceDir = "C:\\path\\to\\your\\source\\directory";
String zipFile = "C:\\path\\to\\your\\destination\\example.zip";
// Create a ZIP file and add files to it
createZipFile(sourceDir, zipFile);
// Extract files from the ZIP file
extractZipFile(zipFile, "C:\\path\\to\\your\\extracted\\directory");
}
private static void createZipFile(String sourceDir, String zipFile) {
try {
// Create an output stream for the ZIP file
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
// Get the list of files in the source directory
File sourceDirectory = new File(sourceDir);
File[] files = sourceDirectory.listFiles();
// Add each file to the ZIP file
for (File file : files) {
addToZip(file, zos);
}
// Close the ZIP output stream
zos.close();
System.out.println("ZIP file created: " + zipFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void addToZip(File file, ZipOutputStream zos) throws IOException {
// Create a ZIP entry for the file
ZipEntry zipEntry = new ZipEntry(file.getName());
zos.putNextEntry(zipEntry);
// Read and write the file content to the ZIP file
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) > 0) {
zos.write(buffer, 0, bytesRead);
}
// Close the current entry
zos.closeEntry();
fis.close();
}
private static void extractZipFile(String zipFile, String extractDir) {
try {
// Create an input stream for the ZIP file
FileInputStream fis = new FileInputStream(zipFile);
ZipInputStream zis = new ZipInputStream(fis);
// Create the output directory if it doesn't exist
File outputDir = new File(extractDir);
if (!outputDir.exists()) {
outputDir.mkdirs();
}
// Extract each entry from the ZIP file
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
String entryName = zipEntry.getName();
File entryFile = new File(outputDir, entryName);
// Create directories if the entry is a directory
if (zipEntry.isDirectory()) {
entryFile.mkdirs();
} else {
// Create parent directories for the file
entryFile.getParentFile().mkdirs();
// Write the entry content to the file
FileOutputStream fos = new FileOutputStream(entryFile);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = zis.read(buffer)) > 0) {
fos.write(buffer, 0, bytesRead);
}
fos.close();
}
// Close the current entry
zis.closeEntry();
}
// Close the ZIP input stream
zis.close();
System.out.println("Files extracted to: " + extractDir);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Lớp Deflater và Inflater
Các lớp Deflater và Inflater là các lớp mở rộng từ lớp Object. Các lớp này được sử dụng để nén và giải nén dữ liệu.
Lớp Deflater
Lớp Deflater nén dữ liệu có sẵn trong một luồng đầu vào. Nó nén dữ liệu bằng cách sử dụng thư viện nén ZLIB.
Constructor của lớp Deflater được sử dụng để tạo các thể hiện của lớp Deflater.
Cú pháp:
public Deflater()
Constructor tạo một thể hiện với mức nén mặc định.
Các Phương thức:
Bảng dưới liệt kê các phương thức khác nhau trong lớp Deflater cùng với mô tả của chúng.
Phương Thức | Mô Tả |
---|---|
deflate(byte[] buffer) | Điền bộ đệm đầu ra bằng dữ liệu được nén và trả về kích thước thực tế của dữ liệu được nén dưới dạng số nguyên. |
deflate(byte[] buffer, int offset, int len) | Điền bộ đệm đầu ra bằng dữ liệu được nén và trả về kích thước thực tế của dữ liệu được nén dưới dạng số nguyên. Ở đây, buffer là bộ đệm được chỉ định để lưu trữ dữ liệu được nén, offset là vị trí bắt đầu của dữ liệu và len là số byte tối đa của dữ liệu được nén. |
setInput(byte[] buffer) | Đặt dữ liệu đầu vào có sẵn trong bộ đệm để nén. |
setInput(byte[] buffer, int offset, int len) | Đặt dữ liệu đầu vào có sẵn trong bộ đệm để nén. Ở đây, buffer là bộ đệm được chỉ định để lưu trữ các byte dữ liệu đầu vào, offset là vị trí bắt đầu của dữ liệu và len là độ dài của dữ liệu đầu vào. |
finish() | Cho biết rằng quá trình nén nên kết thúc với nội dung hiện tại của bộ đệm đầu vào. |
end() | Đóng máy nén và loại bỏ dữ liệu đầu vào chưa được xử lý. |
Ví dụ:
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.util.zip.DataFormatException;
public class DeflaterExample {
public static void main(String[] args) {
// Original string to be compressed
String originalString = "This is a sample string to be compressed using Deflater.";
// Compress the string
byte[] compressedData = compressData(originalString);
// Decompress the data
String decompressedString = decompressData(compressedData);
// Display the results
System.out.println("Original String: " + originalString);
System.out.println("Compressed Data: " + byteArrayToHexString(compressedData));
System.out.println("Decompressed String: " + decompressedString);
}
private static byte[] compressData(String data) {
byte[] input = data.getBytes();
byte[] output = new byte[1024];
Deflater deflater = new Deflater();
deflater.setInput(input);
deflater.finish();
int compressedSize = deflater.deflate(output);
byte[] compressedData = new byte[compressedSize];
System.arraycopy(output, 0, compressedData, 0, compressedSize);
deflater.end();
return compressedData;
}
private static String decompressData(byte[] compressedData) {
byte[] output = new byte[1024];
Inflater inflater = new Inflater();
inflater.setInput(compressedData);
try {
int decompressedSize = inflater.inflate(output);
inflater.end();
return new String(output, 0, decompressedSize);
} catch (DataFormatException e) {
e.printStackTrace();
return null;
}
}
private static String byteArrayToHexString(byte[] byteArray) {
StringBuilder hexString = new StringBuilder();
for (byte b : byteArray) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}
}
Lớp Inflater
Lớp Inflater giải nén dữ liệu đã được nén. Lớp này hỗ trợ giải nén bằng cách sử dụng thư viện nén ZLIB.
Cú pháp:
public Inflater()
Constructor tạo một thể hiện với mức nén mặc định.
Bảng dưới liệt kê các phương thức khác nhau trong lớp Inflater cùng với mô tả của chúng.
Phương Thức | Mô Tả |
---|---|
inflate(byte[] buffer) | Điền bộ đệm đầu ra bằng dữ liệu được giải nén và trả về kích thước thực tế của dữ liệu giải nén dưới dạng số nguyên. |
inflate(byte[] buffer, int offset, int len) | Điền bộ đệm đầu ra bằng dữ liệu được giải nén và trả về kích thước thực tế của dữ liệu giải nén dưới dạng số nguyên. Ở đây, buffer là bộ đệm được chỉ định để lưu trữ dữ liệu giải nén, offset là vị trí bắt đầu của dữ liệu và len là số byte tối đa của dữ liệu giải nén. |
setInput(byte[] buffer) | Đặt dữ liệu đầu vào có sẵn trong bộ đệm để giải nén. |
setInput(byte[] buffer, int offset, int len) | Đặt dữ liệu đầu vào có sẵn trong bộ đệm để giải nén. Ở đây, buffer là bộ đệm được chỉ định để lưu trữ các byte dữ liệu đầu vào, offset là vị trí bắt đầu của dữ liệu và len là độ dài của dữ liệu đầu vào. |
end() | Đóng máy giải nén. |
Ví dụ:
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
public class InflaterExample {
public static void main(String[] args) {
// Compressed data (replace with actual compressed data)
byte[] compressedData = hexStringToByteArray("789C0BC9C82C5600A24485E2C4DC829C5485E292A2CCBC7485927C85A45485E4FCDC82A2D4E2E2D41485D26290B04B6A5A4E62496A911E003FEE1469");
// Decompress the data
String decompressedString = decompressData(compressedData);
// Display the results
System.out.println("Compressed Data: " + byteArrayToHexString(compressedData));
System.out.println("Decompressed String: " + decompressedString);
}
private static String decompressData(byte[] compressedData) {
byte[] output = new byte[1024];
Inflater inflater = new Inflater();
inflater.setInput(compressedData);
try {
int decompressedSize = inflater.inflate(output);
inflater.end();
return new String(output, 0, decompressedSize);
} catch (DataFormatException e) {
e.printStackTrace();
return null;
}
}
private static String byteArrayToHexString(byte[] byteArray) {
StringBuilder hexString = new StringBuilder();
for (byte b : byteArray) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}
private static byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}
Các Lớp DeflaterInputStream và DeflaterOutputStream
Các lớp DeflaterInputStream và DeflaterOutputStream được thừa kế từ các lớp FilterInputStream và FilterOutputStream tương ứng.
Lớp DeflaterInputStream
Lớp DeflaterInputStream đọc dữ liệu nguồn từ một luồng đầu vào và sau đó nén nó theo định dạng nén ‘deflate’. Lớp này cung cấp các constructor và phương thức của riêng mình. Cú pháp cho một số constructor được giải thích dưới đây.
Cú pháp:
public DeflaterInputStream(InputStream in)
Constructor tạo một luồng đầu vào của byte để đọc dữ liệu nguồn với một máy nén và bộ đệm mặc định.
public DeflaterInputStream(InputStream in, Deflater def)
Constructor tạo một luồng đầu vào mới với kích thước bộ đệm mặc định và máy nén được chỉ định.
public DeflaterInputStream(InputStream in, Deflater defl, int bufLen)
Constructor tạo một luồng đầu vào mới với kích thước bộ đệm và máy nén được chỉ định.
Bảng dưới liệt kê các phương thức khác nhau có sẵn trong lớp DeflaterInputStream cùng với mô tả của chúng.
Phương Thức | Mô Tả |
---|---|
read() | Trả về một byte dữ liệu đã được nén đọc từ một luồng đầu vào. |
read(byte[] buffer, int offset, int bufSize) | Trả về số byte dữ liệu đã được nén được đọc vào một mảng byte bắt đầu từ vị trí được chỉ định bởi offset và với kích thước bộ đệm là bufSize. |
close() | Đóng luồng đầu vào sau khi đọc dữ liệu nguồn còn lại. |
markSupported() | Trả về giá trị false. Điều này là do luồng đầu vào không hỗ trợ các phương thức mark() và reset(). |
available() | Trả về 0 sau khi đã đọc hết EOF. Nếu chưa đọc hết, nó trả về 1. |
long skip(long n) | Bỏ qua và loại bỏ dữ liệu từ luồng đầu vào. |
Ví dụ:
import java.io.*;
import java.util.zip.DeflaterInputStream;
import java.util.zip.InflaterInputStream;
public class DeflaterInputStreamExample {
public static void main(String[] args) {
// Original string to be compressed
String originalString = "This is a sample string to be compressed using DeflaterInputStream.";
// Compress the string
byte[] compressedData = compressData(originalString);
// Decompress the data
String decompressedString = decompressData(compressedData);
// Display the results
System.out.println("Original String: " + originalString);
System.out.println("Compressed Data: " + byteArrayToHexString(compressedData));
System.out.println("Decompressed String: " + decompressedString);
}
private static byte[] compressData(String data) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DeflaterInputStream deflaterInputStream = new DeflaterInputStream(new ByteArrayInputStream(data.getBytes()))) {
// Read from the DeflaterInputStream and write to the ByteArrayOutputStream
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = deflaterInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static String decompressData(byte[] compressedData) {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
InflaterInputStream inflaterInputStream = new InflaterInputStream(byteArrayInputStream)) {
// Read from the InflaterInputStream and convert to a string
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inflaterInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
return byteArrayOutputStream.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static String byteArrayToHexString(byte[] byteArray) {
StringBuilder hexString = new StringBuilder();
for (byte b : byteArray) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}
}
Ví dụ 2:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.DeflaterInputStream;
public class DeflaterInputApplication {
public static byte[] increaseArray(byte[] arrTemp) {
byte[] temp = arrTemp;
arrTemp = new byte[arrTemp.length + 1];
for (int itr = 0; itr < temp.length; itr++) {
arrTemp[itr] = temp[itr];
}
return arrTemp;
}
public static void main(String[] args) throws IOException {
FileOutputStream fos = null;
try {
File file = new File("C:/Java/Hello.txt");
FileInputStream fis = new FileInputStream(file);
DeflaterInputStream dis = new DeflaterInputStream(fis);
// Creating byte array for deflating the data
byte[] input = new byte[0];
int index = -1;
// Reads data from file
int iRead = 0;
while ((iRead = dis.read()) != -1) {
input = increaseArray(input);
input[++index] = (byte) iRead;
}
fos = new FileOutputStream("C:/Java/DeflatedMain.df1");
fos.write(input, 0, input.length);
fos.close();
System.out.println("File size after compression: " + input.length);
} catch (FileNotFoundException ex) {
Logger.getLogger(DeflaterInputApplication.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException ex) {
Logger.getLogger(DeflaterInputApplication.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Lớp DeflaterOutputStream
Lớp DeflaterOutputStream đọc dữ liệu nguồn, nén nó theo định dạng nén ‘deflate’ và sau đó, viết dữ liệu nén vào một luồng đầu ra được xác định trước đó. Nó cũng làm nhiệm vụ cơ bản cho các loại bộ lọc nén khác, chẳng hạn như GZIPOutputStream.
Cú pháp:
public DeflaterOutputStream(OutputStream in)
Constructor tạo một luồng đầu ra của byte để viết dữ liệu nén vào với máy nén và kích thước bộ đệm mặc định.
Bảng dưới liệt kê các phương thức khác nhau có sẵn trong lớp DeflaterOutputStream cùng với mô tả của chúng.
Phương Thức | Mô Tả |
---|---|
write(int buffer) | Ghi một byte dữ liệu đã được nén vào luồng đầu ra. |
write(byte[] buffer, int offset, int bufSize) | Ghi một mảng byte dữ liệu đã được nén vào luồng đầu ra. Ở đây, buffer là dữ liệu đầu vào cần được ghi dưới dạng byte, offset là vị trí bắt đầu, và bufSize là kích thước của buffer. |
deflate() | Nén dữ liệu nguồn và sau đó, ghi khối dữ liệu nén tiếp theo vào luồng đầu ra. |
close() | Đóng luồng đầu ra sau khi đã ghi xong dữ liệu nén còn lại. |
finish() | Hoàn thành quá trình viết dữ liệu nén vào luồng đầu ra mà không đóng nó. |
Ví dụ:
import java.io.*;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
public class DeflaterOutputStreamExample {
public static void main(String[] args) {
// Original string to be compressed
String originalString = "This is a sample string to be compressed using DeflaterOutputStream.";
// Compress the string
byte[] compressedData = compressData(originalString);
// Decompress the data
String decompressedString = decompressData(compressedData);
// Display the results
System.out.println("Original String: " + originalString);
System.out.println("Compressed Data: " + byteArrayToHexString(compressedData));
System.out.println("Decompressed String: " + decompressedString);
}
private static byte[] compressData(String data) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream)) {
// Write the data to the DeflaterOutputStream
deflaterOutputStream.write(data.getBytes());
// Close the DeflaterOutputStream
deflaterOutputStream.close();
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static String decompressData(byte[] compressedData) {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
InflaterInputStream inflaterInputStream = new InflaterInputStream(byteArrayInputStream)) {
// Read from the InflaterInputStream and convert to a string
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inflaterInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
return byteArrayOutputStream.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static String byteArrayToHexString(byte[] byteArray) {
StringBuilder hexString = new StringBuilder();
for (byte b : byteArray) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}
}
Ví dụ 2:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.DeflaterOutputStream;
public class DeflaterOutputApplication {
public static void main(String[] args) {
try {
File fileIn = new File("C:/Java/Hello.txt");
FileInputStream finRead = new FileInputStream(fileIn);
File fileOut = new File("C:/Java/DeflatedMain.df1");
FileOutputStream foutWrite = new FileOutputStream(fileOut);
DeflaterOutputStream deflWrite = new DeflaterOutputStream(foutWrite);
System.out.println("Original filesize: " + fileIn.length());
// Reading and writing the compressed data
int bytesRead;
while ((bytesRead = finRead.read()) != -1) {
deflWrite.write(bytesRead);
}
// Closing objects
deflWrite.close();
finRead.close();
System.out.println("File size after compression: " + fileOut.length());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Lớp InflaterInputStream và InflaterOutputStream
Lớp InflaterInputStream và InflaterOutputStream được kế thừa từ các lớp FilterInputStream và FilterOutputStream tương ứng.
Lớp InflaterInputStream
Lớp InflaterInputStream đọc dữ liệu được nén và giải nén nó trong định dạng nén ‘deflate’.
Cú pháp
public InflaterInputStream(InputStream in)
Constructor tạo ra một luồng đầu vào của các byte để đọc dữ liệu được nén với decompressor và kích thước bộ đệm mặc định.
Bảng dưới liệt kê các phương thức khả dụng trong lớp InflaterInputStream cùng với mô tả của chúng.
Phương thức read() | Trả về một byte dữ liệu giải nén được đọc từ luồng đầu vào. |
---|---|
read(byte[] buffer, int offset, int buffSize) | Trả về số byte dữ liệu giải nén được đọc vào một mảng byte từ vị trí bắt đầu được chỉ định bởi offset và kích thước buffSize. |
Ví dụ:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.InflaterInputStream;
public class InflaterDemo {
public static void main(String[] args) {
try {
File fout = new File("C:/DeflatedMain.df1");
FileInputStream finRead = new FileInputStream(fout);
File finf = new File("C:/InflatedMain.java");
FileOutputStream foutWrite = new FileOutputStream(finf);
InflaterInputStream inflaterInputStream = new InflaterInputStream(finRead);
System.out.println("File size before Inflation: " + fout.length());
// Inflating the file to the original size
int bytesRead;
while ((bytesRead = inflaterInputStream.read()) != -1) {
foutWrite.write(bytesRead);
}
finRead.close();
foutWrite.close();
System.out.println("File size after Inflation: " + finf.length());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Lớp InflaterOutputStream
Lớp InflaterOutputStream đọc dữ liệu được nén, giải nén dữ liệu được lưu trữ trong định dạng nén ‘deflate’ và sau đó, viết dữ liệu giải nén vào một luồng đầu ra. Lớp này cũng là lớp cơ sở cho lớp giải nén có tên GZIPInputStream.
Lớp InflaterOutputStream cung cấp các phương thức để giải nén các tệp được nén.
Cú pháp
public InflaterOutputStream(OutputStream out)
Constructor tạo một luồng đầu ra của các byte để viết dữ liệu giải nén với decompressor và kích thước bộ đệm mặc định.
Phương thức:
- close(): Đóng luồng đầu ra sau khi viết xong dữ liệu giải nén còn lại.
- finish(): Hoàn thành việc viết dữ liệu giải nén vào luồng đầu ra mà không đóng luồng đầu ra cơ bản.
Ví dụ:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.InflaterOutputStream;
public class InflaterOutputStreamApplication {
public static void main(String[] args) {
try {
// Writing decompressed data
File fin = new File("C:/Java/DeflatedMain.df1");
File fout = new File("C:/Java/InflatedMain.java");
FileOutputStream foutWrite = new FileOutputStream(fout);
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(foutWrite);
System.out.println("Original file size: " + fin.length());
FileInputStream finRead = new FileInputStream(fin);
// Reading and writing the decompressed data
int bytesRead;
while ((bytesRead = finRead.read()) != -1) {
inflaterOutputStream.write(bytesRead);
}
inflaterOutputStream.close();
finRead.close();
System.out.println("File size after Inflation: " + fout.length());
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Gói java.nio
API Java New Input/Output (NIO) đã được giới thiệu vào năm 2002 với J2SE 1.4 để tăng cường các nhiệm vụ xử lý đầu vào/đầu ra trong phát triển ứng dụng Java. Mặc dù, nó chưa được sử dụng đến tối đa, Java SE 7 đã giới thiệu thêm API New Input/Output (NIO.2). Java SE 16 tiếp tục hỗ trợ điều này.
Mục tiêu chính cho cả NIO và NIO.2 vẫn giữ nguyên, đó là tăng cường các nhiệm vụ xử lý I/O trong phát triển ứng dụng Java. Sử dụng của chúng có thể giảm thời gian cần thiết cho một số thao tác I/O thông thường,
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 tiết lộ các điểm nhập hệ thống cấp thấp phụ thuộc vào hệ điều hành (OS). Chúng cũng cung cấp kiểm soát lớn hơn về I/O. Một khía cạnh khác của NIO là sự chú ý đến sự biểu đạt của ứng dụng.
NIO phụ thuộc vào nền tảng và khả năng tăng cường hiệu suất ứng dụng của nó phụ thuộc vào các yếu tố sau:
- Hệ điều hành cụ thể (OS)
- JVM cụ thể
- Đặc điểm lưu trữ lớn
- Dữ liệu
- Ngữ cảnh ảo hóa máy chủ
Dưới đây là những tính năng chính của các API NIO:
Bảng mã và Bộ giải mã, Bộ mã hóa liên quan của Chúng: Chúng dịch dữ liệu giữa byte và ký tự Unicode. API bảng mã được định nghĩa trong gói java.nio.charset. Gói charset linh hoạt hơn so với phương thức getBytes(), dễ triển khai hơn và mang lại hiệu suất xuất sắc,
Bộ đệm: Đây là các container cho dữ liệu. Các lớp bộ đệm được định nghĩa trong gói ong NIO, các đối tượng của lớp và được sử dụng bởi tất cả các API NIO.
Các Loại Kênh: Đại diện cho các kết nối với các thực thể có thể thực hiện các hoạt động I/O. Các kênh là các tệp trừu tượng và socket và hỗ trợ I/O không đồng bộ.
Bộ chọn và khóa lựa chọn: Cùng với các kênh có thể chọn, chúng xác định một cơ sở dữ liệu I/O không đồng bộ được nhiều lựa chọn. I/O không đồng bộ dựa trên sự kiện, điều này có nghĩa là có một bộ chọn được định nghĩa cho một kênh I/O và sau đó, quá trình xử lý xảy ra. Khi một sự kiện xảy ra như sự xuất hiện của một đầu vào, trên bộ chọn, bộ chọn sẽ thức dậy và thực thi. Điều này có thể thực hiện bằng một luồng đơn. Các API kênh và bộ chọn được định nghĩa trong gói java.nio.channels.
Lưu ý: Mỗi gói con có gói 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ợ đầu vào/đầu ra tệp tin. Chúng cũng giúp truy cập hệ thống tệp mặc định.
Hệ Thống Tệp, Đường Dẫn và Tệp Tin
Hệ thống tệp lưu trữ và tổ chức các tệp tin trên phương tiện truyền thông, thường là ổ đĩa cứng. Những tệp tin này có thể dễ dàng được truy xuất. Mỗi tệp tin đều có một đường dẫn thông qua hệ thống tệp.
Hệ Thống Tệp
Thường, các tệp tin được lưu trữ trong một cấu trúc phân cấp, trong đó có một nút gốc. Dưới nút này là các tệp tin và thư mục. Mỗi thư mục có thể chứa các tệp tin và thư mục con, và cứ như vậy. Không có giới hạn về cấu trúc phân cấp. Hệ thống tệp có thể có một hoặc nhiều thư mục gốc. Hệ thống tệp có các đặc điểm khác nhau đối với bộ phân cách đường dẫn.
Trong NIO, các đối tượng của lớp java.nio.file.Path đại diện cho vị trí tương đối hoặc tuyệt đối của một tệp hoặc thư mục.
Đường Dẫn
Mỗi tệp tin đượ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 tệp. 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 tương ứng vớ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, được biểu thị bằng ký tự gạch chéo, /.
Hệ thống tệp bao gồm các đặc điểm khác nhau đối với bộ phân cách đường dẫn. Ký tự phân cách tên thư mục được gọi là ký tự ngăn cách. Điều này là đặc trưng cho hệ thống tệp. Ví dụ, Microsoft Windows sử dụng dấu gạch chéo () và hệ điều hành Solaris sử dụng dấu gạch chéo xuôi (/).
hình ảnh dưới mô tả cấu trúc thư mục
Dựa trên hình, 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 thị là /home/user/dailySalesReport.
Trong Microsoft Windows, đường dẫn cho dailySalesReport sẽ được biểu thị là 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 tin. Ví dụ, /home/user/dailySalesReport là một đường dẫn tuyệt đối. Trong đó, tất cả thông tin cần để định vị tệp tin đều được bao gồm trong chuỗi đường dẫn.
Một đường dẫn tương đối không bao gồm danh sách thư mục đầy đủ. Ví dụ, user/dailySalesReport. Nó phải được sử dụng với một đường dẫn khác để truy cập một tệp tin. Mà không có thông tin thêm, một chương trình sẽ không thể định vị tệp tin.
Tệp Tin
NIO.2 bao gồm các gói và lớp mới sau:
- java.nio.file.Path: Sử dụng một đường dẫn phụ thuộc vào hệ thống để định vị một tệp tin hoặc thư mục.
- java.nio.file.Files: Sử dụng một đối tượng Path để thực hiện các hoạt động trên các tệp tin và thư mục.
- java.nio.file.FileSystem: Cung cấp một giao diện cho hệ thống tệp tin. Nó cũng giúp tạo ra một đối tượng Path và các đối tượng khác để truy cập hệ thống tệp tin.
Lưu ý: Tất cả các phương thức truy cập hệ thống tệp tin đều ném ra IOException hoặc một lớp con của nó.
Sự khác biệt giữa NIO.2 và java.io.File là cách truy cập hệ thống tệp tin. Trong lớp java.io, các phương thức để điều chỉnh 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 tệp tin và thư mục.
Với NIO.2, hai quá trình đã được tách rời. Trong NIO.2, là giao diện Path giúp tạo và kiểm soát các đường dẫn. Là lớp Files thực hiện các hoạt động trên các tệp tin 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 tin sẽ ném IOException.
Giao Diện Path
Đối tượng giao diện java.nio.file.Path có thể giúp định vị một tệp tin trong hệ thống tệp tin. Thông thường, giao diện đại diện cho một đường dẫn tệp tin phụ thuộc vào hệ thống. Nói cách khác, nó cung cấp điểm nhập cho việc điều chỉnh tệp tin và thư mục.
Một đường dẫn là phân cấp. Nó bao gồm một chuỗi các thư mục và tên tệp tin. Những thư mục và tên tệp tin này được phân tách bằng một dấu phân cách. Dưới đây là những đặc điểm của một 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 tệp tin phân cấp.
- Tên của một tệp tin hoặc thư mục là thành phần tên cách xa nhất từ gốc của cấu trúc thư mục.
- Các thành phần tên khác bao gồm các tên thư mục.
Một Path có thể đại diện cho những điều sau:
- Một gốc
- Một gốc và một chuỗi tên
- Một hoặc nhiều thành phần tên
Một Path là trống nếu nó chỉ bao gồm một thành phần tên trống.
Lưu ý: Việc truy cập một tệp tin bằng cách sử dụng một đườ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 tin.
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à tĩnh và cuối cùng và có phương thức getDefault().
Dưới đây là một số phương thức của giao diện Path có thể được nhóm dựa trên chức năng chung:
- Để truy cập các thành phần đường dẫn hoặc một chuỗi con của các thành phần tên của nó, có thể sử dụng các phương thức getFileName(), getParent(), getRoot(), và subpath().
- Để kết hợp các đường dẫn, giao diện 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, có thể sử dụng phương thức relativize().
- Để so sánh và kiểm tra các đường dẫn, có thể sử dụng các phương thức startsWith() và endsWith().
Lưu ý: Một thư mục được đặt tại một đường dẫn có thể được theo dõi dịch vụ và các mục trong thư mục có thể được quan sát.
Để có được một đối tượng Path, hãy lấy một phiên bản của hệ thống tệp tin mặc định. Tiếp theo, gọi phương thức getPath().
Ví dụ:
import java.nio.file.*;
public class PathExample {
public static void main(String[] args) {
// Create a Path object representing a file path
Path filePath = Paths.get("C:/example/data.txt");
// Print information about the file path
System.out.println("File Path: " + filePath);
System.out.println("File Name: " + filePath.getFileName());
System.out.println("Parent Directory: " + filePath.getParent());
System.out.println("Number of Name Elements in Path: " + filePath.getNameCount());
// Convert the path to an absolute path
Path absolutePath = filePath.toAbsolutePath();
System.out.println("Absolute Path: " + absolutePath);
// Resolve a relative path against the base path
Path relativePath = Paths.get("images/photo.jpg");
Path resolvedPath = filePath.resolve(relativePath);
System.out.println("Resolved Path: " + resolvedPath);
// Check if the file exists
boolean fileExists = Files.exists(filePath);
System.out.println("File Exists: " + fileExists);
// Check if the path is a directory
boolean isDirectory = Files.isDirectory(filePath);
System.out.println("Is Directory: " + isDirectory);
}
}
Đối tượng Path sau khi được tạo ra không thể thay đổi. Nói cách khác, chúng là không thay đổi.
Đối với các hoạt động trên Path, nếu hệ thống tệp tin mặc định được sử dụng thì cần sử dụng tiện ích Paths. Hệ thống tệp tin mặc định là hệ thống tệp tin mà JVM đang chạy trên đó. Đó là cách ngắn gọn hơn. Đối với các hệ thống tệp tin khác (không phải hệ thống mặc định), hãy lấy một phiên bản của hệ thống tệp tin và xây dựng các đối tượng Path để thực hiện các hoạt động trên Path.
Dưới đây là một số phương thức phổ biến của lớp Path:
subpath()
Một phần của đường dẫn có thể được lấy bằng cách sử dụng phương thức subpath().
Cú pháp là:
Path subpath(int startIndex, int endIndex);
Ở đây,
- startIndex: đại diện cho giá trị bắt đầu.
- endIndex: đại diện cho giá trị kết thúc (index cuối cùng là endIndex – 1).
Đoạn mã dưới thể hiện cách sử dụng phương thức subpath():
Path pathObj = Paths.get("C:/java/Hello.txt");
Path subPathObj = pathObj.subpath(0, 2);
Trong đoạn mã này, tên phần tử gần nhất với root có giá trị index là 0. Phần tử xa root nhất có giá trị index là index count – 1. Do đó, kết quả của đoạn mã 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 không đầy đủ và nối đường dẫn không đầy đủ này vào đường dẫn gốc. Đoạn mã dưới sử dụng phương thức resolve() để kết hợp hai đường dẫn.
Path pathObj = Paths.get("/java/test");
Path resultPath = pathObj.resolve("bar");
Kết quả của đoạn mã này sẽ là /java/test/bar.
Nếu bạn truyền một đường dẫn tuyệt đối cho phương thức resolve(), nó sẽ trả về đường dẫn được truyền. Đoạn mã dưới trả về /java/test:
Paths.get("bar").resolve("/java/test");
relativize()
Phương thức relativize() giúp xây dựng một đường dẫn từ một vị trí trong hệ thống tệp tin đến một vị trí khác. Đoạn mã dưới minh họa điều này.
Path pathObj = Paths.get("user");
Path pathTo = Paths.get("sales");
Path relativePath = pathObj.relativize(pathTo);
Kết quả sẽ là /sales.
Lưu ý: Đường dẫn mới là tương đối với đường dẫn gốc. Đường dẫn bắt nguồn từ đường dẫn gốc và kết thúc tại vị trí được xác định bởi đường dẫn được truyền vào.
Theo dõi Thay Đổi Hệ Thống Tệp và Thư Mục bằng lớp java.nio.file.Files
Lớp java.nio.file.Files chứa các phương thức tĩnh thực hiện các chức năng chính cho các đối tượng Path. Những phương thức này có thể nhận diện và quản lý các liên kết biểu tượng tự động.
Dưới đây là một số thao tác quan trọng:
Sao Chép Một Tệp hoặc Thư Mục:
Để sao chép một tệp hoặc thư mục, bạn có thể sử dụng phương thức copy(Path, Path, CopyOption…). Cân nhắc những điểm sau khi sao chép:
- Nếu tệp đích đã tồn tại, quá trình sao chép sẽ thất bại trừ khi có tùy chọn REPLACE_EXISTING.
- 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 biểu tượng, đích của liên kết sẽ đượ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.
Tùy Chọn Sao Chép:
COPY_ATTRIBUTES
: Tùy chọn này sao chép các thuộc tính của tệp nguồn sang tệp đích. Các thuộc tính của tệp phụ thuộc vào hệ thống tệp và phụ thuộc vào nền tảng. Tuy nhiên, thời gian sửa đổi cuối cùng được hỗ trợ trên mọi nền tảng và được sao chép sang tệp đích.NOFOLLOW_LINKS
: Sử dụng tùy chọn này khi các liên kết biểu tượng không nên được theo dõi. Nó chỉ sao chép liên kết nếu tệp cần sao chép là một liên kết biểu tượng và đích của liên kết không được sao chép.REPLACE_EXISTING
: Tùy chọn này sao chép tệp ngay cả khi tệp đích đã tồn tại. Nếu đích là một liên kết biểu tượng, liên kết sẽ được sao chép, nhưng đích của liên kết sẽ không được sao chép. Nếu đích là một thư mục không rỗng, quá trình sao chép sẽ thất bại và ném mộtFileAlreadyExistsException
.
- Files.copy(InputStream in, Path target, CopyOption… options):
- Mô tả: Phương thức này sao chép tất cả byte từ một
InputStream
đến một tệp được chỉ định bởi tham sốPath
(target
). - Tham số:
in
: Đại diện choInputStream
để đọc từ.target
: Đại diện cho đường dẫn đến tệp.options
: Chỉ định cách thức sao chép. Đây là tham số tùy chọn, bạn có thể cung cấp một hoặc nhiều giá trịCopyOption
.
- Ngoại lệ: Phương thức này ném các ngoại lệ như
IOException
,FileAlreadyExistsException
,DirectoryNotEmptyException
,UnsupportedOperationException
, vàSecurityException
. Ví dụ:
import java.io.*;
import java.nio.file.*;
public class CopyInputStreamToFileExample {
public static void main(String[] args) throws IOException {
try (InputStream inputStream = new FileInputStream("source.txt")) {
Path targetPath = Paths.get("destination.txt");
Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
- Files.copy(Path source, OutputStream out):
- Mô tả: Phương thức này sao chép tất cả byte từ một tệp được chỉ định bởi tham số
Path
(source
) đến mộtOutputStream
. - Tham số:
source
: Đại diện cho đường dẫn đến tệp.out
: Đại diện choOutputStream
mà byte sẽ được ghi vào.
- Ngoại lệ: Phương thức này ném
IOException
vàSecurityException
. Ví dụ:
import java.io.*;
import java.nio.file.*;
public class CopyFileToOutputStreamExample {
public static void main(String[] args) throws IOException {
Path sourcePath = Paths.get("source.txt");
try (OutputStream outputStream = new FileOutputStream("destination.txt")) {
Files.copy(sourcePath, outputStream);
}
}
}
Ví dụ:
import java.nio.file.*;
public class FileCopyExample {
public static void main(String[] args) {
// Source and destination file paths
Path sourcePath = Paths.get("source.txt");
Path destinationPath = Paths.get("destination.txt");
try {
// Copy the file from source to destination
Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File copied successfully!");
} catch (Exception e) {
System.err.println("Error copying the file: " + e.getMessage());
}
}
}
Chuyển một tập tin hoặc thư mục
Để thực hiện việc này, sử dụng phương thức move(Path, Path, CopyOption...)
. Nếu tập tin đích tồn tại, tùy chọn REPLACE_EXISTING
nên được sử dụng. Nếu không, quá trình di chuyển sẽ thất bại. Phương thức này nhận một đối số biến đổi.
Các 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 di chuyển nội dung.
Phương thức move(Path source, Path target, CopyOption... options)
hỗ trợ các hằng số liệt kê StandardCopyoption
sau:
REPLACE_EXISTING
: Thay thế tập tin đích ngay cả khi tập tin đích tồn tại cho một thư mục không trống. Nếu thư mục đích là một liên kết tượng trưng, chỉ có liên kết tượng trưng được thay thế. Thư mục mục tiêu không được thay thế._AToMIC_MOVE
: Di chuyển thư mục và tập tin như một hoạt động hệ thống tệp hợp nhất. Sử dụng tùy chọn này để di chuyển một tập tin vào một thư mục. Bất kỳ quy trình nào sau đó quan sát các thư mục này sẽ truy cập một tập tin hoàn chỉnh.
Chú ý:
- Ngoại lệ được ném nếu hệ thống tệp không hỗ trợ di chuyển nguyên tử.
Cú pháp dưới đây thể hiện phương thức move()
.
import static java.nio.file.StandardCopyOption.*;
Files.move(source, target, REPLACE_EXISTING);
Trong đó:
source
đại diện cho đường dẫn đến tập tin cần di chuyển.target
đại diện cho đường dẫn đến tập tin đích.options
chỉ định cách di chuyển nên được thực hiện.
Dưới đâ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 rỗng, thì 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ì quá trình di chuyển sẽ thành công.
- Nếu thư mục đích tồn tại nhưng không trống, thì sẽ ném ra ngoại lệ
DirectoryNotEmptyException
. - Nếu nguồn là một tập tin, thư mục đích tồn tại và
REPLACE_EXISTING
được đặt, thì quá trình di chuyển sẽ đổi tên tập tin thành tên thư mục mong muốn.
Ví dụ:
import java.io.IOException;
import java.nio.file.*;
public class FilesMoveExample {
public static void main(String[] args) {
// Source file path
Path sourcePath = Paths.get("source.txt");
// Destination file path (rename or move)
Path destinationPath = Paths.get("destination.txt");
try {
// Move or rename the file
Files.move(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File moved successfully!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Kiểm tra một tập tin hoặc thư mục
Để làm điều này, hệ thống tệp cần được truy cập bằng cách sử dụng các phương thức của Files
để xác định xem một đường dẫn cụ thể có tồn tại không. Các phương thức trong lớp Path
hoạt động trên đối tượng Path
.
Dưới đây là các phương thức của Files
để kiểm tra sự tồn tại của đối tượng Path
:
exists(Path, LinkOption... opt)
: Phương thức này kiểm tra xem tập tin có tồn tại không. Mặc định, nó sử dụng các liên kết tượng trưng.notExists(Path, LinkOption...)
: Phương thức này kiểm tra xem tập tin có không tồn tại.Files.exists(path)
không tương đương vớiFiles.notExists(path)
. Khi kiểm tra sự tồn tại của một tập tin, có thể có một trong những kết quả sau:- Tập tin được xác minh không tồn tại.
- Tập tin được xác minh tồn tại.
- Sự tồn tại của tập tin không thể được xác minh. Điều này xảy ra nếu cả
exists
vànotExists
đều trả về false. - Tình trạng của tập tin là 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 tin.
Một hệ thống tệp có thể sử dụng hai đường dẫn khác nhau để xác định cùng một tập tin. Đ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ó trỏ đến cùng một tập tin trên hệ thống tệp hay không.
Ví dụ:
import java.io.File;
public class FileExistsExample {
public static void main(String[] args) {
// Provide the file path
String filePath = "C:/Path/To/Your/File.txt";
// Check if the file exists
if (fileExists(filePath)) {
System.out.println("The file exists.");
} else {
System.out.println("The file does not exist.");
}
}
private static boolean fileExists(String filePath) {
// Create a File object
File file = new File(filePath);
// Check if the file exists
return file.exists();
}
}
Xóa một tập tin hoặc thư mục
Phương thức delete(Path)
có thể được sử dụng để xóa một tập tin, thư mục hoặc liên kết. Việc xóa sẽ thất bại nếu thư mục không trống. Phương thức ném một ngoại lệ nếu quá trình xóa thất bại. Nếu tập tin không tồn tại, sẽ được ném một NoSuchFileException
. Để xác định lý do thất bại,
import java.io.IOException;
import java.nio.file.*;
public class FilesDeleteExample {
public static void main(String[] args) {
// File or directory path to be deleted
Path filePath = Paths.get("fileToDelete.txt");
try {
// Delete the file or directory
Files.delete(filePath);
System.out.println("File or directory deleted successfully!");
} catch (NoSuchFileException e) {
System.err.println("File or directory does not exist.");
} catch (DirectoryNotEmptyException e) {
System.err.println("Cannot delete non-empty directory.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ví dụ phương thức deleteIfExists()
import java.io.IOException;
import java.nio.file.*;
public class FilesDeleteIfExistsExample {
public static void main(String[] args) {
// File or directory path to be deleted
Path filePath = Paths.get("fileToDeleteIfExists.txt");
try {
// Delete the file or directory if it exists
boolean deleted = Files.deleteIfExists(filePath);
if (deleted) {
System.out.println("File or directory deleted successfully!");
} else {
System.out.println("File or directory does not exist.");
}
} catch (DirectoryNotEmptyException e) {
System.err.println("Cannot delete non-empty directory.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Liệt kê nội dung thư mục
Để làm điều này, sử dụng lớp DirectoryStream
để lặp qua tất cả các tập tin và thư mục từ bất kỳ Path
thư mục nào. Cân nhắc về các điểm sau:
DirectoryIteratorException
được ném nếu có lỗi /O trong quá trình lặp qua các mục trong thư mục đã chỉ định.PatternSyntaxException
được ném khi biểu thức chính quy không hợp lệ.
Ví dụ:
import java.io.IOException;
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("D:/resources");
try (DirectoryStream<Path> dirStreamObj = Files.newDirectoryStream(pathObj, "*.java")) {
// Iterate over the files in the directory
for (Iterator<Path> itrObj = dirStreamObj.iterator(); itrObj.hasNext();) {
Path fileObj = itrObj.next();
System.out.println(fileObj.getFileName());
}
} catch (IOException ex) {
// Handle IO exception
System.err.println(ex.getMessage());
}
}
}
Tạo mới thư mục
Files.createDirectory(Path dir)
là phương thức đượ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. Đoạn mã dưới minh họa điều này.
Files.createDirectories(Paths.get("/java/test/example"));
Khi thực thi, nó sẽ tạo ra các thư mục theo thứ tự đã cho: java
, test
, và example
. Thư mục test
sẽ được tạo bên trong java
và example
sẽ được tạo bên trong thư mục test
.
Để tạo một tập tin, sử dụng phương thức createFile
.
Đọc và ghi tệp tin
Để đọc từ tệp tin, sử dụng các phương thức readAllBytes
hoặc readAllLines
để đọc toàn bộ nội dung của tệp.
Đoạn mã 46 thể hiện cách sử dụng phương thức readAllBytes
:
Path pathObj = Paths.get("E:/MyFile.txt");
byte[] fileArray;
fileArray = Files.readAllBytes(pathObj);
Để viết bytes hoặc dòng vào một tệp tin, sử dụng các phương thức sau:
write(Path p, byte[] b, OpenOption...)
: Phương thức này ghi bytes vào một tệp tin.
Path file = ...;
byte[] buf = ...;
Files.write(file, buf);
Một số StandardOpenOption
hỗ trợ bao gồm WRITE
, APPEND
, TRUNCATE_EXISTING
, CREATE_NEW
, và CREATE
.
write(Path p, Iterable<? extends CharSequence> lines, Charset charset, OpenOption... options)
: Phương thức này ghi các dòng văn bản vào một tệp tin.
Path file = ...;
List<String> lines = ...;
Files.write(file, lines, StandardCharsets.UTF_8);
Cả hai phương thức trên đều ném ra các ngoại lệ như IOException
, UnsupportedOperationException
, và SecurityException
.
java.nio.file package hỗ trợ I/O theo kiểu channel.
Ví dụ:
import java.io.IOException;
import java.nio.file.*;
public class FilesReadAndWriteExample {
public static void main(String[] args) {
// File path
Path filePath = Paths.get("example.txt");
// Data to be written to the file
byte[] dataToWrite = "Hello, Files API!".getBytes();
try {
// Write data to the file using Files.write
Files.write(filePath, dataToWrite);
System.out.println("Data has been written to the file successfully.");
// Read data from the file using Files.readAllBytes
byte[] readData = Files.readAllBytes(filePath);
// Convert the byte array to a string and print
String content = new String(readData);
System.out.println("Content read from the file: " + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Đọc tệp tin bằng cách sử dụng Buffered Stream
Phương thức newBufferedReader(Path, Charset)
mở một tệp tin để đọc. Nó trả về một đối tượng BufferedReader
giúp đọc văn bản từ một tệp tin một cách hiệu quả.
Ví dụ:
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class BufferedReaderExample {
public static void main(String[] args) {
// Specify the path to the text file
Path filePath = Paths.get("path/to/your/textfile.txt");
// Try-with-resources to ensure the BufferedReader is closed
try (BufferedReader reader = Files.newBufferedReader(filePath)) {
String line;
// Read and print each line from the file
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ghi tệp tin bằng cách sử dụng Buffered Stream
Phương thức newBufferedWriter(Path, Charset, OpenOption...)
có thể được sử dụng để ghi vào một tệp tin bằng cách sử dụng BufferedWriter
.
Ví dụ:
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class BufferedWriterExample {
public static void main(String[] args) {
// Specify the path to the text file
Path filePath = Paths.get("path/to/your/outputfile.txt");
// Data to be written to the file
List<String> linesToWrite = Arrays.asList("Hello, World!", "This is a new line.", "Writing to a file in Java.");
// Try-with-resources to ensure the BufferedWriter is closed
try (BufferedWriter writer = Files.newBufferedWriter(filePath)) {
// Write each line to the file
for (String line : linesToWrite) {
writer.write(line);
writer.newLine(); // Add a newline character after each line
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Đọc ghi chuỗi từ tệp tin
Lớp FileWriter
được sử dụng để ghi dữ liệu vào các tệp văn bản trong Java. Để đọc dữ liệu từ các tệp văn bản, hãy sử dụng FileReader
. Cả hai lớp này đều thuộc về các lớp dòng ký tự. FileInputStream
và FileOutputStream
không nên được sử dụng để đọc/ghi dữ liệu văn bản vì chúng là các lớp dòng byte không phù hợp cho các hoạt động dựa trên ký tự.
FileWriter
Các Constructor của FileWriter
:
- FileWriter(File file): Xây dựng một đối tượng
FileWriter
cho một đối tượng tệp cụ thể. - FileWriter(File file, boolean append): Xây dựng một đối tượng
FileWriter
cho một đối tượng tệp cụ thể, với giá trị boolean chỉ định liệu có nối dữ liệu hay không. - FileWriter(FileDescriptor fd): Xây dựng một đối tượng
FileWriter
liên kết với một bộ mô tả tệp cụ thể. - FileWriter(String fileName): Xây dựng một đối tượng
FileWriter
cho một tên tệp cụ thể. - FileWriter(String fileName, boolean append): Xây dựng một đối tượng
FileWriter
cho một tên tệp cụ thể, với giá trị boolean chỉ định liệu có nối dữ liệu hay không.
Các Phương thức của FileWriter:
- public void write(int c) throws IOException: Sử dụng để ghi một ký tự duy nhất.
- public void write(char[] cbuf) throws IOException: Sử dụng để ghi một mảng ký tự.
- public void write(String str) throws IOException: Sử dụng để ghi dữ liệu của một chuỗi.
- public void write(String str, int off, int len) throws IOException: Sử dụng để ghi một phần của một chuỗi.
- public void flush() throws IOException: Sử dụng để làm rỗng luồng.
- public void close() throws IOException: Sử dụng để đóng người ghi sau khi làm rỗng.
FileWriter
ghi một ký tự mỗi lần và tương tự, đọc mỗi ký tự một lần dẫn đến sự tăng của các quy trình I/O. Do đó, nó ảnh hưởng đến hoạt động của hệ thống. Sử dụng BufferedWriter
với FileWriter
tăng tốc độ thực thi.
FileReader
FileReader
đọc từng ký tự từ một tệp văn bản:
FileReader
được kế thừa từ lớpInputStreamReader
.FileReader
giả định rằng bảng mã mặc định và kích thước bộ đệm byte là phù hợp. Bạn có thể chỉ định các giá trị này bằng cách xây dựng mộtInputStreamReader
qua mộtInputStream
.FileReader
được sử dụng để đọc luồng ký tự, trong khiFileInputStream
đọc luồng dữ liệu byte thô.
Các Constructor của FileReader:
- FileReader(File file): Sử dụng để tạo
FileReader
cho tệp đã cho để đọc. - FileReader(FileDescriptor fd): Sử dụng để tạo
FileReader
mới cho bộ mô tả tệp đã cho. - FileReader(String fileName): Sử dụng để tạo
FileReader
mới cho tên tệp đã cho.
Các Phương thức của FileReader
:
- public int read() throws IOException: Sử dụng để đọc một ký tự duy nhất, chặn cho đến khi có một ký tự có sẵn, có lỗi I/O xảy ra hoặc nó đến cuối luồng.
- public int read(char[] cbuf) throws IOException: Đọc ký tự vào một mảng, chặn cho đến khi đầu vào có sẵn, có lỗi I/O xảy ra, hoặc nó tìm thấy cuối luồng.
- public abstract int read(char[] buff, int off, int len) throws IOException: Đọc ký tự vào một phần của một mảng, chặn cho đến khi đầu vào có sẵn, có lỗi I/O xảy ra, hoặc nó gần cuối luồng.
Các tham số cho các phương thức FileReader
bao gồm:
- cbuf (bộ đệm đích)
- off (điểm bắt đầu để lưu trữ ký tự)
- len (số ký tự tối đa để đọc).
Ngoài ra, phương thức close
được sử dụng để đóng người đọc, và phương thức skip
được sử dụng để bỏ qua các ký tự, chặn cho đến khi có một ký tự có sẵn, có lỗi I/O xảy ra, hoặc nó gần cuối
Bài tập
Question 1:
Create Car Class and Car Manager class following the requirements:
### `Car` Class:
1. **Properties:**
- `carModel` (String): Represents the model of the car.
- `make` (String): Represents the make or manufacturer of the car.
- `year` (int): Represents the manufacturing year of the car.
2. **Constructors:**
- Default constructor: Initializes the `Car` object with default values.
- Parameterized constructor: Initializes the `Car` object with specific values for `carModel`, `make`, and `year`.
3. **Getters and Setters:**
- Getter and setter methods for each property (`getCarModel()`, `setCarModel()`, `getMake()`, `setMake()`, `getYear()`, `setYear()`).
4. **Serializable Interface:**
- Implements the `Serializable` interface to allow the object to be serialized and deserialized.
5. **toString() Method Override:**
- Overrides the `toString()` method to provide a meaningful string representation of the `Car` object.
### `CarManager` Class:
1. **Main Method:**
- The `main` method acts as the entry point for the program.
2. **List of Cars:**
- Creates an `ArrayList` to store objects of the `Car` class.
3. **Input Car Information:**
- Uses a loop to input information for at least three cars from the keyboard.
- Utilizes a separate method (`inputCarInfo()`) to handle the input process.
4. **Car Model Validation:**
- Validates the car model using a regular expression.
- A method (`isValidCarModel()`) is used to perform the validation.
5. **Serialize Cars:**
- Serializes the list of `Car` objects and writes them to a file named "cars.txt".
- Uses a method (`serializeCars()`) to perform the serialization.
6. **Deserialize Cars:**
- Reads the information from the "cars.txt" file and deserializes it into a list of `Car` objects.
- Uses a method (`deserializeCars()`) to perform the deserialization.
7. **Display Cars:**
- Displays the information of cars read from the file on the screen.
- Uses a method (`displayCars()`) for this purpose.
### General Notes:
- Input for the car model should be validated using a regular expression to ensure a valid format (e.g., only uppercase letters and numbers are allowed).
- The `Car` and `CarManager` classes should handle exceptions appropriately, such as `IOException` and `NumberFormatException`.
- The program should provide clear prompts and error messages during the input process.
- The classes should be well-documented with comments to explain the purpose of each method and any relevant details.
Question 2:
- The first Thread: After a 5 second, returns a random number from 1 to 10 and display it into the screen .
- The second Thread: Gets the number of first thread returned and display corresponding text.
Example:
3
three
1
one
5
five
...
- Write main() method with infinitive loop to Demo these threads. When thread 1 number is 10, program exit.
- You must synchronize two threads above.