對於實現了Serializable接口的類,在對其對象進行序列化的時候,會自動將該對象的全部實例變量依次進行序列,特別是在某個實例變量應用到了其餘對象時,表現爲遞歸式的序列化機制。這種「一律而全」序列化方式每每不是咱們實際想要的,所以有必以自定義的方式來序列化(具體地講好比控制某些實例變量能夠序列化,增長一些處理邏輯進行序列化等等)。有如下幾種方式來實現自動以的序列化。java
使用transient關鍵字修飾實例變量。從語義上講transient是短暫的、瞬態的意思,所以不適合用序列化策略來存儲。在序列化的時候該實例變量不會被寫進字節序列,至關於會忽略掉該變量的序列化。這是最簡單方便的自定義序列化方式,在JDK源碼中也有不少地方用到了該關鍵字來修飾的實例變量。算法
使用transient關鍵字實現自定義序列化有幾件事須要瞭解:ide
a、transient只應該用來修飾成員變量,不該修飾類變量。雖然在語法上用transient修飾靜態變量不會報錯,可是這樣作沒有效果也沒有意義:類變量自己就是隸屬於類,不屬於任何一個對象。this
b、反序列化後,以前被transient修飾的變量的值被賦予系統設定的默認初值(同時注意:反序列化的過程不會調用對象的任何構造器)。加密
c、實現了Externalizable接口spa
package com.prac; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class TestSeri{ public static void main(String[] args) { String path = System.getProperty("user.dir")+"\\target.md"; Target target = new Target("hi"); try { //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(target); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path)); Target othetarget = (Target)ois.readObject(); Target.staticVar += " world!";//改變Target類的靜態變量 System.out.println("instVar = "+othetarget.instVar); //輸出了"hello world!",靜態變量不參與序列化 System.out.println("staticVar = "+othetarget.staticVar); //如下成員變量未參與序列化,反序列化後賦予系統設定的初始值 System.out.println("intValue = "+othetarget.intValue);//0 System.out.println("doubleValue = "+othetarget.doubleValue);//0.0 System.out.println("booValue = "+othetarget.booValue);//fasle System.out.println("stringValue = "+othetarget.stringValue);//null System.out.println("objValue = "+othetarget.objValue);//null } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } class Target implements Serializable{ public static String staticVar = "hello"; // public transient static String staticValue = "hello";//語法上能夠用transient修飾靜態變量,但無心義 public String instVar = ""; public transient double doubleValue = 10.0; public transient int intValue = 100; public transient boolean booValue = true; public transient String stringValue = "hello world"; public transient Object objValue = new Object(); public Target(){ System.out.println("invoke Target()"); } public Target(String instVar){ this.instVar = instVar; System.out.println("invoke Target(String instVar)"); } }
在類中定義以下三個特殊簽名的方法,能夠按照自定義的邏輯來實現自定義序列化。code
private void writeObject(ObjectOutputStream oos) throws IOException private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException private void readObjectNoData() throws ObjectStreamException
一個簡單的示例以下:對象
package com.prac; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class TestSeri{ public static void main(String[] args) { String path = System.getProperty("user.dir")+"\\target.md"; User user = new User("qcer","123456"); try { //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(user); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path)); User otheuser = (User)ois.readObject(); System.out.println("username = "+otheuser.username); System.out.println("password = "+otheuser.password); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } class User implements Serializable{ public String username = ""; public String password = ""; public User(){ } public User(String username,String password){ this.username = username; this.password = password; } private void writeObject(ObjectOutputStream oos) throws IOException{ oos.writeObject(username); oos.writeObject(encrypt(password)); } private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{ this.username = (String)ois.readObject(); this.password = dencrypt((String)ois.readObject()); } private String encrypt(String plaintext){ String ciphertext = ""; //...省略加密算法部分 return ciphertext; } private String dencrypt(String ciphertext){ String plaintext = ""; //...省略解密算法部分 return plaintext; } }
Externalizable實接口際上是繼承了Serializable接口blog
實現Externalizable接口的類中須要實現writeExternal(ObjectOutput out)和readExternal(ObjectInput in)兩個方法,同以前的同樣,能夠用out.writeXXX()和in.readXXX()的方式來自定義序列化和反序列化數據。繼承
一個示例以下:
package com.prac; import java.io.*; public class TestExseri{ public static void main(String[] args) { String path = System.getProperty("user.dir")+"\\book.md"; Book book = new Book("Thinking in Java",108.00); try { //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(book); //反序列化,會調用Book類的無參構造器 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path)); Book otherbook = (Book)ois.readObject(); System.out.println("name = "+otherbook.name);//Thinking in Java System.out.println("price = "+otherbook.price);//108.0 } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } class Book implements Externalizable{ public transient String name = ""; public double price = 0.0; public Book(){ System.out.println("invoke Book()"); } public Book(String name,double price) { this.name = name; this.price = price; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeDouble(price); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.name = (String)in.readObject(); this.price = in.readDouble(); } }
實際上,ObjectOutputStream實現了ObjectOutput接口,然後者繼承了DataOutput接口。