對象的序列化
當創建對象時,程序運行時它就會存在,但是程序停止時,對象也就消失了.
但是如果希望對象在程序不運行的情況下仍能存在並保存其信息,將會非常有用,對象將被
重建並且擁有與程序上次運行時擁有的信息相同。可以使用對象的序列化。
對象的序列化: 將內存中的對象直接寫入到文件設備中
對象的反序列化: 將文件設備中持久化的數據轉換爲內存對象
基本的序列化由兩個方法產生:一個方法用於序列化對象並將它們寫入一個流,
另一個方法用於讀取流並反序列化對象。
ObjectOutput
writeObject(Object obj)
將對象寫入底層存儲或流。
ObjectInput
readObject()
讀取並返回對象。
由於上述ObjectOutput和ObjectInput是接口,所以需要使用具體實現類。
ObjectOutput
ObjectOutputStream被寫入的對象必須實現一個接口:Serializable
否則會拋出:NotSerializableException
ObjectInput
ObjectInputStream 該方法拋出異常:ClassNotFountException
ObjectOutputStream和ObjectInputStream 對象分別需要字節輸出流和字節輸入流
對象來構建對象。也就是這兩個流對象需要操作已有對象將對象進行本地持久化存儲。
案例:
序列化和反序列化Cat對象。
public class Demo3 {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Cat cat = new Cat("tom", 3);
FileOutputStream fos = new FileOutputStream(new File("c:\\Cat.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cat);
System.out.println(cat);
oos.close();
FileInputStream fis = new FileInputStream(new File("c:\\Cat.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
Object readObject = ois.readObject();
Cat cat2 = (Cat) readObject;
System.out.println(cat2);
fis.close();
}
class Cat implements Serializable {
public String name;
public int age;
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Cat [name=" + name + ", age=" + age + "]";
}
}
Serializable
類通過實現 java.io.Serializable 接口以啓用其序列化功能。未實現此接口的類將
無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列
化接口沒有方法或字段,僅用於標識可序列化的語義。
所以需要被序列化的類必須是實現Serializable接口,該接口中沒有描述任何的屬性和
方法,稱之爲標記接口。
如果對象沒有實現接口Serializable,在進行序列化時會拋出NotSerializableException 異常。
注意:
保存一個對象的真正含義是什麼?如果對象的實例變量都是基本數據類型,那麼就非常簡
單。但是如果實例變量是包含對象的引用,會怎麼樣?保存的會是什麼?很顯然在Java中
保存引用變量的實際值沒有任何意義,因爲Java引用的值是通過JVM的單一實例的上下文
中才有意義。通過序列化後,嘗試在JVM的另一個實例中恢復對象,是沒有用處的。
如下:
首先建立一個Dog對象,也建立了一個Collar對象。Dog中包含了一個Collar(項圈)
現在想要保存Dog對象,但是Dog中有一個Collar,意味着保存Dog時也應該保存
Collar。假如Collar也包含了其他對象的引用,那麼會發生什麼?意味着保存一個Dog對
象需要清楚的知道Dog對象的內部結構。會是一件很麻煩的事情。
Java的序列化機制可以解決該類問題,當序列化一個對象時,Java的序列化機制會
負責保存對象的所有關聯的對象(就是對象圖),反序列化時,也會恢復所有的相關內容。
口的類纔可以序列化。如果只是Dog實現了該接口,而Collar沒有實現該接口。會發生什
麼?
Dog類和Collar類
import java.io.Serializable;
public class Dog implements Serializable {
private Collar collar;
private String name;
public Dog(Collar collar, String name) {
this.collar = collar;
this.name = name;
}
public Collar getCollar() {
return collar;
}
}
class Collar {
private int size;
public int getSize() {
return size;
}
public Collar(int size) {
this.size = size;
}
}
序列化
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo4 {
public static void main(String[] args) throws IOException {
Collar coll = new Collar(10);
Dog dog = new Dog(coll, "旺財");
FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(dog);
}
}
執行程序,出現了運行時異常。
Exception in thread "main" java.io.NotSerializableException: Collar
所以我們也必須將Dog中使用的Collar序列化。但是如果我們無法訪問Collar的源
代碼,或者無法使Collar可序列化,如何處理?
兩種解決方法:
一:繼承Collar類,使子類可序列化
-------------------
但是:如果Collar是final類,就無法繼承了。並且,如果Collar引用了其他非序列
化對象,也無法解決該問題。
二:transient
-----------
此時就可以使用transient修飾符,可以將Dog類中的成員變量標識爲transient
那麼在序列化Dog對象時,序列化就會跳過Collar。
public class Demo4 {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Collar coll = new Collar(10);
Dog dog = new Dog(coll, "旺財");
System.out.println(dog.getCollar().getSize());
FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(dog);
FileInputStream fos = new FileInputStream(new File("c:\\dog.txt"));
ObjectInputStream ois = new ObjectInputStream(fos);
Object readObject = ois.readObject();
Dog dog2 = (Dog) readObject;
dog2.getCollar().getSize();
}
}
這樣我們具有一個序列化的Dog和非序列化的Collar。
此時反序列化Dog後,訪問Collar,就會出現運行時異常
10
Exception in thread "main" java.lang.NullPointerException
注意:序列化不適用於靜態變量,因爲靜態變量並不屬於對象的實例變量的一部分。靜態
變量隨着類的加載而加載,是類變量。由於序列化只適用於對象。
基本數據類型可以被序列化
public class Demo5 {
public static void main(String[] args) throws IOException {
FileOutputStream fis = new FileOutputStream(new File("c:\\basic.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeDouble(3.14);
os.writeBoolean(true);
os.writeInt(100);
os.writeInt(200);
os.close();
FileInputStream fos = new FileInputStream(new File("c:\\basic.txt"));
ObjectInputStream ois = new ObjectInputStream(fos);
System.out.println(ois.readDouble());
System.out.println(ois.readBoolean());
System.out.println(ois.readInt());
System.out.println(ois.readInt());
fos.close();
}
}
serialVersionUID
用於給類指定一個UID。該UID是通過類中的可序列化成員的數字簽名運算出來的一個long
型的值。
只要是這些成員沒有變化,那麼該值每次運算都一樣。
該值用於判斷被序列化的對象和類文件是否兼容。
如果被序列化的對象需要被不同的類版本所兼容。可以在類中自定義UID。
定義方式:static final long serialVersionUID = 42L;