今天一個朋友問我,JAVA序列化到底是怎麼回事兒,是有什麼作用,一時沒答上來,通過查閱了各種文檔,還有jdk才算是弄明白了,下面我用簡單明了的語言和程式來解釋一下:
JAVA語言隻能将實作了Serializable接口的類的對象儲存到檔案和網絡傳輸流中,這句話很重要一定要了解;
序列化是為反序列化服務的,序列化是讓對象轉化成位元組流能通過網絡傳輸或者儲存到檔案中,反序列化就是從網絡傳輸流或者檔案中能反編譯出這個對象,主要應用例如:RMI要利用對象序列化運作遠端主機上的服務,就像在本地機上運作對象時一樣。
下面用實際的例子來解釋一下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Test implements Serializable{
// private static final long serialVersionUID = 7675836097652452739L;
private String name;
private int age;
public static void main(String[] args) {
Test t = new Test();
t.setAge(26);
t.setName("皇上");
Test.writeObjectToFile(t);
System.out.println(Test.readObjectFromFile());
}
public static void writeObjectToFile(Object obj){
File file =new File("e://test.txt");
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
ObjectOutputStream oos=new ObjectOutputStream(out);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println("寫入對象成功");
} catch (IOException e) {
System.out.println("寫入對象失敗");
e.printStackTrace();
}
}
public static Object readObjectFromFile(){
Object temp=null;
File file =new File("e://test.txt");
FileInputStream in = null;
try {
in = new FileInputStream(file);
ObjectInputStream ois=new ObjectInputStream(in);
temp=ois.readObject();
ois.close();
System.out.println("讀取對象成功");
} catch (IOException e) {
System.out.println("讀取對象失敗");
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return temp;
}
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;
}
@Override
public String toString() {
return "Test [name=" + name + ", age=" + age + "]";
}
}
執行main方法,列印結果如下:
寫入對象成功
讀取對象成功
Test [name=皇上, age=26]
發現讀寫都成功,說明我們序列化和反序列化成功。
現在去掉Serializable實作,再執行main方法,列印結果如下:
寫入對象失敗
java.io.NotSerializableException: Test
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at Test.writeObjectToFile(Test.java:30)
at Test.main(Test.java:20)
讀取對象失敗
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: Test
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at Test.readObjectFromFile(Test.java:47)
at Test.main(Test.java:21)
Caused by: java.io.NotSerializableException: Test
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at Test.writeObjectToFile(Test.java:30)
at Test.main(Test.java:20)
null
發現讀寫都不成功了,沒有序列化的對象既不能寫入檔案,同時也不能被讀出來;
這也證明了上面說的一個話:JAVA語言隻能将實作了Serializable接口的類的對象儲存到檔案中。
然後大家可能會發現我注釋了private static final long serialVersionUID = 7675836097652452739L;
這行代碼是做什麼用的呢?
序列化運作時使用一個稱為 serialVersionUID 的版本号與每個可序列化類(是類不是對象哦)相關聯,該序列号在反序列化過程中用于驗證序列化對象的發送方和接收方是否為該對象加載了與序列化相容的類。如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本号不同,則反序列化将會導緻 InvalidClassException。序列化類可以通過聲明名為 "serialVersionUID" 的字段(該字段必須是靜态 (static)、最終 (final) 的 long 型字段)顯式聲明其自己的 serialVersionUID
下面驗證一下,我們打開serialVersionUID注釋,同時Test還需要實作Serializable接口,
把main方法中的Test.writeObjectToFile(t);這行注釋掉,我們隻從檔案中讀資料
執行main方法,列印結果如下:
讀取對象失敗
java.io.InvalidClassException: Test; local class incompatible: stream classdesc serialVersionUID = 3332569308856534758, local class serialVersionUID = 7675836097652452739
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at Test.readObjectFromFile(Test.java:47)
at Test.main(Test.java:21)
null
為什麼會打開serialVersionUID這行代碼就會報錯呢,這不是在同一個類裡執行的程式嗎?
是的,是在一個類裡執行的,但是寫進檔案的對象是沒有serialVersionUID靜态常量的(使用的是預設的),而讀的時候我增加了serialVersionUID 靜态常量
這樣我們簡單的驗證了 serialVersionUID 與對應的發送者的類的版本号不同,則反序列化将會導緻 InvalidClassException。
如有其他意見請回複,期待和各位共同讨論~