SerialVersionUID là gì?

Bạn đã thêm serialVersionUID vào mã của mình bao nhiêu lần. Nhưng tại sao? Câu trả lời là vì IDE của bạn buộc phải làm điều đó. 90% các nhà phát triển thêm serialVersionUID vào chương trình của họ, họ không biết tại sao nó thực sự cần thiết.
Có thể bạn đã quen thuộc với tuần tự hóa và giải mã hóa. Nói một cách dễ hiểu, các đối tượng Java chỉ tồn tại trong giới hạn của JVM. Khi JVM thoát, các giá trị đối tượng cũng bị hủy. Serialization có nghĩa là bạn lưu các đối tượng dưới dạng byte ở đâu đó. Có thể là trong hệ thống tập tin. Deserialization có nghĩa là bạn đọc các byte này khi xây dựng lại đối tượng Java. Nhưng làm thế nào bạn có thể đảm bảo rằng lớp vẫn không thay đổi từ tuần tự hóa đến giải mã hóa?
Ví dụ: giả sử đó là ngày 1 tháng 1 và chúng ta có lớp Nhân viên như sau:
public class Employee {
private String id;
private String name;
private int age;
}
Với những thứ đó, bạn đã tuần tự hóa dữ liệu vào hệ thống tệp của mình dưới dạng empdata.dat trong cùng một ngày.
Bây giờ, vào ngày 2 tháng 1, một người nào đó thay đổi lớp học như sau:
public class Employee {
private String id;
private String name;
private Date dateOfBirth;
}
Nếu bạn đang cố gắng khôi phục (deserialization) empdata.dat cho lớp Nhân viên, bạn có thể thấy nó không đúng. Đó là hai định dạng khác nhau. Lưu ý: Về mặt kỹ thuật, điều này sẽ không xảy ra lỗi, bỏ qua trường bị thiếu. Nhưng đầu ra logic kinh doanh của bạn có thể không như mong đợi.
Đây là nơi chúng ta cần serialVersionUID. Nó được sử dụng trong quá trình giải tuần tự hóa để xác minh rằng người gửi (người tuần tự hóa) và người nhận (người giải tuần tự hóa) của một đối tượng được tuần tự hóa đã tải các lớp cho đối tượng đó tương thích với việc tuần tự hóa. Trong trường hợp người nhận đã tải một lớp cho đối tượng có serialVersionUID khác với lớp được sử dụng để tuần tự hóa, thì quá trình giải mã hóa sẽ kết thúc bằng InvalidClassException. Một lớp có thể tuần tự hóa có thể khai báo rõ ràng serialVersionUID của chính nó bằng cách khai báo một trường có tên “serialVersionUID” phải là trường tĩnh, cuối cùng và thuộc loại long :.
Ở đây, để dễ hiểu, chúng tôi đang sử dụng hệ thống lưu và đọc thành tệp. Nhưng tuần tự hóa có thể xảy ra trong thời gian chạy chính nó thông qua dây, vì vậy người gửi và người nhận sẽ ở trong một thế giới giống hệt nhau để lưu và đọc.
Mặc dù IDE của bạn yêu cầu bạn thêm seriaVersionUID, bạn vẫn có thể biên dịch chương trình mà không gặp lỗi trình biên dịch. Trong trường hợp như vậy, thời gian chạy tuần tự hóa sẽ tính toán một giá trị serialVersionUID mặc định cho lớp đó. Tính toán này sẽ dựa trên một số yếu tố. Do đó, chúng ta không thể mong đợi câu trả lời giống nhau cho ngay cả cùng một lớp trong các triển khai JVM và trình biên dịch khác nhau.
Chúng tôi đặc biệt khuyến nghị rằng tất cả các lớp có thể tuần tự hóa phải khai báo rõ ràng các giá trị serialVersionUID. Nếu không, nó có thể đổ chuông báo động giả - ngay cả đối với các tình huống hợp lệ. Để đảm bảo giá trị serialVersionUID nhất quán trên các triển khai trình biên dịch Java khác nhau, một lớp có thể tuần tự hóa phải khai báo một giá trị serialVersionUID rõ ràng.
Một điểm khác được khuyến cáo mạnh mẽ: các khai báo serialVersionUID rõ ràng sử dụng công cụ sửa đổi riêng nếu có thể, vì vậy các khai báo chỉ áp dụng cho lớp khai báo ngay lập tức. Có một trường hợp quy tắc này không áp dụng: Các lớp mảng. Những thứ đó không thể khai báo một serialVersionUID rõ ràng, vì vậy những thứ đó luôn có giá trị được tính toán mặc định, nhưng yêu cầu đối với các giá trị serialVersionUID được miễn cho các lớp Mảng.
Hãy thử điều này với một ví dụ.
Tạo một dự án trong IDE yêu thích của bạn và thực hiện lớp sau. (Tên đường dẫn nên được thay đổi nếu cần):
package com.krishantha.sample.java.serialVersion;
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private byte age;
private String address;
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 String whoIsThis() {
StringBuffer employee = new StringBuffer();
employee.append(getName()).append(" is ").append(getAge()).append(" years old and lives at ")
.append(getAddress());
return employee.toString();
}
}
Sau đó triển khai lớp bên dưới để viết một đối tượng được tuần tự hóa (serialization):
package com.krishantha.sample.java.serialVersion;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setName("Ashintha");
employee.setAge((byte) 30);
employee.setAddress("Galle");
FileOutputStream fout = new FileOutputStream("/users/krishantha/employee.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(employee);
oos.close();
System.out.println("Process complete");
}
}
Sau đó triển khai lớp bên dưới để đọc đối tượng tuần tự hóa (deserialization):
package com.krishantha.sample.java.serialVersion;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, IOException {
Employee employee = new Employee();
FileInputStream fin = new FileInputStream("/users/krishantha/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}
Bây giờ bạn đã sẵn sàng. Thực thi lớp Writer và nó sẽ tạo một tệp theo đường dẫn đã cho.
Process complete
Bây giờ thực thi lớp Reader. Nó sẽ đọc các giá trị từ đối tượng.
Ashintha is 30 years old and lives at Galle
Bây giờ thay đổi serialVersionUID của lớp Nhân viên và lưu:
private static final long serialVersionUID = 2L;
Bây giờ, một lần nữa, thực thi lớp Reader. (Đảm bảo không thực thi lớp Writer):
Exception in thread "main" java.io.InvalidClassException: com.krishantha.sample.java.serialVersion.Employee; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
Điều này có nghĩa là lớp Nhân viên đã thay đổi về mặt tuần tự hóa. Bạn có thể thử hoàn nguyên về id cũ và xem - nó sẽ hoạt động.
Có thể bạn quan tâm
