java序列化与反序列化
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
通俗来说,序列化是可以将类写入到磁盘中,即使Java虚拟机停止运行,该类还继续存在于磁盘中。在java程序再次运行后,可以继续读取出该类的内容。
Java序列化代码实现
注:可序列化的类必须在类定义的时候实现Serializable接口
Baby:
创建一个Baby对象
WriteObject(序列化):
创建一个baby对象
创建文件输出流
打开一个对象输出流
将baby对象写入
ReadObject(反序列化):
创建文件输入流
打开一个对象输入流
读取出baby对象
Baby
package com.company;import java.io.Serializable;public class Baby implements Serializable {private String name;private int age;public Baby(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Baby{" +"name='" + name + '\'' +", age=" + age +'}';}}
WriteObject
package com.company;import java.io.FileOutputStream;import java.io.ObjectOutputStream;class WriteObject {public static void main(String[] args) throws Exception {Baby baby = new Baby("Xiaowenwen", 23);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Baby.txt"));oos.writeObject(baby);oos.close();}}
ReadObject
package com.company;import java.io.FileInputStream;import java.io.ObjectInputStream;public class ReadObject {public static void main(String[] args) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Baby.txt"));Baby brady = (Baby) ois.readObject();System.out.println(brady);ois.close();}}
序列化多个对象
反序列化的顺序与序列化时的顺序一致
ReadManyObjects
package com.company;import java.io.FileInputStream;import java.io.ObjectInputStream;public class ReadManyObjects {public static void main(String[] args) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Babys.txt"));Baby baby1 = (Baby) ois.readObject();System.out.println(baby1);Baby baby2 = (Baby) ois.readObject();System.out.println(baby2);Baby baby3 = (Baby) ois.readObject();System.out.println(baby3);Baby baby4 = (Baby) ois.readObject();System.out.println(baby4);ois.close();}}
WriteManyObjects
package com.company;import java.io.FileOutputStream;import java.io.ObjectOutputStream;public class WriteManyObjects {public static void main(String[] args) throws Exception {Baby baby1 = new Baby("Xiaowenwen1", 21);Baby baby2 = new Baby("Xiaowenwen2", 22);Baby baby3 = new Baby("Xiaowenwen3", 23);Baby baby4 = new Baby("Xiaowenwen4", 24);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Babys.txt"));oos.writeObject(baby1);oos.writeObject(baby2);oos.writeObject(baby3);oos.writeObject(baby4);oos.close();}}
Baby{name='Xiaowenwen1', age=21}Baby{name='Xiaowenwen2', age=22}Baby{name='Xiaowenwen3', age=23}Baby{name='Xiaowenwen4', age=24}
序列化注意事项
一个对象只能序列化一次,第二次序列化该对象只能获得该对象的编号,即使对象在第一次序列化后改变了值,第二次序列化也不会改变。
WriteObjects:
package com.company;import java.io.FileOutputStream;import java.io.ObjectOutputStream;class WriteObjects {public static void main(String[] args) throws Exception {Baby baby = new Baby("Xiaowenwen", 23);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Baby2.txt"));oos.writeObject(baby);baby.setName("bigwenwen");oos.writeObject(baby);oos.close();}}
反序列化输出:
Baby{name='Xiaowenwen', age=23}
自定义序列化属性
transient关键字
transient:修饰的属性不会被序列化,在反序列化时,会将该值设为默认值
public class Baby implements Serializable {private String name;private transient int age;... ...}
序列化后反序列化输出:
Baby{name='Xiaowenwen', age=0}
重写writeObject方法与readObject方法
public class Baby implements Serializable {private String name;private transient int age;private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.name).reverse().toString());out.writeInt(age);}// private void readObject(ObjectInputStream ins) throws IOException, ClassNotFoundException {// 将读出的字符串反转恢复回来// this.name = ((StringBuffer) ins.readObject()).reverse().toString();// this.age = ins.readInt();// }... ...}
序列化后反序列化输出:
Baby{name='newnewoaiX', age=23}
writeReplace与readResolve
writeReplace:在序列化时,会先调用此方法,再调用writeObject方法。此方法可将任意对象代替目标序列化对象
readResolve:在反序列化时,会序列化后调用此方法。反序列化时替换反序列化出的对象,反序列化出来的对象会丢弃。
writeReplace:
public class Baby implements Serializable {private String name;private transient int age;private Object writeReplace() throws ObjectStreamException {ArrayList<Object> list = new ArrayList<>(2);list.add(this.name);list.add(this.age);return list;}... ...}
ReadObject:
public class ReadObject {public static void main(String[] args) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Baby.txt"));// Baby brady = (Baby) ois.readObject();ArrayList<Object> brady = (ArrayList<Object>) ois.readObject();ois.close();System.out.println(brady);}}
序列化后反序列化输出:
[Xiaowenwen, 23]
readResolve:
public class Baby implements Serializable {private String name;private transient int age;private Object readResolve() throws ObjectStreamException{return new Baby("xxxx", 111);}... ...}
public class ReadObject {public static void main(String[] args) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Baby.txt"));Baby brady = (Baby) ois.readObject();ois.close();System.out.println(brady);}}
序列化后反序列化输出:
Baby{name='xxxx', age=111}
Externalizable
通过实现Externalizable接口,必须实现writeExternal、readExternal方法。
必须提供pulic的无参构造器,因为在反序列化的时候需要反射创建对象。
| Serializable接口 | Externalizable接口 |
|---|---|
| 系统自动存储必要的信息 | 程序员决定存储哪些信息 |
| Java内建支持,易于实现,只需要实现该接口即可,无需任何代码支持 | 必须实现接口内的两个方法 |
| 性能略差 | 性能略好 |
Baby2:
public class Baby2 implements Externalizable {private String name;private int age;public Baby2() {}public Baby2(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Baby{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {StringBuffer reverse = new StringBuffer(name).reverse();out.writeObject(reverse.toString());out.writeInt(age);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {this.name = (String) in.readObject();System.out.println(name);this.age = in.readInt();}}
序列化反序列化输出:
newnewoaiXBaby2{name='newnewoaiX', age=23}
总结
所有需要网络传输的对象都需要实现序列化接口。
对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;
方法、类变量、被transient修饰的变量不会被序列化。
序列化对象的引用类型成员变量,也必须是可序列化的。
单例类序列化,需要重写readResolve()方法。
一个对象只能序列化一次,第二次序列化该对象只能获得该对象的编号,即使对象在第一次序列化后改变了值,第二次序列化也不会改变。




