"Xin chào, Amigo! Hôm nay chúng ta sẽ làm quen với các luồng đầu vào/đầu ra . Chúng tôi đã chọn chủ đề này cách đây vài ngày, nhưng hôm nay chúng ta sẽ tìm hiểu kỹ về nó. Các luồng đầu vào/đầu ra được chia thành 4 loại:"

1) Các luồng được phân chia theo hướng: luồng vàoluồng ra

2) Các luồng được chia theo loại dữ liệu của chúng: luồng hoạt động với byte và luồng hoạt động với ký tự .

Ở đây các bộ phận này được thể hiện trong một bảng:

luồng đầu vào luồng đầu ra
Hoạt động với byte Dòng đầu vào Dòng đầu ra
Hoạt động với các ký tự Người đọc nhà văn

Nếu một đối tượng triển khai giao diện InputStream , thì nó hỗ trợ khả năng đọc tuần tự các byte từ nó.

Nếu một đối tượng triển khai giao diện OutputStream , thì nó hỗ trợ khả năng ghi tuần tự các byte vào nó.

Nếu một đối tượng triển khai giao diện Reader , thì nó hỗ trợ khả năng đọc tuần tự các ký tự (ký tự) từ nó.

Nếu một đối tượng triển khai giao diện Writer , thì nó hỗ trợ khả năng ghi tuần tự các ký tự (ký tự) vào nó.

Luồng vào/ra - 1

Một luồng đầu ra giống như một máy in. Chúng tôi có thể xuất tài liệu ra máy in. Chúng ta có thể xuất dữ liệu ra một luồng đầu ra.

Về phần mình, luồng đầu vào có thể được so sánh với máy quét hoặc có thể là ổ cắm điện. Với máy quét, chúng tôi có thể mang tài liệu vào máy tính của mình. Hoặc chúng ta có thể cắm vào ổ cắm điện và nhận điện từ nó. Chúng tôi có thể nhận dữ liệu từ một luồng đầu vào.

"Chúng được sử dụng ở đâu?"

"Các lớp này được sử dụng ở mọi nơi trong Java. Người bạn quen thuộc của chúng ta System.in là một biến InputStream tĩnh có tên trong lớp System ."

"Thật sao?! Vậy là suốt thời gian qua tôi đã sử dụng InputStream và thậm chí còn không nhận ra điều đó. System.out cũng là một luồng sao?"

"Có, System.out là biến PrintStream tĩnh (hậu duệ của OutputStream ) trong lớp Hệ thống."

"Ý bạn là nói với tôi rằng tôi đã luôn sử dụng các luồng và thậm chí không biết điều đó?"

"Vâng, và điều đó chỉ cho chúng tôi biết những luồng này tiện lợi như thế nào. Bạn chỉ cần lấy một cái và sử dụng nó."

"Nhưng bạn không thể nói như vậy về System.in. Chúng tôi liên tục phải thêm BufferedReader hoặc InputStreamReader vào nó."

"Đúng vậy. Nhưng cũng có lý do cho việc đó."

Có rất nhiều loại dữ liệu và nhiều cách để làm việc với chúng. Vì vậy, số lượng các lớp I/O tiêu chuẩn tăng lên rất nhanh, mặc dù chúng làm mọi thứ gần như giống nhau. Để tránh sự phức tạp này, các nhà phát triển Java đã sử dụng nguyên tắc trừu tượng hóa và chia các lớp thành nhiều phần nhỏ.

Nhưng bạn có thể kết nối các phần này một cách chặt chẽ và có được chức năng rất phức tạp, nếu bạn cần. Nhìn vào ví dụ này:

Xuất một chuỗi ra bàn điều khiển
System.out.println("Hello");
Lưu trữ luồng đầu ra của bảng điều khiển trong một biến riêng biệt.
Xuất một chuỗi vào luồng.
PrintStream console = System.out;
console.println("Hello");
Tạo một mảng byte động (mở rộng) trong bộ nhớ.
Kết nối nó với luồng đầu ra mới (đối tượng PrintStream).
Xuất một chuỗi vào luồng.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintStream console = new PrintStream(stream);
console.println("Hello");

"Thành thật mà nói, đây giống như một bộ Lego. Chỉ là tôi không rõ bất kỳ đoạn mã nào trong số này đang làm gì."

"Đừng lo lắng về điều đó lúc này. Mọi thứ đều có thời điểm của nó."

Đây là điều tôi muốn bạn ghi nhớ: Nếu một lớp cài đặt giao diện OutputStream, bạn có thể ghi byte vào nó. Gần như chính xác như bạn xuất dữ liệu ra console. Nó làm gì với nó là việc của nó. Với "Lego kit" của chúng tôi, chúng tôi không quan tâm đến mục đích của từng bộ phận riêng lẻ. Chúng tôi quan tâm đến thực tế là việc lựa chọn nhiều bộ phận cho phép chúng tôi tạo ra những thứ tuyệt vời như vậy.

"Được. Vậy chúng ta bắt đầu từ đâu?"