1. Giới thiệu về Tuần tự hóa (Serialization)
Tuần tự hóa (Serialization) là quá trình chuyển đổi trạng thái của một đối tượng Java thành một dòng byte, với mục đích lưu trữ hoặc truyền tải thông qua mạng. Phương pháp ngược lại, giải tuần tự hóa (deserialization), cho phép chúng ta chuyển đổi dòng byte trở lại thành đối tượng Java.
2. Serialized và Deserialized
Quá trình tuần tự hóa không phụ thuộc vào phiên bản, cho phép bạn tuần tự hóa đối tượng trên một nền tảng và giải tuần tự hóa trên một nền tảng khác. Để một lớp có thể được tuần tự hóa, nó cần thực thi giao diện đánh dấu Serializable.
Các lớp ObjectInputStream và ObjectOutputStream thuộc về các luồng dữ liệu trong java.io và cho phép ghi và đọc đối tượng. Điều quan trọng nhất là hai phương thức sau:
java
public final void writeObject(Object o) throws IOException;
Phương thức này giúp ghi một đối tượng có thể tuần tự hóa thành dòng byte. Tương tự, phương thức sau trong ObjectInputStream:
java
public final Object readObject() throws IOException, ClassNotFoundException;
Phương thức này nhận đầu vào là một luồng byte và trả về một đối tượng Java, có thể được ép kiểu về kiểu dữ liệu gốc.
Ví dụ Về Tuần tự hóa với Lớp Person
Chúng ta sẽ xem xét quá trình tuần tự hóa một lớp đơn giản là Person. Các trường tĩnh (static fields) của lớp sẽ không được tuần tự hóa, vì chúng thuộc về lớp thay vì đối tượng. Từ khóa transient có thể được sử dụng để loại bỏ các trường trong quá trình tuần tự hóa:
java
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
static String country = "ITALY";
private int age;
private String name;
transient int height;
// getters và setters
}
Dưới đây là ví dụ về việc lưu đối tượng Person vào file và đọc lại:
java
@Test
public void whenSerializingAndDeserializing_ThenObjectIsTheSame() throws IOException, ClassNotFoundException {
Person person = new Person();
person.setAge(20);
person.setName("Joe");
FileOutputStream fileOutputStream = new FileOutputStream("yourfile.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(person);
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("yourfile.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Person p2 = (Person) objectInputStream.readObject();
objectInputStream.close();
assertTrue(p2.getAge() == person.getAge());
assertTrue(p2.getName().equals(person.getName()));
}
Chúng ta đã sử dụng ObjectOutputStream để lưu trạng thái của đối tượng vào một file, sau đó sử dụng ObjectInputStream để đọc lại. Đảm bảo trạng thái của đối tượng được khôi phục đúng với đối tượng ban đầu.
3. Những Lưu Ý Quan Trọng Về Tuần tự hóa trong Java
3.1 Kế thừa và Thành phần
Khi một lớp thực thi giao diện Serializable, tất cả các lớp con cũng sẽ tự động có khả năng tuần tự hóa. Nếu một đối tượng tham chiếu đến một đối tượng khác, mọi đối tượng này cũng cần thực thi Serializable, ngược lại sẽ gây ra lỗi NotSerializableException.
java
public class Person implements Serializable {
private int age;
private String name;
private Address country; // phải có khả năng tuần tự hóa
}
3.2 Serial Version UID
JVM gán một số phiên bản cho mỗi lớp tuần tự hóa. Số này được sử dụng để xác minh tính tương thích giữa các đối tượng đã lưu và tải. Nếu một lớp không khai báo serialVersionUID, JVM sẽ tự động tạo trong thời gian chạy.
3.3 Tuần tự hóa Tùy Chọn
Java cho phép bạn ghi đè hành vi tuần tự hóa mặc định, điều này hữu ích cho các thuộc tính không thể tuần tự hóa. Để thực hiện điều này, bạn chỉ cần định nghĩa hai phương thức sau trong lớp của bạn:
java
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
Khi đó, bạn có thể kiểm soát cách thức tuần tự hóa các thuộc tính phức tạp hơn.
Ví dụ về Tuần tự hóa Tùy Chọn:
java
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private transient Address address;
private Person person;
// setters và getters
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(address.getHouseNumber());
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
Integer houseNumber = (Integer) ois.readObject();
Address a = new Address();
a.setHouseNumber(houseNumber);
this.setAddress(a);
}
}
public class Address {
private int houseNumber;
// setters và getters
}
Giả sử ta muốn kiểm tra quá trình tuần tự hóa tùy chọn:
java
@Test
public void whenCustomSerializingAndDeserializing_ThenObjectIsTheSame() throws IOException, ClassNotFoundException {
Person p = new Person();
p.setAge(20);
p.setName("Joe");
Address a = new Address();
a.setHouseNumber(1);
Employee e = new Employee();
e.setPerson(p);
e.setAddress(a);
// quá trình tuần tự hóa và giải tuần tự hóa
}
Lưu ý rằng cá thuộc tính không thể tuần tự hóa cần phải đánh dấu là transient.
Kết Luận
Tìm hiểu về tuần tự hóa trong Java không chỉ giúp bạn chuyển đổi đối tượng dễ dàng mà còn mở rộng khả năng lưu trữ và truyền tải dữ liệu ứng dụng của bạn qua mạng hiệu quả hơn. Nắm vững các nguyên tắc sẽ giúp bạn xây dựng ứng dụng Java mạnh mẽ và an toàn hơn.
source: viblo