一路鹰、概述
有一個Object持久化于緩存中贷洲,經(jīng)常需要變更字段(添加或刪除)收厨,每次做變更就要更改緩存表(擔(dān)心不兼容帶來問題,一直不確定哪些變更會來問題或引起什么樣的問題)优构,我希望實現(xiàn)一種序列化诵叁,當(dāng)變更或刪除字段時不需要變更緩存表,這需要達(dá)到兩個目的:1俩块、新的類訪問舊的緩存時沒問題黎休;2.舊的類訪問新的緩存時也沒問題。
二玉凯、Java序列化
java序列化提供了一個框架势腮,用來將對象編碼成字節(jié)流,并從字節(jié)流編碼中重新構(gòu)建的對象漫仆。將對象編碼為字節(jié)流稱之為序列化捎拯,反之將字節(jié)流重建成對象稱之為反序列化。
三盲厌、序列化實現(xiàn)方式
簡單示例
一個Person類署照,具有兩個屬性:name和age;
public class Personimplements Serializable {
????private Stringname ;// 姓名
? ? private int age ;// 年齡
? ? public Person(String name,int age){// 通過構(gòu)造方法賦值
? ? ? ? name = name ;
????????age = age ;
? ? ?}
? //getter ,setter方法
?}
生成一個Person的實例p,將期通過ObjectOutputStream寫入文件,并通過ObjectInputStream讀出來吗浩。這是一個完整的序列化/反序列化過程:ObjectOutputStream將p轉(zhuǎn)化成字節(jié)流寫入文件建芙,ObjectInputStream將從文件中讀出的字節(jié)流重新創(chuàng)建newPerson實例。
@Test
public void testSerializable()throws Exception {
File file =new File("p.dat");
Person p =new Person("xiaoming",10);
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(p);
oos.close();
ObjectInputStream ois =new ObjectInputStream(new FileInputStream(file));
Object newPerson? = ois.readObject();
ois.close();
System.out.println(newPerson);
}
通過上面的過程懂扼,我們可以看出默認(rèn)的序列化機(jī)制對使用者而言是非常簡單的禁荸。序列化具體的實現(xiàn)是由ObjectOutputStream完成的;反序列化的具體實現(xiàn)是由ObjectInputStream完成的阀湿。
四赶熟、為何要實現(xiàn)Serializable
最重要的兩個原因是: 1、將對象的狀態(tài)保存在存儲媒體中以便可以在以后重新創(chuàng)建出完全相同的副本陷嘴; 2映砖、按值將對象從一個應(yīng)用程序域發(fā)送至另一個應(yīng)用程序域。 通俗的說:在分布式應(yīng)用中灾挨,你就得實現(xiàn)序列化邑退,如果你不需要分布式應(yīng)用,那就沒那個必要實現(xiàn)序列化涨醋。
Serializable是標(biāo)識類
Serializable是一個空接口瓜饥,沒有什么具體內(nèi)容,它的目的只是簡單的標(biāo)識一個類的對象可以被序列化浴骂。
五、哪些情況下需要序列化
當(dāng)你想把的內(nèi)存中的對象寫入到硬盤的時候宪潮;
當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對象的時候溯警;
當(dāng)你想通過RMI傳輸對象的時候趣苏;再稍微解釋一下:
比如說你的內(nèi)存不夠用了,那計算機(jī)就要將內(nèi)存里面的一部分對象暫時的保存到硬盤中梯轻,等到要用的時候再讀入到內(nèi)存中食磕,硬盤的那部分存儲空間就是所謂的虛擬內(nèi)存。在比如過你要將某個特定的對象保存到文件中喳挑,我隔幾天在把它拿出來用彬伦,那么這時候就要實現(xiàn)Serializable接口;
在進(jìn)行java的Socket編程的時候伊诵,你有時候可能要傳輸某一類的對象单绑,那么也就要實現(xiàn)Serializable接口;最常見的你傳輸一個字符串曹宴,它是JDK里面的類搂橙,也實現(xiàn)了Serializable接口,所以可以在網(wǎng)絡(luò)上傳輸笛坦。
如果要通過遠(yuǎn)程的方法調(diào)用(RMI)去調(diào)用一個遠(yuǎn)程對象的方法区转,如在計算機(jī)A中調(diào)用另一臺計算機(jī)B的對象的方法,那么你需要通過JNDI服務(wù)獲取計算機(jī)B目標(biāo)對象的引用版扩,將對象從B傳送到A废离,就需要實現(xiàn)序列化接口。
六礁芦、底層實現(xiàn)原理
先看下面代碼
Foo? myFoo = new Foo();?
myFoo .setWidth(37);?
myFoo.setHeight(70);
當(dāng)通過下面的代碼序列化之后蜻韭,MyFoo對象中的width和Height實例變量的值(37,70)都被保存到foo.ser文件中宴偿,這樣以后又可以把它從文件中讀出來湘捎,重新在堆中創(chuàng)建原來的對象。當(dāng)然保存時候不僅僅是保存對象的實例變量的值窄刘,JVM還要保存一些小量信息窥妇,比如類的類型等以便恢復(fù)原來的對象。
FileOutputStream fs = new FileOutputStream("foo.ser");?
ObjectOutputStream os = new ObjectOutputStream(fs);?
os.writeObject(myFoo);
那么大概步驟如下所示
1.Make a FileOutputStream? ? ?
`FileOutputStream fs = new FileOutputStream("foo.ser"); `
2.Make a ObjectOutputStream?
`ObjectOutputStream os =? new ObjectOutputStream(fs);? `
3.write the object
`os.writeObject(myObject1);?
os.writeObject(myObject2);?
os.writeObject(myObject3);? `
4.close the ObjectOutputStream
`os.close();`
serialVersionUID適用于Java的序列化機(jī)制娩践。
簡單來說活翩,Java的序列化機(jī)制是通過判斷類的serialVersionUID來驗證版本一致性的。在進(jìn)行反序列化時翻伺,JVM會把傳來的字節(jié)流中的serialVersionUID與本地相應(yīng)實體類的serialVersionUID進(jìn)行比較材泄,如果相同就認(rèn)為是一致的,可以進(jìn)行反序列化吨岭,否則就會出現(xiàn)序列化版本不一致的異常拉宗,即是InvalidCastException。
serialVersionUID有兩種顯示的生成方式:
一是默認(rèn)的1L,比如:private static final long serialVersionUID = 1L;
二是根據(jù)類名旦事、接口名魁巩、成員方法及屬性等來生成一個64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;