Trong lập trình Java, luồng byte (byte stream) là cơ chế nền tảng để thao tác với dữ liệu nhị phân, bao gồm cả văn bản và tài nguyên đa phương tiện như ảnh, video. Các lớp chính thuộc nhóm này được định nghĩa trong gói java.io, với hai lớp cốt lõi là FileInputStream và FileOutputStream.
Các phương thức ghi dữ liệu bằng luồng byte
Luồng đầu ra byte hỗ trợ ba cách phổ biến để ghi dữ liệu:
- Ghi từng byte riêng lẻ: sử dụng
write(int b)— chỉ ghi byte thấp nhất của giá trị truyền vào. - Ghi toàn bộ mảng byte: dùng
write(byte[] b)để ghi toàn bộ nội dung mảng. - Ghi một phần mảng: thông qua
write(byte[] b, int off, int len), cho phép chỉ định vị trí bắt đầu và độ dài cần ghi.
Ví dụ minh họa:
import java.io.*;
public class ByteStreamWriter {
public static void main(String[] args) throws IOException {
try (FileOutputStream output = new FileOutputStream("data/output.bin")) {
// Cách 1: Ghi từng ký tự ASCII
output.write('J');
output.write('a');
output.write('v');
output.write('a');
// Cách 2: Ghi chuỗi dưới dạng mảng byte
byte[] content = " - Hello World".getBytes(StandardCharsets.UTF_8);
output.write(content);
// Cách 3: Chỉ ghi phần con "World" từ mảng trên
output.write(content, 11, 5); // offset=11, length=5
}
}
}Thêm dòng mới và ghi nối tiếp
Để xuống dòng khi ghi, chèn byte tương ứng với ký tự xuống dòng của hệ điều hành: \n (LF, Unix/Linux/macOS) hoặc \r\n (CRLF, Windows). Để ghi nối tiếp (append), khởi tạo FileOutputStream với tham số thứ hai là true:
FileOutputStream appendStream = new FileOutputStream("log.txt", true);Xử lý ngoại lệ an toàn
Nên sử dụng khối try-with-resources để đảm bảo đóng luồng tự động, tránh rò rỉ tài nguyên — đặc biệt quan trọng khi làm việc với tệp lớn hoặc trong môi trường sản xuất.
Đọc dữ liệu từ luồng byte
Hai kỹ thuật phổ biến:
- Đọc từng byte: gọi
read(), trả về giá trịinttừ0đến255, hoặc-1nếu hết dữ liệu. - Đọc theo khối: dùng
read(byte[] b)hoặcread(byte[] b, int off, int len)để tăng hiệu suất đáng kể.
Sao chép tệp nhị phân
Do luồng byte không phụ thuộc vào mã hóa, chúng phù hợp để sao chép mọi loại tệp — từ văn bản thuần đến ảnh JPEG, video MP4 hay file nén ZIP. Mẫu thiết kế chuẩn là đọc từng khối (ví dụ: 8192 byte) và ghi ngay lập tức vào đích.
Hiệu năng với luồng đệm
Để tối ưu tốc độ I/O, nên bao bọc luồng cơ sở bằng BufferedInputStream và BufferedOutputStream. Các lớp này duy trì vùng đệm trong bộ nhớ giúp giảm số lần truy cập đĩa.
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BufferedCopyExample {
public static void main(String[] args) throws IOException {
// Ghi có đệm
try (BufferedOutputStream bufferedOut = new BufferedOutputStream(
new FileOutputStream("buffered.txt"))) {
bufferedOut.write("Dòng đầu tiên".getBytes(StandardCharsets.UTF_8));
bufferedOut.write("\n".getBytes());
bufferedOut.write("Dòng thứ hai".getBytes(StandardCharsets.UTF_8));
}
// Đọc có đệm
try (BufferedInputStream bufferedIn = new BufferedInputStream(
new FileInputStream("buffered.txt"))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bufferedIn.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, bytesRead, StandardCharsets.UTF_8));
}
}
}
}