Giao Diện Externalizable
(Interface Externalizable)
Trong Java, giao diện Externalizable kế thừa từ giao diện Serializable và cho phép các lớp có toàn quyền kiểm soát quá trình tuần tự hóa (serialization) và giải tuần tự hóa (deserialization) của các đối tượng. Dưới đây là một số đặc điểm quan trọng của Externalizable:
Kiểm soát hoàn toàn dữ liệu
Khi một lớp triển khai Externalizable, chỉ danh tính của lớp được ghi vào dòng dữ liệu tuần tự hóa. Trách nhiệm của lớp là phải tự lưu trữ và khôi phục nội dung của các đối tượng thông qua hai phương thức chính là writeExternal và readExternal. Những phương thức này cho phép lớp kiểm soát toàn bộ định dạng và nội dung của dòng dữ liệu, bao gồm cả dữ liệu của các siêu lớp (supertypes).
Phương thức writeExternal() và phương thức readExternal()
Khi một đối tượng thực thi giao diện Externalizable, phương thức writeExternal sẽ được gọi để lưu trữ dữ liệu của đối tượng. Khi giải tuần tự hóa, một phiên bản của đối tượng sẽ được tạo ra bằng cách sử dụng constructor công khai không tham số (public no-arg constructor), và sau đó phương thức readExternal sẽ được gọi để khôi phục dữ liệu của đối tượng.
Thay thế đối tượng (writeReplace và readResolve)
Tương tự như giao diện Serializable, các đối tượng Externalizable có thể chỉ định một đối tượng thay thế thông qua các phương thức writeReplace và readResolve. Điều này có thể được sử dụng để thay thế một đối tượng khác trong quá trình tuần tự hóa và giải tuần tự hóa.
Ưu tiên interface Externalizable
Khi cơ chế tuần tự hóa đối tượng kiểm tra xem một đối tượng có thể tuần tự hóa hay không, nếu đối tượng hỗ trợ Externalizable, phương thức writeExternal sẽ được gọi. Nếu đối tượng không hỗ trợ Externalizable nhưng có triển khai Serializable, đối tượng sẽ được lưu trữ bằng ObjectOutputStream theo cách mặc định.
Sử dụng Externalizable là cần thiết khi bạn cần kiểm soát cụ thể cách một đối tượng được lưu trữ và khôi phục, đặc biệt là trong các trường hợp cần tối ưu hóa dữ liệu tuần tự hóa hoặc khi định dạng dữ liệu tuần tự hóa cần tuân theo một quy tắc nhất định.
Khai báo interface Externalizable trong Java
Để sử dụng interface Externalizable, bạn cần import gói java.io bạn cần thêm câu lệnh import vào đầu file Java của mình. Gói này cung cấp các lớp và giao diện để thực hiện các hoạt động nhập xuất (I/O) trong Java.
Cú pháp câu lệnh import:
Cú pháp
import java.io.Externalizable;
Cú pháp khai báo interface Externalizable:
Cú pháp
public interface Externalizable
extends Serializable
Dưới đây là giải thích chi tiết về cú pháp khai báo này:
↳ public: Đây là phạm vi truy cập, cho phép Externalizable có thể được truy cập từ bất kỳ đâu trong chương trình.
↳ interface: Externalizable là một giao diện (interface), nghĩa là nó chỉ chứa các phương thức trừu tượng mà các lớp triển khai phải định nghĩa.
↳ Externalizable: Đây là tên của giao diện. Nó cung cấp cơ chế để tùy chỉnh quá trình tuần tự hóa (serialization) và giải tuần tự hóa (deserialization) của đối tượng.
↳ extends Serializable: Externalizable mở rộng (kế thừa) từ Serializable, nhưng thay vì sử dụng cơ chế tuần tự hóa mặc định của Java, nó yêu cầu lập trình viên định nghĩa cách đối tượng được ghi và đọc bằng cách triển khai hai phương thức:
(1) void writeExternal(ObjectOutput out) throws IOException: Được gọi khi ghi đối tượng ra luồng dữ liệu. Lập trình viên phải chỉ rõ cách dữ liệu của đối tượng được ghi.
(2) void readExternal(ObjectInput in) throws IOException, ClassNotFoundException: Được gọi khi đọc dữ liệu và tạo lại đối tượng. Lập trình viên phải chỉ rõ cách đọc dữ liệu từ luồng.
Các phương thức của interface Externalizable
Giao diện Externalizable định nghĩa hai phương thức chính để thực hiện quá trình tuần tự hóa và giải tuần tự hóa:
(1) Phương thức writeExternal()
↳ Được gọi khi đối tượng được tuần tự hóa.
↳ Cho phép lớp tự định nghĩa cách ghi dữ liệu vào luồng.
↳ Sử dụng các phương thức của DataOutput để ghi các giá trị nguyên thủy hoặc writeObject để ghi các đối tượng, chuỗi và mảng.
↳ Có thể xảy ra ngoại lệ IOException trong quá trình ghi.
(2) Phương thức readExternal()
↳ Được gọi khi đối tượng được giải tuần tự hóa.
↳ Cho phép lớp tự định nghĩa cách đọc dữ liệu từ luồng.
↳ Sử dụng các phương thức của DataInput để đọc các giá trị nguyên thủy hoặc readObject để đọc các đối tượng, chuỗi và mảng.
↳ Phải đọc dữ liệu theo cùng thứ tự và kiểu dữ liệu như đã được ghi bởi writeExternal.
↳ Có thể xảy ra ngoại lệ IOException và ClassNotFoundException trong quá trình đọc.
Ví dụ về interface Externalizable
Ví dụ: Example.java
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class Example {
public static void main(String[] args) {
// 1. Tạo đối tượng và tuần tự hóa nó vào file
try (FileOutputStream fileOut = new FileOutputStream("person.ext");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
Person person = new Person("Wang", 30);
out.writeObject(person);
} catch (IOException e) {
System.err.println("IOException occurred: " + e.getMessage());
}
// 2. Đọc đối tượng từ file
try (FileInputStream fileIn = new FileInputStream("person.ext");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
Person person = (Person) in.readObject();
System.out.println("Đối tượng đã được giải tuần tự hóa: " + person);
} catch (IOException e) {
System.err.println("IOException đã xảy ra: " + e.getMessage());
} catch (ClassNotFoundException e) {
System.err.println("ClassNotFoundException đã xảy ra: " + e.getMessage());
}
}
// Lớp Person thực hiện Externalizable
static class Person implements Externalizable {
private static final long serialVersionUID = 1L; // UID cho tương thích phiên bản
private String name;
private int age;
// Constructor mặc định bắt buộc cho Externalizable
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt();
}
@Override
public String toString() {
return "\nPerson{Tên='" + name + "', Tuổi=" + age + "}";
}
}
}
Giải thích các bước:
↳ Ghi đối tượng:
Tạo một đối tượng Person và sử dụng ObjectOutputStream để ghi đối tượng vào file person.ext. Phương thức writeExternal sẽ ghi dữ liệu của đối tượng.
Chạy chương trình này sẽ tạo ra một file person.ext chứa dữ liệu tuần tự hóa của đối tượng Person:
Tệp tin: person.ext
���sr�Example$Person���������xpw�
Wang��� x
↳ Đọc đối tượng:
Sử dụng ObjectInputStream để đọc đối tượng từ file person.ext. Phương thức readExternal sẽ đọc dữ liệu và phục hồi trạng thái của đối tượng
Chạy chương trình này sẽ đọc đối tượng Person từ file person.ser và in ra thông tin của đối tượng đó. Chú ý rằng bạn cần phải đảm bảo rằng file person.ext đã được tạo trước đó bằng cách thực hiện quá trình tuần tự hóa. Kết quả của chương trình là
Kết quả của chương trình là
Đối tượng đã được giải tuần tự hóa:
Person{Tên='Wang', Tuổi=30}
Lưu ý:
Constructor mặc định: Khi sử dụng Externalizable, lớp phải có một constructor không tham số để khôi phục đối tượng.