Java1.1中實現了Serializable接口的對象都可被轉換成一系列字節,並可完全恢復成原狀。Java中的兩個特性運用了對象序列化:遠程調用方法(RMI),Java Beans
要序列化一個對象(其類已實現Serializable接口),需要創建一些OutputStream對象,將其封裝到ObjectOutputStream對象中,調用writeObject()方法即可將序列化的對象發生到OutputStream中,比如文件、String、網絡等。當要恢復對象時,只需將InputStream封裝到ObjectInputStream對象中,調用readObject()方法即可,當然此時必須上溯造型到原始類型。
1. Serializable接口實例
package com.test;
import java.io.*;
class Data implements Serializable {
private int i;
Data(int x) { i = x; }
public String toString() {
return Integer.toString(i);
}
}
public class Serial implements Serializable {
//Generate random int
private static int r() {
return (int)(Math.random() * 10);
}
private Data[] d = {new Data(r()), new Data(r()), new Data(r())};
private Serial next;
private char c;
Serial(int i, char x) {
System.out.println(" Serial constructor: " + i);
c = x;
if (--i >0)
next = new Serial(i, (char)(x+1));
}
Serial() {
System.out.println("Default constructor");
}
public String toString() {
String s = ":" + c + "(";
for (int i = 0; i < d.length; i++) {
s += d[i].toString();
}
s += ")";
if (next != null)
s += next.toString();
return s;
}
public static void main(String[] args) {
Serial w = new Serial(6, 'e');
System.out.println("w = " + w);
try {
//write the serialized objects to file
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("serial.out"));
out.writeObject("Serial test");
out.writeObject(w);
out.close(); //will also flush output
//read and restore serialized objects from file
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("serial.out"));
String s = (String)in.readObject();
Serial w2 = (Serial)in.readObject();
System.out.println(s + ", w2 = " + w2);
} catch (Exception e) {
e.printStackTrace();
}
try {
//write the serialized objects to memory
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject("Serial test");
out.writeObject(w);
out.flush();
//read and restore from memory
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bout.toByteArray()));
String s = (String)in.readObject();
Serial w3 = (Serial)in.readObject();
System.out.println(s + ", w3 = " + w3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在讀取並恢復Serializable對象的時候不會允許構建器,而且對象序列化沒有用到IO系統的Reader和Writer,而是使用Java1.0的InputStream和OutputStream體系。2. Externalizable接口
實現了Serializable接口的對象序列化時,默認會將類中所有字段包含進去。但是有時涉及安全問題,有些字段我們不想被序列化,這時可使用Externalizable接口控制序列化過程。Externalizable接口擴展了Serializable接口,並添加了兩個方法writeExternal()和readExternal()。在序列化和恢復的過程中會自動調用這兩個方法,我們可以通過改寫他們控制序列化和恢復過程。
實例1,實現Externalizable接口的對象在恢復是會調用類的默認構建器,注意Blip2的默認構建器不是public的,這樣會導致調用權限不夠而產生異常
package com.test;
import java.io.*;
import java.util.*;
class Blip1 implements Externalizable {
public Blip1() {
System.out.println("Blip1 constructor");
}
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip1.writeExternal");
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blip1.readExternal");
}
}
class Blip2 implements Externalizable {
Blip2() {
System.out.println("Blip2 constructor");
}
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip2.writeExternal");
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blip2.readExternal");
}
}
public class External {
public static void main(String[] args) {
System.out.println("Constructing objects: ");
Blip1 b1 = new Blip1();
Blip2 b2 = new Blip2();
try {
//write serialzed objects
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("Blips.out"));
System.out.println("Saving objects: ");
out.writeObject(b1);
out.writeObject(b2);
out.close();
//read and restore objects
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("Blips.out"));
System.out.println("Recovering b1:");
b1 = (Blip1)in.readObject();
//following will throw exception
//as Blip2's default constructor is not public
//b2 = (Blip2)in.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
實例2,在readExternal和writeExternal中控制序列化過程
package com.test;
import java.io.*;
import java.util.*;
public class External2 implements Externalizable {
int i;
String s; //not initialize
public External2() {
System.out.println("External2 constructor");
}
public External2(String x, int a) {
System.out.println("External2(String x, int a)");
s = x;
i = a;
}
public String toString() { return s + i; }
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("External2.writeExternal");
//write the fields you want to serialize
//you can only serialize some fields
out.writeObject(s);
out.writeObject(i);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("External2.readExternal");
//Restore the fields that are serialized
s = (String)in.readObject();
i = in.readInt();
}
public static void main(String[] args) {
System.out.println("Constructing objects: ");
External2 e2 = new External2("A string", 42);
System.out.println(e2.toString());
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("external2.out"));
System.out.println("Saving object: ");
out.writeObject(e2);
out.close();
//restore object
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("external2.out"));
System.out.println("Recovering e2:");
e2 = (External2)in.readObject();
System.out.println(e2.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. transient關鍵字
實現Serializable接口的類序列化時默認所有fields都會被序列化,transient關鍵字可以關閉fields的序列化。設置成transient的字段不參與序列化,恢復時簡單的設置成默認值,如int 0, 對象句柄 null。
4. writeObject()和readObject()方法代替Externalizable
實現Serializable接口,並添加writeObject()和readObject()方法也可以控制序列化過程。但是這兩個方法在類中都被定義成private,按理說外部類是不能調用的,這裏序列化過程繞過了檢查,這種繞過檢查的機制我們普通用戶是不可能接觸到的。。。
private void writeObject(ObjectOutputStream stream) throws IOException;
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException;
實例:
//: SerialCtl.java
//Controlling serialization by adding your own
//writeObject() and readObject() methods.
import java.io.*;
public class SerialCtl implements Serializable {
String a;
transient String b;
public SerialCtl(String aa, String bb) {
a = "Not Transient: " + aa;
b = "Transient: " + bb;
}
public String toString() {
return a + "\n" + b;
}
private void
writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
stream.writeObject(b);
}
private void
readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
b = (String)stream.readObject();
}
public static void main(String[] args) {
SerialCtl sc =
new SerialCtl("Test1", "Test2");
System.out.println("Before:\n" + sc);
ByteArrayOutputStream buf =
new ByteArrayOutputStream();
try {
ObjectOutputStream o =
new ObjectOutputStream(buf);
o.writeObject(sc);
// Now get it back:
ObjectInputStream in =
new ObjectInputStream(
new ByteArrayInputStream(
buf.toByteArray()));
SerialCtl sc2 = (SerialCtl)in.readObject();
System.out.println("After:\n" + sc2);
} catch(Exception e) {
e.printStackTrace();
}
}
} ///:~