Java 之 Serializable 序列化和反序列化
序列化基本概念:
序列化:把對象轉換爲字節序列化的過程稱爲對象的序列化。
反序列化:把字節序列恢復爲對象的過程稱之爲對象的反序列化。
什麼情況下需要序列化
當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
當你想用套接字在網絡上傳送對象的時候;
當你想通過RMI傳輸對象的時候;
當需要深度clone的時候做序列化和反序列化也可以得到一個全新的對象(詳細可以了設計模式的原型模式)
實現序列化
實現Serializable接口
設置兩個類:
地址類
public class Address implements Serializable{
String pro;
String city;
String zip;
public Address(String p,String c,String z){
pro = p;city=c;zip =z;
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
個人信息類
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
String name;
int age;
Address add; // 籍貫信息
public Student(String na,int a,Address add){
this.name = na;
this.age = a;
this.add = add;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAdd() {
return add;
}
public void setAdd(Address add) {
this.add = add;
}
}
測試實現類(我是在springboot中測試的 忽略上面@標註)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TaotaoApplication.class)
@ContextConfiguration
public class PortotypeTest {
@Test
public void testPorto()throws IOException{
Address address = new Address("天津市","南開區","300000");
Student student = new Student("tom",22, address);
// 對象輸出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("c:/student.txt")));
objectOutputStream.writeObject(student); // 序列化
objectOutputStream.close();
}
@Test
public void testPortoReturn() throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("c:/student.txt")));
Student student = (Student) objectInputStream.readObject();
System.out.println( student.getName());
System.out.println( student.getAge());
System.out.println( student.getAdd().getCity());
}
// 反序列化 操作
// 2018-06-07 21:46:06,794:INFO main (StartupInfoLogger.java:57) - Started PortotypeTest in 2.622 seconds (JVM running for 3.979)
// tom
// 22
// 南開區
}
testPorto 爲序列化操作 測試成功打開student 會看到有內容
testPortoReturn 爲反序列化
注意 此處address一定也要實現 Serializable 接口否則無法實現序列化和反序列化
注意不被序列化情況
1 static關鍵字修飾的變量
我們在student中增加一個屬性字段:
public static String shotname = "to";
此時序列化到文件中
修改shotname 的值
public static String shotname = "to555";
在進行反序列化(在序列化成功的時候shotname 的值爲to )
執行:
@Test
public void testPortoReturn() throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("c:/student.txt")));
Student student = (Student) objectInputStream.readObject();
System.out.println( student.getName());
System.out.println( student.getAge());
System.out.println( student.getShotname());
System.out.println( student.getAdd().getCity());
}
得到結果:
2018-06-07 21:59:51,262:INFO main (StartupInfoLogger.java:57) - Started PortotypeTest in 2.738 seconds (JVM running for 4.119)
tom
22
to555
南開區
所以status修飾的變量不會被序列化
2 transient修飾的變量
在實際開發過程中,我們常常會遇到這樣的問題,這個類的有些屬性需要序列化,而其他屬性不需要被序列化,打個比方,如果一個用戶有一些敏感信息(如密碼,銀行卡號等),爲了安全起見,不希望在網絡操作(主要涉及到序列化操作,本地序列化緩存也適用)中被傳輸,這些信息對應的變量就可以加上transient關鍵字。換句話說,這個字段的生命週期僅存於調用者的內存中而不會寫到磁盤裏持久化。(具體代碼類似status)
serialVersionUID = 1L
解釋下面一行代碼
private static final long serialVersionUID = 1L;
序列化運行時使用一個稱爲 serialVersionUID 的版本號與每個可序列化類相關聯,該序列號在反序列化過程中用於驗證序列化對象的發送者和接收者是否爲該對象加載了與序列化兼容的類。如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本號不同,則反序列化將會導致 InvalidClassException。可序列化類可以通過聲明名爲 “serialVersionUID” 的字段(該字段必須是靜態 (static)、最終 (final) 的 long 型字段)顯式聲明其自己的 serialVersionUID:
如果可序列化類未顯式聲明 serialVersionUID,則序列化運行時將基於該類的各個方面計算該類的默認 serialVersionUID 值,如“Java(TM) 對象序列化規範”中所述。不過,強烈建議 所有可序列化類都顯式聲明 serialVersionUID 值,原因是計算默認的 serialVersionUID 對類的詳細信息具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在反序列化過程中可能會導致意外的 InvalidClassException。因此,爲保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用於直接聲明類 – serialVersionUID 字段作爲繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有默認的計算值,但是數組類沒有匹配 serialVersionUID 值的要求。
總之建議用到序列化就設置該值,否則序列化成功後反序列化該值不匹配的話會報NotSerializableException