1.概念
序列化:將對象轉化爲字節序列的過程html
反序列化:將字節序列轉化爲對象的過程java
用途:網絡
A:將對象轉化爲字節序列保存在硬盤上,如文件中,如文本中的例子就是將person對象序列化成字節序列,存在person.txt文件中app
B:網絡傳輸中通常都是以對象序列化的形式存在,在網絡的發送/接收兩端進行對象的序列化/反序列化ide
輸入/輸出的使用:函數
通常都是針對內存而言,內存--硬盤--》outputStream 從硬盤--內存--》inputStream測試
具體使用要根據場景來肯定ui
2.Serializable-example
2.1 無顯式的指定UID,採用編譯系統自動生成的
Object:this
package com.java.baseinfo.knowledge.code.serializable; import java.io.Serializable; public class Person implements Serializable { private int age; private String name; private String sex; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { final StringBuffer sb = new StringBuffer("Person{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append('}'); return sb.toString(); } }
object uidurl
package com.java.baseinfo.knowledge.code.serializable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class TestObjSerializeAndDeserialize { public static void main(String[] args) { // 序列化 SerializablePerson(); // 反序列化 //Deserialization(); } private static void SerializablePerson() { Person person = new Person(); person.setAge(10); person.setName("測試"); person.setSex("女"); String path = "src/test/resources/person.txt"; try { FileOutputStream fileOutputStream = new FileOutputStream(new File(path)); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(person); System.out.println("序列化成功"); fileOutputStream.close(); objectOutputStream.close(); } catch (FileNotFoundException e) { System.out.printf("FileNotFoundException====>" + e); } catch (IOException e) { System.out.printf("IOException====>" + e); } } private static Person Deserialization() { try { FileInputStream fileInputStream = new FileInputStream(new File("src/test/resources/person.txt")); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Person person = (Person) objectInputStream.readObject(); System.out.printf("Deserialization.person===>"+person); } catch (FileNotFoundException e) { System.out.printf("FileNotFoundException====>" + e); } catch (IOException e) { System.out.printf("IOException====>" + e); } catch (ClassNotFoundException e) { System.out.printf("ClassNotFoundException====>" + e); } return null; } }
先序列化,才進行反序列化
Deserialization.person===>Person{age=10, name='測試', sex='女'}
給person增長字段,再用原來的序列化後的結果,進行反序列化;
package com.java.baseinfo.knowledge.code.serializable; import java.io.Serializable;
public class Person implements Serializable { private int age; private String name; private String sex; // 增長字段測試序列化 private String addText; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddText() { return addText; } public void setAddText(String addText) { this.addText = addText; } @Override public String toString() { final StringBuffer sb = new StringBuffer("Person{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append(", addText='").append(addText).append('\''); sb.append('}'); return sb.toString(); } }
運行結果
IOException====>java.io.InvalidClassException: com.java.baseinfo.knowledge.code.serializable.Person; local class incompatible: stream classdesc serialVersionUID = 6964452789008335213, local class serialVersionUID = -3534890433624150186 Process finished with exit code 0
緣由是:以前代碼中未顯示的指定UID,當Object中新增字段時,編譯器又新生成一個UID,因而出現序列化版本不一致的問題;
2.2 顯式指定UID
package com.java.baseinfo.knowledge.code.serializable; import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = -3534890433624150186L; private int age; private String name; private String sex; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { final StringBuffer sb = new StringBuffer("Person{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append('}'); return sb.toString(); } }
運行上述main函數, 先進行序列化,而後再對object增長屬性,再序列化的結果,進行反序列化
package com.java.baseinfo.knowledge.code.serializable; import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = -3534890433624150186L; private int age; private String name; private String sex; // 增長字段測試序列化 private String addText; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddText() { return addText; } public void setAddText(String addText) { this.addText = addText; } @Override public String toString() { final StringBuffer sb = new StringBuffer("Person{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append(", addText='").append(addText).append('\''); sb.append('}'); return sb.toString(); } }
反序列化結果,新增的字段爲空值
Deserialization.person===>Person{age=10, name='測試', sex='女', addText='null'} Process finished with exit code 0
顯式的指定UID,目前經常使用到的場景是:當object增長的字段時候,不但願反序列化出現異常,即但願類的不一樣版本對序列化兼容;
等用到其餘場景的時候,再更新。。
3. Externalizable-example
import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class PersonExternal implements Externalizable { private int age; private String name; private String sex; @Override public void writeExternal(ObjectOutput out) throws IOException {
//未實現 } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
//未實現 } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { final StringBuffer sb = new StringBuffer("PersonExternal{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append('}'); return sb.toString(); }
先序列化再反序列化
package com.java.baseinfo.knowledge.code.serializable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class TetsExternalizable { public static void main(String[] args) { //ExternalizableEnCode(); ExternalizableDecode(); } private static void ExternalizableEnCode() { PersonExternal personExternal = new PersonExternal(); personExternal.setName("測試"); personExternal.setAge(18); personExternal.setSex("女"); File file = new File("src/test/resources/personExternal.txt"); try { FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream); outputStream.writeObject(personExternal); System.out.printf("ExternalizableEnCode===>序列化完成"); } catch (FileNotFoundException e) { System.out.printf("ExternalizableEnCode.FileNotFoundException===>" + e); } catch (IOException e) { System.out.printf("ExternalizableEnCode.IOException===>" + e); } } private static void ExternalizableDecode() { try { File file = new File("src/test/resources/personExternal.txt"); FileInputStream fileInputStream = new FileInputStream(file); ObjectInputStream inputStream = new ObjectInputStream(fileInputStream); PersonExternal personExternal = (PersonExternal) inputStream.readObject(); System.out.println("ExternalizableDecode.personExternal====>" + personExternal); } catch (FileNotFoundException e) { System.out.printf("ExternalizableDecode.FileNotFoundException===>" + e); } catch (IOException e) { System.out.printf("ExternalizableDecode.IOException===>" + e); } catch (ClassNotFoundException e) { System.out.printf("ExternalizableDecode.ClassNotFoundException===>" + e); } } }
運行結果
ExternalizableDecode.personExternal====>PersonExternal{age=0, name='null', sex='null'}
此時發現,當時序列化的對象值並未持久化,是由於咱們沒有重寫Externalizable的writeExternal和readExternal方法
重寫externalizable方法
package com.java.baseinfo.knowledge.code.serializable; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class PersonExternal implements Externalizable { private int age; private String name; private String sex; @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(age); out.writeObject(name); out.writeObject(sex); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { age = in.readInt(); name = (String) in.readObject(); sex = (String) in.readObject(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { final StringBuffer sb = new StringBuffer("PersonExternal{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append('}'); return sb.toString(); } }
運行結果
ExternalizableEnCode===>序列化完成ExternalizableDecode.personExternal====>PersonExternal{age=18, name='測試', sex='女'}
結論:
Externalizable反序列化的時候,會調用被序列化的無參構造函數去建立一個新的對象,而後再將被保存的對象的字段分別填充到新的對象中;因此使用Externalizable時候,除了重寫write/read方法外,所須要序列化的對象還須要提供一個無參的構造函數;若沒有無參構造函數,則會拋異常:java.io.InvalidClassException
eg:
package com.java.baseinfo.knowledge.code.serializable; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class PersonExternal implements Externalizable { private int age; private String name; private String sex; //構造函數 public PersonExternal(int age, String name, String sex) { this.age = age; this.name = name; this.sex = sex; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(age); out.writeObject(name); out.writeObject(sex); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { age = in.readInt(); name = (String) in.readObject(); sex = (String) in.readObject(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { final StringBuffer sb = new StringBuffer("PersonExternal{"); sb.append("age=").append(age); sb.append(", name='").append(name).append('\''); sb.append(", sex='").append(sex).append('\''); sb.append('}'); return sb.toString(); } }
test main
package com.java.baseinfo.knowledge.code.serializable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class TetsExternalizable { public static void main(String[] args) { ExternalizableEnCode(); ExternalizableDecode(); } private static void ExternalizableEnCode() { PersonExternal personExternal = new PersonExternal(18,"測試","女"); /* personExternal.setName("測試"); personExternal.setAge(18); personExternal.setSex("女");*/ File file = new File("src/test/resources/personExternal.txt"); try { FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream); outputStream.writeObject(personExternal); System.out.printf("ExternalizableEnCode===>序列化完成"); } catch (FileNotFoundException e) { System.out.printf("ExternalizableEnCode.FileNotFoundException===>" + e); } catch (IOException e) { System.out.printf("ExternalizableEnCode.IOException===>" + e); } } private static void ExternalizableDecode() { try { File file = new File("src/test/resources/personExternal.txt"); FileInputStream fileInputStream = new FileInputStream(file); ObjectInputStream inputStream = new ObjectInputStream(fileInputStream); PersonExternal personExternal = (PersonExternal) inputStream.readObject(); System.out.println("ExternalizableDecode.personExternal====>" + personExternal); } catch (FileNotFoundException e) { System.out.printf("ExternalizableDecode.FileNotFoundException===>" + e); } catch (IOException e) { System.out.printf("ExternalizableDecode.IOException===>" + e); } catch (ClassNotFoundException e) { System.out.printf("ExternalizableDecode.ClassNotFoundException===>" + e); } } }
運行結果
ExternalizableEnCode===>序列化完成ExternalizableDecode.IOException===>java.io.InvalidClassException: com.java.baseinfo.knowledge.code.serializable.PersonExternal; no valid constructor
4.transient 關鍵字
若是對象的屬性被賦予transient屬性,則該對象在序列化的時候,當前字段不會被序列化;那麼再反序列化的時候,該字段會被賦予默認值,如int類型會是0,String爲null
eg
public class Person implements Serializable { private static final long serialVersionUID = -3534890433624150186L; private transient int age; private transient String name; private String sex; // 增長字段測試序列化 private String addText;
運行2中的序列化代碼,則結果爲
序列化成功 反序列化Deserialization.person===>Person{age=0, name='null', sex='女', addText='addText'} Process finished with exit code 0