Java對象序列化(Object Serialization)

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(); 
 } 
} 
} ///:~



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章