關於Java序列化的一些高階用法
阿新 • • 發佈:2019-01-20
該說的都在註釋中說完了。直接給程式吧。
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]