
簡述
序列化就是將某個(gè)對(duì)象轉(zhuǎn)換成特定的數(shù)據(jù)格式,反序列化即序列化的返操作。一般將序列化后的數(shù)據(jù)通過網(wǎng)絡(luò)傳輸或者保存到數(shù)據(jù)庫。
java的序列化操作
在Java中,若要將一個(gè)對(duì)象可序列化則要實(shí)現(xiàn) Serializable接口。且盡可能標(biāo)記屬性serialVersionUID(后面Test將說明為什么要標(biāo)記此屬性),一般IDEA會(huì)自動(dòng)生成。
- 如我有一列
class Order implements Serializable {
private static final long serialVersionUID = 1L;
private String orderNo;
private int amount;
//構(gòu)造
//getter setter...
@Override
public String toString() {
//所有屬性
}
}
現(xiàn)在將利用java.io包下的ObjectOutputStream和FileOutputStream 來演示如何將Order對(duì)象序列化到文件里保存起來
public class SerializableTest {
public final static String PATH = "/tmp/order.txt";
@Test
public void test_serializable() throws IOException {
Order o = new Order("abc", 1299, new Address("安徽"), new Person("Savey"));
try ( FileOutputStream fstream = new FileOutputStream(PATH);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fstream)
) {
//接收一個(gè)對(duì)象、并將對(duì)象轉(zhuǎn)為字節(jié)流
objectOutputStream.writeObject(o);
//刷新到輸出fstream
objectOutputStream.flush();
}
}
}
此時(shí) Order對(duì)象已經(jīng)被序列化到 /tmp/order.txt文件里。
java的反序列化操作
如上回說到Order已經(jīng)被我們保存到指定文件,此時(shí)我們想讀取數(shù)據(jù)則這樣處理
@Test
public void test_covert_object() throws IOException, ClassNotFoundException {
try (FileInputStream fileInputStream = new FileInputStream(PATH);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)
) {
//從一個(gè)輸入流讀取數(shù)據(jù)轉(zhuǎn)為對(duì)象,這里加個(gè)強(qiáng)轉(zhuǎn)為Order
Order o = (Order)objectInputStream.readObject();
System.out.println(o);
}
}
此時(shí)我們利用了幾個(gè)重要的對(duì)象幫助我們序列和反序列操作
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
注意事項(xiàng)
serialVersionUID
假如我在序列化之前的 Order對(duì)象是這樣的
class Order implements Serializable {
private static final long serialVersionUID = 1L;
}
在反序列化之后serialVersionUID變了
class Order implements Serializable {
private static final long serialVersionUID = 2L;
}
那么不好意思,會(huì)報(bào)錯(cuò),如下:
java.io.InvalidClassException: Order; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
序列化對(duì)象里有其他自定義對(duì)象
比如我在Order里加了一個(gè)Person對(duì)象
class Order implements Serializable {
private static final long serialVersionUID = 1L;
//其他屬性
private Person person;
//getter setter construct toString
}
class Person implements Serializable {
private static final long serialVersionUID = 2L;
}
如果Person沒有實(shí)現(xiàn)Serializable接口也會(huì)出錯(cuò),如下:
java.io.NotSerializableException: Person
But 你可以修改Person的申明方式
private transient Person person;
用關(guān)鍵字transient來修飾您不想序列化的屬性!在ArrayList源碼里 是不是也見過這個(gè)關(guān)鍵字呢???
/**
* 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
自定義序列化
不錯(cuò),您還可以自定義你要序列化的內(nèi)容,java很優(yōu)雅的為我們提供了兩個(gè)方法重寫序列化的方法
private void writeObject(ObjectOutputStream oos)
private void readObject(ObjectInputStream ois)
這兩個(gè)方法見名思意,一個(gè)寫入、一個(gè)讀取。如上回說到一個(gè)屬性被關(guān)鍵寫transient修飾了,但我還想將它序列化下~
class Order implements Serializable {
//其他屬性
private transient Person person;
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
//自定義寫入Person這個(gè)對(duì)象、盡管你是 transient修飾了?。? oos.writeObject(person);
}
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
//讀取強(qiáng)行轉(zhuǎn)成Person對(duì)象
Person person = (Person) ois.readObject();
this.setPerson(person);
}
//getter setter construct
}
上面代碼演示了如果自定義序列化和反序列化,如果你有多個(gè)屬性要自定義操作,請(qǐng)注意的寫入的順序,和讀取的順序要一一對(duì)應(yīng),不然會(huì)出錯(cuò)的哦!
java.lang.ClassCastException: XXX cannot be cast to XXX