Java類實現serializable
1.Java序列化與反序列化
Java序列化是指把Java對象轉換為字節序列的過程;而Java反序列化是指把字節序列恢復為Java對象的過程。
2.為什么需要序列化與反序列化
我們知道,當兩個進程進行遠程通信時,可以相互發送各種類型的數據,包括文本、圖片、音頻、視頻等, 而這些數據都會以二進制序列的形式在網絡上傳送。那么當兩個Java進程進行通信時,能否實現進程間的對象傳送呢?答案是可以的。如何做到呢?這就需要Java序列化與反序列化了。換句話說,一方面,發送方需要把這個Java對象轉換為字節序列,然后在網絡上傳送;另一方面,接收方需要從字節序列中恢復出Java對象。
當我們明晰了為什么需要Java序列化和反序列化后,我們很自然地會想Java序列化的好處。其好處一是實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上(通常存放在文件里),二是,利用序列化實現遠程通信,即在網絡上傳送對象的字節序列。
3.如何實現Java序列化與反序列化
1)JDK類庫中序列化API
java.io.ObjectOutputStream:表示對象輸出流
它的writeObject(Object obj)方法可以對參數指定的obj對象進行序列化,把得到的字節序列寫到一個目標輸出流中。
java.io.ObjectInputStream:表示對象輸入流
它的readObject()方法源輸入流中讀取字節序列,再把它們反序列化成為一個對象,並將其返回。
2)實現序列化的要求
只有實現了Serializable或Externalizable接口的類的對象才能被序列化,否則拋出異常。
3)實現Java對象序列化與反序列化的方法
假定一個Student類,它的對象需要序列化,可以有如下三種方法:
方法一:若Student類僅僅實現了Serializable接口,則可以按照以下方式進行序列化和反序列化
ObjectOutputStream采用默認的序列化方式,對Student對象的非transient的實例變量進行序列化。
ObjcetInputStream采用默認的反序列化方式,對對Student對象的非transient的實例變量進行反序列化。
方法二:若Student類僅僅實現了Serializable接口,並且還定義了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),則采用以下方式進行序列化與反序列化。
ObjectOutputStream調用Student對象的writeObject(ObjectOutputStream out)的方法進行序列化。
ObjectInputStream會調用Student對象的readObject(ObjectInputStream in)的方法進行反序列化。
方法三:若Student類實現了Externalnalizable接口,且Student類必須實現readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,則按照以下方式進行序列化與反序列化。
ObjectOutputStream調用Student對象的writeExternal(ObjectOutput out))的方法進行序列化。
ObjectInputStream會調用Student對象的readExternal(ObjectInput in)的方法進行反序列化。
4)JDK類庫中序列化的步驟
步驟一:創建一個對象輸出流,它可以包裝一個其它類型的目標輸出流,如文件輸出流:
ObjectOutputStream out = new ObjectOutputStream(new fileOutputStream(“D:\\objectfile.obj”));
步驟二:通過對象輸出流的writeObject()方法寫對象:
out.writeObject(“Hello”);
out.writeObject(new Date());
5)JDK類庫中反序列化的步驟
步驟一:創建一個對象輸入流,它可以包裝一個其它類型輸入流,如文件輸入流:
ObjectInputStream in = new ObjectInputStream(new fileInputStream(“D:\\objectfile.obj”));
步驟二:通過對象輸出流的readObject()方法讀取對象:
String obj1 = (String)in.readObject();
Date obj2 = (Date)in.readObject();
說明:為了正確讀取數據,完成反序列化,必須保證向對象輸出流寫對象的順序與從對象輸入流中讀對象的順序一致。
為了更好地理解Java序列化與反序列化,選擇方法一編碼實現。
Student類定義如下:
- package com.jieke.io;
- import java.io.Serializable;
- /**
- *Title:學生類
- *Description:實現序列化接口的學生類
- *Copyright: copyright(c) 2012
- *Filename: Student.java
- *@author Wang Luqing
- *@version 1.0
- */
- public class Student implements Serializable
- {
- private String name;
- private char sex;
- private int year;
- private double gpa;
- public Student()
- {
- }
- public Student(String name,char sex,int year,double gpa)
- {
- this.name = name;
- this.sex = sex;
- this.year = year;
- this.gpa = gpa;
- }
- public void setName(String name)
- {
- this.name = name;
- }
- public void setSex(char sex)
- {
- this.sex = sex;
- }
- public void setYear(int year)
- {
- this.year = year;
- }
- public void setGpa(double gpa)
- {
- this.gpa = gpa;
- }
- public String getName()
- {
- return this.name;
- }
- public char getSex()
- {
- return this.sex;
- }
- public int getYear()
- {
- return this.year;
- }
- public double getGpa()
- {
- return this.gpa;
- }
- }
-
package com.jieke.io;
-
import java.io.Serializable;
-
-
/**
-
*Title:學生類
-
*Description:實現序列化接口的學生類
-
*Copyright: copyright(c) 2012
-
*Filename: Student.java
-
*@author Wang Luqing
-
*@version 1.0
-
*/
-
public class Student implements Serializable
-
{
-
private String name;
-
private char sex;
-
private int year;
-
private double gpa;
-
-
public Student()
-
{
-
-
}
-
public Student(String name,char sex,int year,double gpa)
-
{
-
this.name = name;
-
this.sex = sex;
-
this.year = year;
-
this.gpa = gpa;
-
}
-
-
public void setName(String name)
-
{
-
this.name = name;
-
}
-
-
public void setSex(char sex)
-
{
-
this.sex = sex;
-
}
-
-
public void setYear(int year)
-
{
-
this.year = year;
-
}
-
-
public void setGpa(double gpa)
-
{
-
this.gpa = gpa;
-
}
-
-
public String getName()
-
{
-
return this.name;
-
}
-
-
public char getSex()
-
{
-
return this.sex;
-
}
-
-
public int getYear()
-
{
-
return this.year;
-
}
-
-
public double getGpa()
-
{
-
return this.gpa;
-
}
-
}
把Student類的對象序列化到文件O:\\Java\\com\\jieke\\io\\student.txt,並從該文件中反序列化,向console顯示結果。代碼如下:
- import java.io.*;
- /**
- *Title:應用學生類
- *Description:實現學生類實例的序列化與反序列化
- *Copyright: copyright(c) 2012
- *Filename: UseStudent.java
- *@author Wang Luqing
- *@version 1.0
- */
- public class UseStudent
- {
- public static void main(String[] args)
- {
- Student st = new Student("Tom",'M',20,3.6);
- File file = new File("O:\\Java\\com\\jieke\\io\\student.txt");
- try
- {
- file.createNewFile();
- }
- catch(IOException e)
- {
- e.printStackTrace();
- }
- try
- {
- //Student對象序列化過程
- FileOutputStream fos = new FileOutputStream(file);
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(st);
- oos.flush();
- oos.close();
- fos.close();
- //Student對象反序列化過程
- FileInputStream fis = new FileInputStream(file);
- ObjectInputStream ois = new ObjectInputStream(fis);
- Student st1 = (Student) ois.readObject();
- System.out.println("name = " + st1.getName());
- System.out.println("sex = " + st1.getSex());
- System.out.println("year = " + st1.getYear());
- System.out.println("gpa = " + st1.getGpa());
- ois.close();
- fis.close();
- }
- catch(ClassNotFoundException e)
- {
- e.printStackTrace();
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- }
- }
-
import java.io.*;
-
-
/**
-
*Title:應用學生類
-
*Description:實現學生類實例的序列化與反序列化
-
*Copyright: copyright(c) 2012
-
*Filename: UseStudent.java
-
*@author Wang Luqing
-
*@version 1.0
-
*/
-
-
public class UseStudent
-
{
-
public static void main(String[] args)
-
{
-
Student st = new Student("Tom",'M',20,3.6);
-
File file = new File("O:\\Java\\com\\jieke\\io\\student.txt");
-
try
-
{
-
file.createNewFile();
-
}
-
catch(IOException e)
-
{
-
e.printStackTrace();
-
}
-
try
-
{
-
//Student對象序列化過程
-
FileOutputStream fos = new FileOutputStream(file);
-
ObjectOutputStream oos = new ObjectOutputStream(fos);
-
oos.writeObject(st);
-
oos.flush();
-
oos.close();
-
fos.close();
-
-
//Student對象反序列化過程
-
FileInputStream fis = new FileInputStream(file);
-
ObjectInputStream ois = new ObjectInputStream(fis);
-
Student st1 = (Student) ois.readObject();
-
System.out.println( "name = " + st1.getName());
-
System.out.println( "sex = " + st1.getSex());
-
System.out.println( "year = " + st1.getYear());
-
System.out.println( "gpa = " + st1.getGpa());
-
ois.close();
-
fis.close();
-
}
-
catch(ClassNotFoundException e)
-
{
-
e.printStackTrace();
-
}
-
catch (IOException e)
-
{
-
e.printStackTrace();
-
}
-
}
-
}
結果如下所示:
name = Tom
sex = M
year = 20
gpa = 3.6
總結:
1)Java序列化就是把對象轉換成字節序列,而Java反序列化就是把字節序列還原成Java對象。
2)采用Java序列化與反序列化技術,一是可以實現數據的持久化,在MVC模式中很是有用;二是可以對象數據的遠程通信。
Java中實現序列化的兩種方式 Serializable 接口和 Externalizable接口
對象的序列化就是將對象寫入輸出流中。
反序列化就是從輸入流中將對象讀取出來。
用來實現序列化的類都在java.io包中,我們常用的類或接口有:
ObjectOutputStream:提供序列化對象並把其寫入流的方法
ObjectInputStream:讀取流並反序列化對象
Serializable:一個對象想要被序列化,那么它的類就要實現 此接口,這個對象的所有屬性(包括private屬性、包括其引用的對象)都可以被序列化和反序列化來保存、傳遞。
Externalizable:他是Serializable接口的子類,有時我們不希望序列化那么多,可以使用這個接口,這個接口的writeExternal()和readExternal()方法可以指定序列化哪些屬性;
但是如果你只想隱藏一個屬性,比如用戶對象user的密碼pwd,如果使用Externalizable,並除了pwd之外的每個屬性都寫在writeExternal()方法里,這樣顯得麻煩,可以使用Serializable接口,並在要隱藏的屬性pwd前面加上transient就可以實現了。
方法一:
實現Serializable接口。
序列化的時候的一個關鍵字:transient(臨時的)。它聲明的變量實行序列化操作的時候不會寫入到序列化文件中去。
例子:
package demo2; import java.io.Serializable; //實現Serializable接口才能被序列化 public class UserInfo implements Serializable{ private String userName; private String usePass; private transient int userAge;//使用transient關鍵字修飾的變量不會被序列化 public String getUserName() { return userName; } public UserInfo() { userAge=20; } public UserInfo(String userName, String usePass, int userAge) { super(); this.userName = userName; this.usePass = usePass; this.userAge = userAge; } public void setUserName(String userName) { this.userName = userName; } public String getUsePass() { return usePass; } public void setUsePass(String usePass) { this.usePass = usePass; } public int getUserAge() { return userAge; } public void setUserAge(int userAge) { this.userAge = userAge; } @Override public String toString() { return "UserInfo [userName=" + userName + ", usePass=" + usePass + ",userAge="+(userAge==0?"NOT SET":userAge)+"]"; } }
package demo2; 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.util.Date; public class UserInfoTest { /** * 序列化對象到文件 * @param fileName */ public static void serialize(String fileName){ try { ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName)); out.writeObject("序列化的日期是:");//序列化一個字符串到文件 out.writeObject(new Date());//序列化一個當前日期對象到文件 UserInfo userInfo=new UserInfo("郭大俠","961012",21); out.writeObject(userInfo);//序列化一個會員對象 out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 從文件中反序列化對象 * @param fileName */ public static void deserialize(String fileName){ try { ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName)); String str=(String) in.readObject();//剛才的字符串對象 Date date=(Date) in.readObject();//日期對象 UserInfo userInfo=(UserInfo) in.readObject();//會員對象 System.out.println(str); System.out.println(date); System.out.println(userInfo); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args){ // serialize("text"); deserialize("text");//這里userAge取讀不到是因為使用了transient修飾,所以得到的是默認值 /** * 我修改了一下UserInfo的無參構造,在無參構造中給userAge屬性賦值蛋反序列化得到的結果還是一樣。 * 得出結論: * 當從磁盤中讀出某個類的實例時,實際上並不會執行這個類的構造函數, * 而是載入了一個該類對象的持久化狀態,並將這個狀態賦值給該類的另一個對象。 */ } }
方法二:
實現Externalizable接口:
使用這個接口的場合是這樣的:
一個類中我們只希望序列化一部分數據,其他數據都使用transient修飾的話顯得有點麻煩,這時候我們使用externalizable接口,指定序列化的屬性。
例子:
package demo2;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
//實現Externalizable接口序列化
public class UserInfo implements Externalizable{
private String userName;
private String usePass;
private int userAge;
public String getUserName() {
return userName;
}
public UserInfo() {
userAge=20;//這個是在第二次測試使用,判斷反序列化是否通過構造器
}
public UserInfo(String userName, String usePass, int userAge) {
super();
this.userName = userName;
this.usePass = usePass;
this.userAge = userAge;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUsePass() {
return usePass;
}
public void setUsePass(String usePass) {
this.usePass = usePass;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
@Override
public String toString() {
return "UserInfo [userName=" + userName + ", usePass=" + usePass + ",userAge="+(userAge==0?"NOT SET":userAge)+"]";
}
public void writeExternal(ObjectOutput out) throws IOException {
/*
* 指定序列化時候寫入的屬性。這里仍然不寫入年齡
*/
out.writeObject(userName);
out.writeObject(usePass);
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
/*
* 指定反序列化的時候讀取屬性的順序以及讀取的屬性
* 如果你寫反了屬性讀取的順序,你可以發現反序列化的讀取的對象的指定的屬性值也會與你寫的讀取方式一一對應。因為在文件中裝載對象是有序的
*/
userName=(String) in.readObject();
usePass=(String) in.readObject();
}
}
測試:
package demo2;
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.util.Date;
public class UserInfoTest {
/**
* 序列化對象到文件
* @param fileName
*/
public static void serialize(String fileName){
try {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));
out.writeObject("序列化的日期是:");//序列化一個字符串到文件
out.writeObject(new Date());//序列化一個當前日期對象到文件
UserInfo userInfo=new UserInfo("郭大俠","961012",21);
out.writeObject(userInfo);//序列化一個會員對象
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 從文件中反序列化對象
* @param fileName
*/
public static void deserialize(String fileName){
try {
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));
String str=(String) in.readObject();//剛才的字符串對象
Date date=(Date) in.readObject();//日期對象
UserInfo userInfo=(UserInfo) in.readObject();//會員對象
System.out.println(str);
System.out.println(date);
System.out.println(userInfo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
// serialize("text");
deserialize("text");
/**
* 我修改了一下UserInfo的無參構造,在無參構造中給userAge屬性賦值蛋反序列化得到的結果是userAge變成了20。
* 得出結論:
* 當從磁盤中讀出某個類的實例時,如果該實例使用的是Externalizable序列化,會執行這個類的構造函數,
* 然后調用readExternal給其他屬性賦值
*/
}
}
原理分析:
總結:
首先,我們在序列化UserInfo對象的時候,由於這個類實現了Externalizable 接口,在writeExternal()方法里定義了哪些屬性可以序列化,哪些不可以序列化,所以,對象在經過這里就把規定能被序列化的序列化保存文件,不能序列化的不處理,然后在反序列的時候自動調用readExternal()方法,根據序列順序挨個讀取進行反序列,並自動封裝成對象返回,然后在測試類接收,就完成了反序列
一些api:
Externalizable 實例類的唯一特性是可以被寫入序列化流中,該類負責保存和恢復實例內容。 若某個要完全控制某一對象及其超類型的流格式和內容,則它要實現 Externalizable 接口的 writeExternal 和 readExternal 方法。這些方法必須顯式與超類型進行協調以保存其狀態。這些方法將代替定制的 writeObject 和 readObject 方法實現。
writeExternal(ObjectOutput out)
該對象可實現 writeExternal 方法來保存其內容,它可以通過調用 DataOutput 的方法來保存其基本值,或調用 ObjectOutput 的 writeObject 方法來保存對象、字符串和數組。
readExternal(ObjectInput in)
對象實現 readExternal 方法來恢復其內容,它通過調用 DataInput 的方法來恢復其基礎類型,調用 readObject 來恢復對象、字符串和數組。
externalizable和Serializable的區別:(靜態屬性持保留意見,60%偏向不能直接序列化)
1:
實現serializable接口是默認序列化所有屬性,如果有不需要序列化的屬性使用transient修飾。
externalizable接口是serializable的子類,實現這個接口需要重寫writeExternal和readExternal方法,指定對象序列化的屬性和從序列化文件中讀取對象屬性的行為。
2:
實現serializable接口的對象序列化文件進行反序列化不走構造方法,載入的是該類對象的一個持久化狀態,再將這個狀態賦值給該類的另一個變量
實現externalizable接口的對象序列化文件進行反序列化先走構造方法得到控對象,然后調用readExternal方法讀取序列化文件中的內容給對應的屬性賦值。
serialVersionUID作用:序列化時為了保持版本的兼容性,即在版本升級時反序列化仍保持對象的唯一性。 有兩種生成方式: 一個是默認的1L,比如:private static final long se...
serialVersionUID作用:
序列化時為了保持版本的兼容性,即在版本升級時反序列化仍保持對象的唯一性。
有兩種生成方式:
一個是默認的1L,比如:private static final long serialVersionUID = 1L;
一個是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段;
幾個問題:
1、 如果一個類沒有實現Serializable接口,但是它的基類實現 了,這個類可不可以序列化?
2、 和上面相反,如果一個類實現了Serializable接口,但是它的父類沒有實現 ,這個類可不可以序列化?
第1個問題:一個類實現 了某接口,那么它的所有子類都間接實現了此接口,所以它可以被 序列化。
第2個問題:Object是每個類的超類,但是它沒有實現 Serializable接口,但是我們照樣在序列化對象,所以說明一個類要序列化,它的父類不一定要實現Serializable接口。但是在父類中定義 的狀態能被正確 的保存以及讀取嗎?
第3個問題:如果將一個對象寫入某文件(比如是a),那么之后對這個對象進行一些修改,然后把修改的對象再寫入文件a,那么文件a中會包含該對象的兩個 版本嗎?
關於幾個問題的答案見這個博客:http://blog.csdn.net/moreevan/article/details/6698529(我是從上面copy轉過來的,不願深入了)
Java將對象保存到文件中/從文件中讀取對象
1.保存對象到文件中
Java語言只能將實現了Serializable接口的類的對象保存到文件中,利用如下方法即可:
public static void writeObjectToFile(Object obj) { File file =new File("test.dat"); FileOutputStream out; try { out = new FileOutputStream(file); ObjectOutputStream objOut=new ObjectOutputStream(out); objOut.writeObject(obj); objOut.flush(); objOut.close(); System.out.println("write object success!"); } catch (IOException e) { System.out.println("write object failed"); e.printStackTrace(); } }
參數obj一定要實現Serializable接口,否則會拋出java.io.NotSerializableException異常。另外,如果寫入的對象是一個容器,例如List、Map,也要保證容器中的每個元素也都是實現 了Serializable接口。例如,如果按照如下方法聲明一個Hashmap,並調用writeObjectToFile方法就會拋出異常。但是如果是Hashmap<String,String>就不會出問題,因為String類已經實現了Serializable接口。另外如果是自己創建的類,如果繼承的基類沒有實現Serializable,那么該類需要實現Serializable,否則也無法通過這種方法寫入到文件中。
Object obj=new Object(); //failed,the object in map does not implement Serializable interface HashMap<String, Object> objMap=new HashMap<String,Object>(); objMap.put("test", obj); writeObjectToFile(objMap);
2.從文件中讀取對象
可以利用如下方法從文件中讀取對象
public static Object readObjectFromFile() { Object temp=null; File file =new File("test.dat"); FileInputStream in; try { in = new FileInputStream(file); ObjectInputStream objIn=new ObjectInputStream(in); temp=objIn.readObject(); objIn.close(); System.out.println("read object success!"); } catch (IOException e) { System.out.println("read object failed"); e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return temp; }
讀取到對象后,再根據對象的實際類型進行轉換即可。
java中類實現Serializable接口的原因
背景:一個java中的類只有實現了Serializable接口,它的對象才是可序列化的。如果要序列化某些類的對象,這些類就必須實現Serializable接口。Serializable是一個空接口,沒有什么具體內容,它的目的只是簡單的標識一個類的對象可以被序列化。
為什么要進實現Serializable接口:為了保存在內存中的各種對象的狀態(也就是實例變量,不是方法),並且可以把保存的對象狀態再讀出來,這是java中的提供的保存對象狀態的機制—序列化。
在什么情況下需要使用到Serializable接口呢?
1、當想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
2、當想用套接字在網絡上傳送對象的時候;
3、當想通過RMI傳輸對象的時候;
serialVersionUID
serialVersionUID的取值是Java運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID的取值有可能也會發生變化。類的serialVersionUID的默認值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的serialVersionUID,也有可能相同。為了提高serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類中顯示的定義serialVersionUID,為它賦予明確的值。顯式地定義serialVersionUID有兩種用途:
a. 在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;
b. 在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。
代碼實現:
在這里 定義一個實現了Serializable接口的Person類
import java.io.Serializable; public class Person implements Serializable { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
再定義一個SerializationUtils類來模擬 序列化和反序列化的過程
import java.io.*;
public class SerializationUtils { private static String FILE_NAME = "f:/obj"; //序列化 寫的過程 public static void write(Serializable s){ try { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(FILE_NAME)); objectOutputStream.writeObject(s); objectOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } //反序列化 讀的過程 public static Object read(){ Object obj=null; // 反序列化 try { ObjectInput input = new ObjectInputStream(new FileInputStream(FILE_NAME)); obj = input.readObject(); input.close(); } catch (Exception e) { e.printStackTrace(); } return obj; } }
測試函數
import com.txp.SerializationUtils; import org.junit.Test; public class testSerializable { @Test public void testWrite(){ Person person=new Person(); person.setId(1); person.setName("張丹"); SerializationUtils.write(person); } @Test public void testRead(){ Person p = (Person) SerializationUtils.read(); System.out.println(p.getName()); } }
先運行testWrite()實現序列化持久化,再運行testRead()實現反序列化讀出數據 ,這一次的Person類中沒有給定serialVersionUID
,結果會輸出‘張丹’。
如果此時給Person類加一個屬性 age,運行testRead(),會拋出會拋出 java.io.InvalidClassException異常。因為JVM在反序列化時,會比較數據流中的serialVersionUID與類的serialVersionUID是否相同,如果相同,則認為類沒有發生改變,可以把數據流load為實例對象;如果不相同,對不起,JVM會拋異常InvalidClassException
,這是JVM一個很好的一個校驗機制,確保類的一致性。
但是如果顯式給定serialVersionUID
(而隱式聲明則是我不聲明,編譯器在編譯的時候幫我生成。),即是 private static final long serialVersionUID = XXL;,修改Person類如下:
public class Person implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
再進行同樣的操作過程,則不會拋出異常,會打印出結果。但是最好不要這樣操作,要在類修改后,先序列化,再但序列化。確保類的前后一致性。
參考文章:
https://www.cnblogs.com/yoohot/p/6019767.html
https://blog.csdn.net/jaryle/article/details/52598296
http://www.cnblogs.com/DreamDrive/p/5412931.html