Java 序列化Serializable詳解(附詳細例子)
1、什么是序列化和反序列化
Serialization(序列化)是一種將對象以一連串的字節(jié)描述的過程;反序列化deserialization是一種將這些字節(jié)重建成一個對象的過程。
2、什么情況下需要序列化
a)當(dāng)你想把的內(nèi)存中的對象保存到一個文件中或者數(shù)據(jù)庫中時候;
b)當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對象的時候;
c)當(dāng)你想通過RMI傳輸對象的時候;
3、如何實現(xiàn)序列化
將需要序列化的類實現(xiàn)Serializable接口就可以了,Serializable接口中沒有任何方法,可以理解為一個標記,即表明這個類可以序列化。
4、序列化和反序列化例子
如果我們想要序列化一個對象,首先要創(chuàng)建某些OutputStream(如FileOutputStream、ByteArrayOutputStream等),然后將這些OutputStream封裝在一個ObjectOutputStream中。這時候,只需要調(diào)用writeObject()方法就可以將對象序列化,并將其發(fā)送給OutputStream(記?。簩ο蟮男蛄谢腔谧止?jié)的,不能使用Reader和Writer等基于字符的層次結(jié)構(gòu))。而反序列的過程(即將一個序列還原成為一個對象),需要將一個InputStream(如FileInputstream、ByteArrayInputStream等)封裝在ObjectInputStream內(nèi),然后調(diào)用readObject()即可。
package com.sheepmu;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class MyTest implements Serializable{ private static final long serialVersionUID = 1L; private String name='SheepMu'; private int age=24; public static void main(String[] args) {//以下代碼實現(xiàn)序列化 try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream('my.out'));//輸出流保存的文件名為 my.out ;ObjectOutputStream能把Object輸出成Byte流 MyTest myTest=new MyTest(); oos.writeObject(myTest); oos.flush(); //緩沖流 oos.close(); //關(guān)閉流 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } fan();//調(diào)用下面的 反序列化 代碼 } public static void fan()//反序列的過程 { ObjectInputStream oin = null;//局部變量必須要初始化 try { oin = new ObjectInputStream(new FileInputStream('my.out')); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } MyTest mts = null; try { mts = (MyTest ) oin.readObject();//由Object對象向下轉(zhuǎn)型為MyTest對象 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println('name='+mts.name); System.out.println('age='+mts.age); }}
會在此項目的工作空間生成一個 my.out文件。序列化后的內(nèi)容稍后補齊,先看反序列化后輸出如下:
name=SheepMu
age=24
5、序列化ID
序列化 ID 在 Eclipse 下提供了兩種生成策略,一個是固定的 1L,一個是隨機生成一個不重復(fù)的 long 類型數(shù)據(jù)(實際上是使用 JDK 工具生成),在這里有一個建議,如果沒有特殊需求,就是用默認的 1L 就可以,這樣可以確保代碼一致時反序列化成功。這也可能是造成序列化和反序列化失敗的原因,因為不同的序列化id之間不能進行序列化和反序列化。
6.序列化前和序列化后的對象的關(guān)系
是 '=='還是equal? or 是淺復(fù)制還是深復(fù)制?
答案:深復(fù)制,反序列化還原后的對象地址與原來的的地址不同
序列化前后對象的地址不同了,但是內(nèi)容是一樣的,而且對象中包含的引用也相同。換句話說,通過序列化操作,我們可以實現(xiàn)對任何可Serializable對象的”深度復(fù)制(deep copy)'——這意味著我們復(fù)制的是整個對象網(wǎng),而不僅僅是基本對象及其引用。對于同一流的對象,他們的地址是相同,說明他們是同一個對象,但是與其他流的對象地址卻不相同。也就說,只要將對象序列化到單一流中,就可以恢復(fù)出與我們寫出時一樣的對象網(wǎng),而且只要在同一流中,對象都是同一個。
7.靜態(tài)變量能否序列化
若把上面的代碼中的 age變量前加上 static ,輸出任然是
name=SheepMu
age=24
但是看下面的例子:
package com.sheepmu;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class MyTest implements Serializable{ private static final long serialVersionUID = 1L; private String name='SheepMu'; private static int age=24; public static void main(String[] args) {//以下代碼實現(xiàn)序列化 try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream('my.out'));//輸出流保存的文件名為 my.out ;ObjectOutputStream能把Object輸出成Byte流 MyTest myTest=new MyTest(); oos.writeObject(myTest); oos.flush(); //緩沖流 oos.close(); //關(guān)閉流 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } fan();//調(diào)用下面的 反序列化 代碼 } public static void fan() { new MyTest().name='SheepMu_1';//!!!!!!!!!!!!!!!!重點看這兩行 更改部分 age=1;//!!!!!!!!!!!!!!!!!!!重點看這兩行 更改部分 ObjectInputStream oin = null;//局部變量必須要初始化 try { oin = new ObjectInputStream(new FileInputStream('my.out')); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } MyTest mts = null; try { mts = (MyTest ) oin.readObject();//由Object對象向下轉(zhuǎn)型為MyTest對象 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println('name='+mts.name); System.out.println('age='+mts.age); }}
輸出結(jié)果為:
name=SheepMu
age=1
為何把最上面代碼的age變量添上static 后還是反序列化出了24呢?而新的從新對變量賦值的代碼,不是static的得到了序列化本身的值,而static的則得到的是從新附的值。原因: 序列化會忽略靜態(tài)變量,即序列化不保存靜態(tài)變量的狀態(tài)。靜態(tài)成員屬于類級別的,所以不能序列化。即 序列化的是對象的狀態(tài)不是類的狀態(tài)。這里的不能序列化的意思,是序列化信息中不包含這個靜態(tài)成員域。最上面添加了static后之所以還是輸出24是因為該值是JVM加載該類時分配的值。注:transient后的變量也不能序列化,但是情況稍復(fù)雜,稍后開篇說。
8、總結(jié):
a)當(dāng)一個父類實現(xiàn)序列化,子類自動實現(xiàn)序列化,不需要顯式實現(xiàn)Serializable接口;
b)當(dāng)一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進行序列化;
c) static,transient后的變量不能被序列化;