1. 程式人生 > >關於Java序列化的一些高階用法

關於Java序列化的一些高階用法

該說的都在註釋中說完了。直接給程式吧。

package test.javaPuzzler.p5;

import java.io.*;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream.PutField;

// 轉載請註明來自http://blog.csdn.net/sunxing007
// 一個類實現Serializable來表明自己可以被序列化;
// 有一點需要特別注意的是:
// 如果子類實現了Serializable,而父類沒有,則父類不會被序列化;
public class SerializableObject implements Serializable {

	// 生成的序列化版本號會因為編譯環境,宣告的類名,成員名稱和數量的變化而不同;
	// 也就是說這個版本號一定程度上記錄著類的定義性的資訊,如果類的定義變化了,最好重新生成版本號;
	// 如果新的程式碼使用了舊的版本號,則在反序列化的時候,可以相容讀取舊類的位元組碼而不會報錯;
	private static final long serialVersionUID = 9038542591452547920L;

	public String name;
	public String password;
	// 如果你不希望某個非靜態成員被序列化,可以用transient來修飾它;
	public transient int age;
	// 靜態成員不會被序列化,因為序列化儲存的是例項的狀態資訊,而靜態成員是類的狀態資訊;
	public static int version = 1;

	public SerializableObject(String name, String password) {
		this.name = name;
		this.password = password;
	}

	// 每個類可以寫一個writeObject方法,這個方法將會負責該類自身的序列化過程;
	// 比如對於敏感資訊如password,可以加密之後再序列化;
	// 這個過程需要用到PutField,它可以指定哪些域會被序列化,怎麼序列化(比如加密);
	// 如果沒有定義這個方法,將會呼叫ObjectOutputStream 的 defaultWriteObject;

	// 你可以註釋掉readObject方法,然後執行測試用例來測試密碼是否被加密;
	private void writeObject(ObjectOutputStream out) throws IOException {
		PutField putFields = out.putFields();
		putFields.put("name", name);
		// 模擬加密密碼
		putFields.put("password", "thePassword:" + password);
		out.writeFields();
	}

	// 每個類可以寫一個readObject方法,該方法負責該類自身的反序列化過程;
	// 比如對序列化時加密後的密碼解密;
	// 這個過程需要用到GetField,他可以具體地讀取每個域;或執行解密動作等等;
	// 如果沒有定義這個方法,將會呼叫ObjectInputStream 的 defaultReadObject;
	private void readObject(ObjectInputStream in)
			throws ClassNotFoundException, IOException {
		GetField readFields = in.readFields();
		// 讀取到成員的值之後,直接賦給該域,即完成該域的反序列化;
		name = (String) readFields.get("name", "defaultName");
		// 模擬解密密碼
		String encPassword = (String) readFields.get("password",
				"thePassword:defaultValue");
		password = encPassword.split(":")[1];
	}

	// 序列化
	// 主要用到ObjectOutputStream;
	public void save() throws IOException {
		FileOutputStream fout = new FileOutputStream("e:\\obj");
		ObjectOutputStream oout = new ObjectOutputStream(fout);
		oout.writeObject(this);
		oout.close();
		fout.close();
	}

	// 反序列化
	// 主要用到ObjectInputStream
	public static SerializableObject load() throws IOException,
			ClassNotFoundException {
		FileInputStream fin = new FileInputStream("e:\\obj");
		ObjectInputStream oin = new ObjectInputStream(fin);
		Object o = oin.readObject();
		return (SerializableObject) o;

	}

	@Override
	public String toString() {
		return "name: " + name + ", password: " + password;
	}

	// 測試用例
	public static void main(String[] args) throws IOException,
			ClassNotFoundException {
		SerializableObject so = new SerializableObject(
				"http://blog.csdn.net/sunxing007", "123456");
		so.save();
		System.out.println(so);
		System.out.println(SerializableObject.load());
	}

}

序列化會對單例模式不利, 因為可以通過反序列化而破壞單例. 這個時候就要請出readResolve這個方法了. 比如下面的程式:

public class Dog extends Exception {
	//private static final long serialVersionUID = -7156412195888553079L;
	public static final Dog INSTANCE = new Dog();
	private Dog() { }
	public String toString() {
		return "Woof";
	}
	// 通過readResolve, 保證反序列化的時候能完全自主地處理返回物件.
	private Object readResolve(){
            return INSTANCE;
        }
	public static void main(String[] args) throws IOException, ClassNotFoundException{
		Dog d = Dog.INSTANCE;
		ByteArrayOutputStream bro = new ByteArrayOutputStream();
		ObjectOutputStream oout = new ObjectOutputStream(bro);
		oout.writeObject(d);
		ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bro.toByteArray()));
		Dog d1 = (Dog)oin.readObject();
		System.out.println(d1==d);
	}
}


[轉載請註明來自http://blog.csdn.net/sunxing007]

相關推薦

no