背景:一個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
