對象序列化的目標是將對象保存到磁盤中,或容許網絡中直接傳輸對象。對象序列化機制容許把內存中的對象轉換成平臺無關的二進制流,從而容許這種二進制流持久地保存在磁盤上,經過網絡將這種二進制流傳輸到另外一個網絡節點,其它程序一旦得到這種二進制流,均可以將這種二進制流恢復爲原來的Java對象。java
對象的序列化指將一個Java對象寫入IO流中,與此對應,對象的反序列化指從IO流中恢復該Java對象。
若是須要讓對象支持序列化機制,則必須讓它的類是可序列化的。爲了讓某個類是可序列化的,該類必須實現以下兩個接口之一。算法
一旦某個類實現了Serializable接口,該類的對象就是可序列化的,程序能夠經過以下兩個步驟來序列化該對象。網絡
public class Person implements Serializable { private static final long serialVersionUID = 3232557182439996130L; private String name; private int age; public Person(String name, int age) { System.out.println("有參構造器"); this.name=name; this.age=age; } public static void main(String[] args) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"))) { Person per = new Person("sunqiang", 30); oos.writeObject(per); } catch (IOException ex) { ex.printStackTrace(); } } }
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"))) { Person person = new Person("sun", 500); Teacher t1 = new Teacher("li", person); oos.writeObject(t1); t1.setName("改變姓名"); oos.writeObject(t1); } catch (IOException ex) { ex.printStackTrace(); }
兩次將t1對象序列化,但在第二次序列化前改變了t1變量的值,由於t1已經被序列化過一次,再次調用writeObject()不會將該對象寫入,下面讀取對象,能夠看到兩次讀取的對象name屬性相同。ide
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher.txt"))) { Teacher t1 = (Teacher) ois.readObject(); Teacher t2 = (Teacher) ois.readObject(); System.out.println("t1:name:" + t1.getName()); System.out.println("t2:name:" + t2.getName()); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException cnf) { cnf.printStackTrace(); }
經過在實例變量前使用transient關鍵字,能夠指定Java序列化時無須理會該實例變量。this
使用transient修飾的變量被隔離在序列化機制以外,這樣致使在反序列化恢復Java對象是沒法獲取該實例變量的值。Java還提供了一種自定義序列化機制,經過這種機制可讓程序控制如何序列化各實例變量。
有一個Person類:code
public class Person implements Serializable { private static final long serialVersionUID = -4271096500079406727L; private String name; private int age; //省略getter,setter方法 }
爲序列化類提供以下方法:對象
private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; private void readObjectNoData() throws ObjectStreamException;
下面是一個例子接口
private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(new StringBuffer(name).reverse()); out.writeInt(age); } private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException { this.name=((StringBuffer)in.readObject()).reverse().toString(); this.age=in.readInt(); }
爲序列化類提供以下方法:內存
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
writeReplace由序列化機制調用,只要該方法存在,Java在序列化機制運行保證在序列化某個對象以前,先調用該對象的writeReplace()方法。get
private Object writeReplace(){ ArrayList<Object> list=new ArrayList<>(); list.add(name); list.add(age); return list; } public static void main(String[] args) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("replace.txt")); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("replace.txt"))){ Person person=new Person("sun",550); oos.writeObject(person); ArrayList list=(ArrayList)ois.readObject(); System.out.println(list); } catch (Exception ex) { ex.printStackTrace(); } }
爲序列化類提供以下方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
要實現該接口裏的兩個方法:
@Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(new StringBuffer(name).reverse()); out.writeInt(age); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.name = ((StringBuffer) in.readObject()).reverse().toString(); this.age = in.readInt(); }
須要指出的是當使用Externalizable機制反序列化時,程序會先使用public的無參構造器建立實例,而後才執行readExternal()方法進行反序列化,所以實現Externalizable的序列化類必須提供public的無參構造器。
關於對象序列化,還要注意一下幾點: