[Java] 序列化之基本認識
一、簡介
Java 序列化即將 Java 物件轉換為二進位制序列的過程,主要用於網路通訊、持久化儲存。
二、實現方式
Serializable
Java 中實現 Serializable
介面即可實現序列化。
Externalizable
Serializable
的子類,實現類需實現 writeExternal(), readExternal()
方法,和實現 writeObject(), readObject()
類似,可自行修改序列化或反序列化後的物件的資訊:
三、主要特性
serialVersionUID
兩個類的序列化ID不同,則無法相互序列化與反序列化。
要點:
- 可顯示指定
serialVersionUID
- 未指定時,會預設生成一個
serialVersionUID
: 根據類資訊自動生成
序列化ID不一致
SuperClass
類:
import java.io.Serializable;
/**
* @author wengliemiao
*/
public class SuperClass implements Serializable {
private static final long serialVersionUID = 1L;
private String name = "";
private String age = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SerialVersionTest
測試類:
package com.wlm.jdk.serialVersion;
import java.io.*;
/**
1. @author wengliemiao
*/
public class SerialVersionTest {
public static void main (String[] args) {
SuperClass superClass = new SuperClass();
superClass.setName("2333");
try {
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("/Users/wengliemiao/Documents/timerange_test7"));
oo.writeObject(superClass);
oo.close();
} catch (IOException e) {
e.printStackTrace();
}
/*
try {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("/Users/wengliemiao/Documents/timerange_test7"));
SuperClass superClass1 = (SuperClass) oi.readObject();
System.out.println(superClass1.getName());
oi.close();
} catch (Exception e) {
e.printStackTrace();
}
*/
}
}
操作步驟:
SuperClass
類的serialVersionUID
為1L時,執行writeObject()
;- 將
SuperClass
類的serialVersionUID
改為2L,執行readObject()
;
結果為:
未指定序列化ID
類未執行序列化ID時,會自動生成一個,後續如果修改了類資訊,則會導致序列化ID不一致,從而導致反序列化失敗。
執行步驟:
SuperClass
類資訊除不指定serialVersionUID
外,其他資訊和前面一致,執行writeObject()
;- 為
SuperClass
類隨意增加一個欄位,執行readObject()
;
結果為:
靜態變數序列化
靜態變數無法被序列化,因為序列化儲存的是物件的狀態,而靜態變數是類的狀態。
驗證步驟:
SuperClass
類增加靜態變數b = 10;- 執行序列化操作,並設定 b = 20;
- 執行反序列化操作,並輸出 b 的值;
測試 main
方法為:
public static void main(String[] args) {
SuperClass superClass = new SuperClass();
try {
System.out.println("序列化前: " + superClass.b);
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("/Users/wengliemiao/Documents/timerange_test9"));
oo.writeObject(superClass);
oo.close();
} catch (IOException e) {
e.printStackTrace();
}
superClass.b = 20;
try {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("/Users/wengliemiao/Documents/timerange_test9"));
SuperClass superClass1 = (SuperClass) oi.readObject();
System.out.println("反序列化後: " + superClass1.b);
oi.close();
} catch (Exception e) {
e.printStackTrace();
}
}
結果為:
父類序列化
如果子類實現 Serializable
介面,而父類未實現,則序列化與反序列化時,父類資料不會參與。驗證如下:
SuperClass
類:
public class SuperClass {
private String name = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SubClass
類:
package com.wlm.jdk.serialVersion;
import java.io.Serializable;
/**
* @author wengliemiao
*/
public class SubClass extends SuperClass implements Serializable {
private static final long serialVersionUID = -1681989450035759750L;
private String address = "";
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
測試類如下:
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.setName("wlm");
subClass.setAddress("火車東站");
try {
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("/Users/wengliemiao/Documents/timerange_test11"));
oo.writeObject(subClass);
oo.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("/Users/wengliemiao/Documents/timerange_test11"));
SubClass subClass1= (SubClass) oi.readObject();
System.out.println("姓名: " + subClass1.getName());
System.out.println("地址: " + subClass1.getAddress());
oi.close();
} catch (Exception e) {
e.printStackTrace();
}
}
此時輸出資料為:
而為父類實現 Serializable
介面後,輸出為:
transient 關鍵字
transient
關鍵字的作用則是阻止變數的序列化,被反序列化後,transient
變數的值被設為初始值。驗證如下, Subclass
類中增加 transient
變數:
private transient String sex;
其他程式碼與前面一致,輸出為:
writeObject(), readObject()
可通過實現 writeObject(), readObject()
方法來進行自定義的序列化和反序列化。如果未實現,則預設呼叫是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。
如 JDK 中 ArrayList
類,elementData[]
欄位是 transient
型別,不會參與序列化:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
ArrayList
通過實現 writeObject(), readObject()
方法,序列化和反序列化 elementData[]
中的資料:
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}