serialVersionUID 實際作用
JAVA 中對序列化的支持 都是需要實現 Serializable 接口,然後需要聲明一個serialVersionUID (也可以不用申明).
serialVersionUID 的作用是什麼呢?
JAVA序列化的機制是通過判斷類的serialVersionUID來驗證的版本一致的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID於本地相應實體類的serialVersionUID進行比較。如果相同說明是一致的,可以進行反序列化
serialVersionUID 申明方式
- 顯示聲明
public class User implements Serializable {
private static final long serialVersionUID = -3350966179389669783L;
private String name;
顯示申明是根據類名 字段 參數 生成的一個64位的hash值(申明之後就不會變了 就算修改了類信息)
- 隱式申明
public class User implements Serializable {
private String name;
隱式申明 也是會根據類名 字段 參數 生成的一個64位的hash值(但是每次類信息修改 都會重新生成)
類字段新增與sid聲明方式
- 新增字段&sid 隱式申明
聲明 User 類 (不申明serialVersionUID)持久化到文件, 在User 類中新增字段 在從文件讀取
申明User類
public class User implements Serializable {
private String name;
private int age;
寫入序列化文件
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void serialize() {
User user = new User();
user.setName("sunla");
user.setAge(31);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(user);
os.close();
bos.close();
FileUtils.writeByteArrayToFile(new File(FILE),bos.toByteArray());
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
}
}
user類新增字段
public class User implements Serializable {
private String name;
private int age;
/** 新增屬性 */
private HashSet<String> sets = new HashSet<>();
執行從文件反序列化操作
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void deserialize() {
Object rv = null;
try {
byte[] in = FileUtils.readFileToByteArray(new File(FILE));
if (in != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(in);
ObjectInputStream is = new ObjectInputStream(bis);
rv = is.readObject();
is.close();
bis.close();
}
User user = (User)rv;
System.out.println(JSON.toJSONString(user));
} catch (Exception e) {
e.printStackTrace();
}
}
執行的結果
java.io.InvalidClassException: com.test.serial.User; local class incompatible: stream classdesc serialVersionUID = 7212634958119174257, local class serialVersionUID = 5030622290002077224
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1880)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1746)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2037)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1568)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428)
執行結果報錯了,2個很明顯的 serialVersionUID 不一樣 導致反序列化失敗,就是前面說到的 隱式聲明的sid 當新增字段後會變更
- 新增字段 & sid 顯示申明
聲明 User 類 (申明serialVersionUID)持久化到文件, 在User 類中新增字段 在從文件讀取
申明User類
public class User implements Serializable {
private static final long serialVersionUID = -3350966179389669783L;
private String name;
private int age;
寫入序列化文件
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void serialize() {
User user = new User();
user.setName("sunla");
user.setAge(31);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(user);
os.close();
bos.close();
FileUtils.writeByteArrayToFile(new File(FILE),bos.toByteArray());
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
}
}
user類新增字段
public class User implements Serializable {
private static final long serialVersionUID = -3350966179389669783L;
private String name;
private int age;
/** 新增屬性(這裏使用了集合 反序列化出來大家可以猜下 調用getSets的結果) */
private HashSet<String> sets = new HashSet<>();
執行從文件反序列化操作
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void deserialize() {
Object rv = null;
try {
byte[] in = FileUtils.readFileToByteArray(new File(FILE));
if (in != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(in);
ObjectInputStream is = new ObjectInputStream(bis);
rv = is.readObject();
is.close();
bis.close();
}
User user = (User)rv;
System.out.println(JSON.toJSONString(user));
System.out.println(user.getSets() == null ? "set null":"set not null");
} catch (Exception e) {
e.printStackTrace();
}
}
執行的結果
{"age":31,"name":"sunla"}
set null
執行結果 是新增的集合對象是個null ,其他字段完整映射