序列化是什么

序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。

  • 序列化:将对象写入到IO流中
  • 反序列化:从IO流中恢复对象
  • 使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现Serializeable接口。

    序列化 ID 问题

    情境:两个客户端 A 和 B 试图通过网络传递对象数据,A 端将对象 C 序列化为二进制数据再传给 B,B 反序列化得到 C。
    问题:C 对象的全类路径假设为 com.inout.Test,在 A 和 B 端都有这么一个类文件,功能代码完全一致。也都实现了 Serializable 接口,但是反序列化时总是提示不成功
    解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。清单 1 中,虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化。

    序列化实现

    1、Serializable

    1.1 普通序列化
    Serializable接口是一个标记接口,不用实现任何方法。一旦实现了此接口,该类的对象就是可序列化的。
  1. 序列化步骤:
  • 步骤一:创建一个ObjectOutputStream输出流;
  • 步骤二:调用ObjectOutputStream对象的writeObject输出可序列化对象。 ```java public class Person implements Serializable { private String name; private int age; //我不提供无参构造器 public Person(String name, int age) {

    1. this.name = name;
    2. this.age = age;

    }

    @Override public String toString() {

    1. return "Person{" +
    2. "name='" + name + '\'' +
    3. ", age=" + age +
    4. '}';

    } }

public class WriteObject { public static void main(String[] args) { try (//创建一个ObjectOutputStream输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“object.txt”))) { //将对象序列化到文件s Person person = new Person(“9龙”, 23); oos.writeObject(person); } catch (Exception e) { e.printStackTrace(); } } }

  1. 1. **反序列化步骤:**
  2. - **步骤一:创建一个ObjectInputStream输入流;**
  3. - **步骤二:调用ObjectInputStream对象的readObject()得到序列化的对象。**我们将上面序列化到person.txtperson对象反序列化回来
  4. ```java
  5. public class Person implements Serializable {
  6. private String name;
  7. private int age;
  8. //我不提供无参构造器
  9. public Person(String name, int age) {
  10. System.out.println("反序列化,你调用我了吗?");
  11. this.name = name;
  12. this.age = age;
  13. }
  14. @Override
  15. public String toString() {
  16. return "Person{" +
  17. "name='" + name + '\'' +
  18. ", age=" + age +
  19. '}';
  20. }
  21. }
  22. public class ReadObject {
  23. public static void main(String[] args) {
  24. try (//创建一个ObjectInputStream输入流
  25. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"))) {
  26. Person brady = (Person) ois.readObject();
  27. System.out.println(brady);
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. //输出结果
  34. //Person{name='9龙', age=23}

输出告诉我们,反序列化并不会调用构造方法。反序列的对象是由JVM自己生成的对象,不通过构造方法生成。

1.2 成员是引用的序列化

如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的;否则,会导致此类不能序列化。

public class Person implements Serializable{
    //省略相关属性与方法
}
public class Teacher implements Serializable {

    private String name;
    private Person person;
}

2、Externalizable:强制自定义序列化

通过实现Externalizable接口,必须实现writeExternal、readExternal方法。

public interface Externalizable extends java.io.Serializable {
     void writeExternal(ObjectOutput out) throws IOException;
     void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
public class ExPerson implements Externalizable {

    private String name;
    private int age;
    //注意,必须加上pulic 无参构造器
    public ExPerson() {
    }

    public ExPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        //将name反转后写入二进制流
        StringBuffer reverse = new StringBuffer(name).reverse();
        System.out.println(reverse.toString());
        out.writeObject(reverse);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        //将读取的字符串反转后赋值给name实例变量
        this.name = ((StringBuffer) in.readObject()).reverse().toString();
        System.out.println(name);
        this.age = in.readInt();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ExPerson.txt"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ExPerson.txt"))) {
            oos.writeObject(new ExPerson("brady", 23));
            ExPerson ep = (ExPerson) ois.readObject();
            System.out.println(ep);
        }
    }
}
//输出结果
//ydarb
//brady
//ExPerson{name='brady', age=23}

3、两种序列化对比

实现Serializable接口 实现Externalizable接口
系统自动存储必要的信息 程序员决定存储哪些信息
Java内建支持,易于实现,只需要实现该接口即可,无需任何代码支持 必须实现接口内的两个方法
性能略差 性能略好

虽然Externalizable接口带来了一定的性能提升,但变成复杂度也提高了,所以一般通过实现Serializable接口进行序列化。