Input/Output (I/O) là một khía cạnh quan trọng của bất kỳ ứng dụng nào, cho phép chúng ta truy nhập và truy xuất dữ liệu từ các nguồn và đích khác nhau như tệp tin, bộ nhớ, mạng, và thiết bị ngoại vi. Trong Java, I/O thường được thực hiện thông qua gói java.io hoặc java.nio.
Dưới đây là một số khái niệm cơ bản về I/O trong Java:
| Khái niệm | Mô tả |
|---|---|
| Input/Output Streams | Luồng dữ liệu byte để đọc và ghi dữ liệu. InputStream và OutputStream là các lớp trừu tượng cơ bản. |
| Readers và Writers | Luồng dữ liệu ký tự để đọc và ghi dữ liệu dưới dạng ký tự. Reader và Writer là các lớp trừu tượng tương ứng. |
| File I/O | Quá trình đọc và ghi dữ liệu từ và đến tệp tin. Sử dụng các lớp như FileInputStream, FileOutputStream, FileReader, và FileWriter. |
| Buffered I/O | Cơ chế bộ đệm để tăng hiệu suất đọc và ghi dữ liệu bằng cách lưu trữ dữ liệu tạm thời trong bộ nhớ. Sử dụng các lớp như BufferedInputStream, BufferedOutputStream, BufferedReader, và BufferedWriter. |
| Network I/O | Quá trình truy nhập và truy xuất dữ liệu từ và đến các nguồn trên mạng. Sử dụng các lớp như Socket, ServerSocket, và URLConnection. |
| Data I/O | Quá trình đọc và ghi dữ liệu dạng nguyên thủy như int, float, double, và boolean. Sử dụng các lớp như DataInputStream và DataOutputStream. |
A. Phân loại
1. Phân loại dựa trên Stream
"Stream" là một khái niệm quan trọng để biểu diễn luồng dữ liệu liên tục, nơi dữ liệu được truyền từ một nguồn đến một đích một cách tuần tự và liên tục. Dữ liệu trong luồng được xử lý một lượng nhỏ tại mỗi thời điểm, không cần phải đợi toàn bộ dữ liệu được tạo ra hoặc đọc vào bộ nhớ trước khi bắt đầu xử lý.
Dưới đây là một số điểm quan trọng về khái niệm stream:
| Khái niệm | Mô tả |
|---|---|
| Luồng Đầu Vào (Input Stream) | Sử dụng để đọc dữ liệu từ một nguồn (ví dụ: tệp tin, socket, hoặc bộ nhớ) vào chương trình. |
| Luồng Đầu Ra (Output Stream) | Sử dụng để ghi dữ liệu từ chương trình ra ngoài (ví dụ: tệp tin, socket, hoặc bộ nhớ). |
| Xử Lý Tuần Tự | Dữ liệu trong stream được xử lý một phần nhỏ tại mỗi thời điểm và tiếp tục cho đến khi tất cả dữ liệu được xử lý. |
| Dữ Liệu Tuần Tự | Dữ liệu trong stream được xử lý tuần tự, từ đầu đến cuối, không truy cập ngẫu nhiên vào các phần của dữ liệu. |
| Cấu Trúc Tuần Tự | Stream thường được xem như là một dãy tuần tự của dữ liệu, phù hợp để xử lý dữ liệu dạng tập hợp hoặc dạng dòng. |
| Phù Hợp Với I/O | Stream được sử dụng để thực hiện hoạt động Input/Output (I/O), dữ liệu được truyền từ nguồn đến đích một cách tuần tự và liên tục. |
| Sử dụng stream trong Java | Gói java.io cung cấp nhiều lớp và giao diện để thực hiện các hoạt động I/O bằng cách sử dụng stream, đảm bảo hiệu quả và linh hoạt trong xử lý dữ liệu. |
Dưới đây là một số lớp và giao diện sử dụng stream:
Lớp và Giao Diện cho Đầu Vào (Input):
| Lớp/Giao Diện | Mô Tả |
|---|---|
| InputStream | Lớp trừu tượng cơ bản để đọc dữ liệu byte từ một nguồn. |
| Reader | Lớp trừu tượng cơ bản để đọc dữ liệu ký tự từ một nguồn. |
| FileInputStream | Lớp con của InputStream để đọc dữ liệu từ một tập tin. |
| ByteArrayInputStream | Lớp con của InputStream để đọc dữ liệu từ một mảng byte. |
| BufferedInputStream | Lớp con của InputStream để đọc dữ liệu với bộ đệm. |
| FileReader | Lớp con của Reader để đọc dữ liệu từ một tập tin văn bản. |
| InputStreamReader | Lớp con của Reader để đọc dữ liệu từ một luồng byte và chuyển đổi sang ký tự. |
| BufferedReader | Lớp con của Reader để đọc dữ liệu với bộ đệm và hiệu quả hơn. |
Lớp và Giao Diện cho Đầu Ra (Output):
| Lớp/Giao Diện | Mô Tả |
|---|---|
| OutputStream | Lớp trừu tượng cơ bản để ghi dữ liệu byte đến một đích. |
| Writer | Lớp trừu tượng cơ bản để ghi dữ liệu ký tự đến một đích. |
| FileOutputStream | Lớp con của OutputStream để ghi dữ liệu vào một tập tin. |
| ByteArrayOutputStream | Lớp con của OutputStream để ghi dữ liệu vào một mảng byte. |
| BufferedOutputStream | Lớp con của OutputStream để ghi dữ liệu với bộ đệm và hiệu quả hơn. |
| FileWriter | Lớp con của Writer để ghi dữ liệu vào một tập tin văn bản. |
| OutputStreamWriter | Lớp con của Writer để ghi dữ liệu từ ký tự sang byte và ghi vào một luồng byte. |
| BufferedWriter | Lớp con của Writer để ghi dữ liệu với bộ đệm và hiệu quả hơn. |
Giao Diện Cho Việc Đóng Tài Nguyên:
| Giao Diện | Mô Tả |
|---|---|
| Closeable | Giao diện đại diện cho các luồng hoặc nguồn tài nguyên có thể được đóng sau khi sử dụng. |
| AutoCloseable | Giao diện tương tự như Closeable nhưng được sử dụng trong các tình huống try-with-resources để tự động đóng luồng hoặc giải phóng tài nguyên. |
Dưới đây là các lớp mà không sử dụng stream để thực hiện các hoạt động Input/Output :
| Lớp | Mục Đích | Cách Thực Hiện I/O |
|---|---|---|
| String | Xử lý chuỗi ký tự và mã hóa | Sử dụng phương thức như getBytes() và getBytes(Charset charset) để chuyển đổi chuỗi thành mảng byte hoặc mã hóa dữ liệu dưới dạng byte. |
| Scanner | Đọc dữ liệu từ nguồn dữ liệu (ví dụ: System.in) | Sử dụng phương thức như next(), nextInt(), và nextLine() để đọc dữ liệu từng token hoặc dòng một cách tuần tự. |
| RandomAccessFile | Đọc và ghi dữ liệu từ và đến một vị trí xác định trong tệp tin | Sử dụng phương thức như read() và write() để đọc và ghi dữ liệu trực tiếp từ và đến vị trí cụ thể trong tệp tin. |
| File và Path | Thao tác với tệp và thư mục trong hệ thống tệp | Sử dụng phương thức như createNewFile(), delete(), và listFiles() để tạo, xóa, và liệt kê các tệp và thư mục. |
2.Phân loại theo nơi đọc/ ghi dữ liệu
| Lớp | Mô tả | Nơi Đọc/Ghi Dữ Liệu |
|---|---|---|
| BufferedInputStream và BufferedOutputStream | Một luồng đệm cho phép đọc/ghi dữ liệu từ/vào một luồng byte với hiệu suất cao hơn. | Byte Stream |
| BufferedReader và BufferedWriter | Đọc/Ghi dữ liệu từ/vào một luồng ký tự (character stream) với khả năng đệm để cung cấp hiệu suất tốt hơn. | Character Stream |
| ByteArrayInputStream và ByteArrayOutputStream | Một luồng đọc/ghi dữ liệu từ/vào một mảng byte. | Byte Array |
| FileReader và FileWriter | Đọc/Ghi dữ liệu từ/vào một tệp văn bản bằng cách sử dụng một bộ mã ký tự được cài đặt sẵn. | Text File |
| FileInputStream và FileOutputStream | Đọc/Ghi dữ liệu từ/vào một tệp trong hệ thống tệp. | File |
| ObjectInputStream và ObjectOutputStream | Đọc/Ghi các đối tượng Java từ/vào một luồng byte. | Serialized Object |
| PipedInputStream và PipedOutputStream | Đọc/Ghi dữ liệu từ/vào một ống thông tin dùng để truyền dữ liệu giữa hai luồng trong cùng một chương trình. | Pipe |
| PushbackInputStream và PushbackReader | Một luồng đặc biệt cho phép "đẩy ngược" một byte hoặc ký tự trở lại luồng đọc để đọc lại sau này. | Byte/Character Stream |
| SequenceInputStream | Biểu diễn sự kết hợp tuần tự của các luồng đầu vào, cho phép đọc dữ liệu từ nhiều nguồn liên tiếp nhau. | Byte Stream |
| FilterInputStream và FilterOutputStream | Lớp trừu tượng cơ sở cho các lớp lọc (filter) để mở rộng chức năng của luồng đầu vào và đầu ra. | Byte Stream |
| InputStream và OutputStream | Lớp trừu tượng cơ sở cho tất cả các luồng đầu vào và đầu ra. | Abstract |
| PrintStream và PrintWriter | Cho phép ghi dữ liệu được định dạng vào một luồng đầu ra. | Abstract |
| DataInputStream và DataOutputStream | Đọc và ghi dữ liệu theo các kiểu dữ liệu nguyên thủy Java từ/vào một luồng byte. | Byte Stream |
| File và FileDescriptor | Đại diện cho đường dẫn tệp và miêu tả tệp hoặc nguồn/suất byte khác. | System |
| RandomAccessFile | Cho phép đọc và ghi dữ liệu từ một tệp mà bạn có thể di chuyển ngẫu nhiên đến các vị trí cụ thể trong tệp. | File |
| Reader và Writer | Lớp trừu tượng cơ sở cho tất cả các lớp đọc và ghi ký tự. | Abstract |
| CharArrayReader và CharArrayWriter | Cho phép đọc và ghi dữ liệu từ/vào một mảng ký tự. | Memory |
| StringReader và StringWriter | Cho phép đọc và ghi dữ liệu từ/vào một chuỗi. | Memory |
| Console | Cung cấp các phương thức để truy cập thiết bị giao diện dựa trên ký tự (console) được liên kết với máy ảo Java hiện tại. | System Console |
| StreamTokenizer | Phân tích một luồng ký tự thành các token (từ hoặc số). | Byte/Character Stream |
| SerializablePermission | Cho phép quyền truy cập vào các lớp và phương thức khi tạo và đọc đối tượng Java Serialization. | Serialization |
| FilePermission | Đại diện cho quyền truy cập vào tệp. | System |
| FileFilter và FilenameFilter | Cung cấp các giao diện để lọc các tệp trong một thư mục. | File System |
3.Phân loại theo mục đích sử dụng
Tên | Mục đích | Sử dụng khi | Ưu điểm | Nhược điểm |
|---|---|---|---|---|
FileOutputStream và FileInputStream | Đọc và ghi dữ liệu nhị phân (binary data). | Làm việc với dữ liệu không phải văn bản (hình ảnh, âm thanh), hoặc khi cần ghi và đọc dữ liệu dạng byte. | Hiệu quả khi làm việc với dữ liệu nhị phân. | Khó khăn khi làm việc với dữ liệu văn bản, cần phải chuyển đổi thủ công giữa byte và ký tự. |
FileWriter và FileReader | Đọc và ghi dữ liệu văn bản (text data). | Làm việc với dữ liệu văn bản như tệp tin văn bản, tệp tin cấu hình. | Dễ sử dụng cho dữ liệu văn bản, tự động xử lý chuyển đổi ký tự. | Không phù hợp cho dữ liệu nhị phân. |
BufferedWriter và BufferedReader | Đọc và ghi dữ liệu văn bản với bộ đệm (buffered). | Cần tăng hiệu suất khi làm việc với tệp tin lớn hoặc khi thực hiện nhiều thao tác đọc/ghi. | Hiệu suất cao hơn so với FileWriter và FileReader nhờ vào việc sử dụng bộ đệm. | Cần thêm mã để đóng bộ đệm một cách chính xác. |
ObjectOutputStream và ObjectInputStream | Đọc và ghi đối tượng Java dưới dạng tuần tự hóa (serialization). | Lưu trữ và khôi phục trạng thái của đối tượng Java. | Đơn giản hóa việc lưu trữ và khôi phục đối tượng, hỗ trợ đối tượng phức tạp. | Yêu cầu các lớp phải triển khai giao diện Serializable, dữ liệu không dễ đọc và chỉnh sửa thủ công. |
BufferedOutputStream và BufferedInputStream | Đọc và ghi dữ liệu nhị phân với bộ đệm (buffered). | Cần tăng hiệu suất khi làm việc với dữ liệu nhị phân lớn hoặc khi thực hiện nhiều thao tác đọc/ghi. | Hiệu suất cao hơn so với FileOutputStream và FileInputStream nhờ vào việc sử dụng bộ đệm. | Cần thêm mã để đóng bộ đệm một cách chính xác. |
Để hiểu rõ hơn về mục đích sử dụng, ta đến với ví dụ sau:
File: Student.java - Lớp này dùng để lưu thông tin cho mỗi sinh viên
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import java.io.Serializable;public class Student implements Serializable { private int id; private String name; private byte age; private String address; private float gpa; public Student() { } public Student(int id, String name, byte age, String address, float gpa) { super(); this.id = id; this.name = name; this.age = age; this.address = address; this.gpa = gpa; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getAge() { return age; } public void setAge(byte age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public float getGpa() { return gpa; } public void setGpa(float gpa) { this.gpa = gpa; }} |
File: StudentDao.java - Lớp này dùng để đọc và ghi mỗi sinh viên vào file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | public class StudentDao { private static final String STUDENT_FILE_NAME = "student.txt"; public void write(List<Student> studentList) { FileOutputStream fos = null; ObjectOutputStream oos = null; try { fos = new FileOutputStream(new File(STUDENT_FILE_NAME)); oos = new ObjectOutputStream(fos); oos.writeObject(studentList); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { closeStream(fos); closeStream(oos); } } public List<Student> read() { List<Student> studentList = new ArrayList<>(); FileInputStream fis = null; ObjectInputStream ois = null; try { fis = new FileInputStream(new File(STUDENT_FILE_NAME)); ois = new ObjectInputStream(fis); studentList = (List<Student>) ois.readObject(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { closeStream(fis); closeStream(ois); } return studentList; } private void closeStream(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } private void closeStream(OutputStream os) { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } }} |
Nếu không sử dụng ObjectO/IputStream mà sử dụng FileStream :
Nếu bạn muốn thay đổi cách đọc và ghi file từ việc sử dụng ObjectStream sang sử dụng FileStream, bạn có thể sử dụng các phương thức của FileReader và FileWriter để đọc và ghi file dạng văn bản.
Trước tiên, bạn cần đảm bảo rằng lớp Student có thể được chuyển đổi sang dạng chuỗi (string) và ngược lại. Một cách đơn giản là tạo phương thức toString để chuyển đổi Student thành chuỗi và một phương thức tĩnh để chuyển đổi chuỗi ngược lại thành Student.
Giải thích
Phương thức
write:- Sử dụng
BufferedWriterđể ghi từng đối tượngStudentdưới dạng chuỗi vào file. - Mỗi đối tượng
Studentđược chuyển đổi thành chuỗi bằng phương thứctoStringvà ghi vào file. FileWritergiúp ghi dữ liệu vào file văn bản, vàBufferedWritergiúp ghi dữ liệu một cách hiệu quả hơn bằng cách giảm số lần truy cập trực tiếp vào file hệ thống.
- Sử dụng
Phương thức
read:- Sử dụng
BufferedReaderđể đọc từng dòng từ file. - Mỗi dòng được chuyển đổi ngược lại thành đối tượng
Studentbằng phương thứcfromString. FileReadergiúp đọc dữ liệu từ file văn bản, vàBufferedReadergiúp đọc dữ liệu một cách hiệu quả hơn bằng cách giảm số lần truy cập trực tiếp vào file hệ thống.
- Sử dụng
Nhược điểm :
Tại sao cần kết hợp FileOutputStream với ObjectOutputStream?
FileOutputStream:- Đây là lớp luồng (stream) cơ bản trong Java dùng để ghi dữ liệu thô (raw data) vào file.
- Nó làm việc ở cấp độ byte, nghĩa là nó không biết cách xử lý các đối tượng hoặc kiểu dữ liệu phức tạp.
ObjectOutputStream:- Đây là lớp luồng cao cấp (high-level stream) dùng để ghi các đối tượng Java vào luồng đầu ra.
- Nó thực hiện việc tuần tự hóa (serialization) các đối tượng, biến chúng thành một chuỗi byte để có thể ghi vào file hoặc gửi qua mạng.
ObjectOutputStreamcần một luồng cơ bản (nhưFileOutputStream) để thực hiện ghi các byte đã tuần tự hóa vào file.
FileOutputStream làm nhiệm vụ mở một kết nối đến file cụ thể, trong khi ObjectOutputStream sử dụng kết nối này để ghi các đối tượng đã tuần tự hóa vào file.
Ví dụ minh họa:
Nếu bạn chỉ sử dụng ObjectOutputStream mà không có FileOutputStream, ObjectOutputStream sẽ không biết nơi để ghi các byte đã tuần tự hóa.
ObjectOutputStream không thể được sử dụng một mình vì nó là một luồng cao cấp (high-level stream) và cần một luồng cơ bản (low-level stream) để thực sự ghi các byte đã tuần tự hóa vào đâu đó, chẳng hạn như một file hoặc một kết nối mạng.
4.Phân loại theo loại luồng
4.1. Low-Level Streams (Luồng Cơ Bản)
Các luồng cơ bản đọc và ghi dữ liệu trực tiếp từ hoặc đến một nguồn như file, mảng byte, hoặc kết nối mạng. Chúng thực hiện các thao tác I/O ở mức byte hoặc ký tự đơn lẻ và không cung cấp tính năng bổ sung như bộ đệm hoặc tuần tự hóa.
Loại Luồng | Tên | Mục đích |
|---|---|---|
Byte Streams (Luồng Byte) | | |
| InputStream | Lớp cha của tất cả các luồng byte đầu vào. |
| FileInputStream | Đọc byte từ file. |
| ByteArrayInputStream | Đọc byte từ mảng byte. |
| SocketInputStream | Đọc byte từ socket (kết nối mạng). |
| OutputStream | Lớp cha của tất cả các luồng byte đầu ra. |
| FileOutputStream | Ghi byte vào file. |
| ByteArrayOutputStream | Ghi byte vào mảng byte. |
| SocketOutputStream | Ghi byte vào socket (kết nối mạng). |
Character Streams (Luồng Ký Tự) | | |
| Reader | Lớp cha của tất cả các luồng ký tự đầu vào. |
| FileReader | Đọc ký tự từ file. |
| CharArrayReader | Đọc ký tự từ mảng ký tự. |
| StringReader | Đọc ký tự từ chuỗi. |
| Writer | Lớp cha của tất cả các luồng ký tự đầu ra. |
| FileWriter | Ghi ký tự vào file. |
| CharArrayWriter | Ghi ký tự vào mảng ký tự. |
| StringWriter | Ghi ký tự vào chuỗi. |
4.2.High-Level Streams (Luồng Cao Cấp)
Các luồng cao cấp được xây dựng trên các luồng cơ bản và cung cấp thêm các tính năng như bộ đệm (buffering), tuần tự hóa đối tượng (object serialization), hoặc xử lý dữ liệu dạng dòng (line processing).
Loại Luồng | Tên | Mục đích |
|---|---|---|
Buffered Streams (Luồng Đệm) | | |
| BufferedInputStream | Thêm bộ đệm cho InputStream. |
| BufferedOutputStream | Thêm bộ đệm cho OutputStream. |
| BufferedReader | Thêm bộ đệm cho Reader và cung cấp phương thức đọc dòng. |
| BufferedWriter | Thêm bộ đệm cho Writer và cung cấp phương thức ghi dòng. |
Object Streams (Luồng Đối Tượng) | | |
| ObjectInputStream | Đọc các đối tượng đã tuần tự hóa từ InputStream. |
| ObjectOutputStream | Ghi các đối tượng đã tuần tự hóa vào OutputStream. |
Data Streams (Luồng Dữ Liệu) | | |
| DataInputStream | Đọc dữ liệu nguyên thủy (primitive data) từ InputStream. |
| DataOutputStream | Ghi dữ liệu nguyên thủy vào OutputStream. |
1. Ghi nội dung vào file
Để ghi nội dung vào file, bạn có thể sử dụng lớp FileWriter. Dưới đây là ví dụ chi tiết:
- Tạo một đối tượng
File: Kiểm tra xem file có tồn tại không, nếu không thì tạo một file mới. - Tạo một đối tượng
FileWriter: Sử dụng đối tượng này để ghi nội dung vào file. - Viết nội dung vào file: Sử dụng phương thức
write()củaFileWriter. - Đóng
FileWriter: Đảm bảo rằng tài nguyên được giải phóng.
2. Đọc nội dung từ file
Để đọc nội dung từ file, bạn có thể sử dụng lớp FileReader kết hợp với BufferedReader để đọc dữ liệu từng dòng hoặc từng ký tự. Dưới đây là ví dụ chi tiết:
- Tạo một đối tượng
File: Xác định file cần đọc. - Tạo một đối tượng
FileReadervàBufferedReader: Sử dụng đối tượng này để đọc dữ liệu từ file. - Đọc nội dung từ file: Sử dụng phương thức
readLine()để đọc từng dòng. - Đóng
BufferedReader: Đảm bảo rằng tài nguyên được giải phóng.
m

.png)
.png)
.png)
Không có nhận xét nào:
Đăng nhận xét