Serializable工作原理

173 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

  • 序列化:把对象转换成二进制数据,以便于持久化或网络传输

  • 反序列化:从网络/磁盘读取读取字节序列然后转化为对象或者数据结构

Serializable接口

/**
 * @see java.io.ObjectOutputStream
 * @see java.io.ObjectInputStream
 * @see java.io.ObjectOutput
 * @see java.io.ObjectInput
 * @see java.io.Externalizable
 * @since   1.1
 */
public interface Serializable {
}

Serializable是一个空接口,所有实现Serializable接口的对象都可以被ObjectOutputStream序列化,以及被ObjectInputStream反序列化

Serializable接口特点

  • 序列化类的属性没有实现 Serializable 那么在序列化就会报错
public class User {

    private String name;

    private int age;

    public static void main(String[] args) throws IOException {
        try (ObjectOutputStream outputStream =
                new ObjectOutputStream(new FileOutputStream("User.txt"))) {
            User user = new User();
            user.name = "zouwei";
            user.age = 22;
            outputStream.writeObject(user);
            outputStream.flush();
        }
    }
}

image-20210202113331606.png

  • 在序列化反序列化过程中,父类没有实现序列化接口,那么父类的属性将不会参与到序列化反序列化的过程中,父类需要提供无参构造函数来重新创建对象
public class Animal {

    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class Dog extends Animal implements Serializable {
    private static final long serialVersionUID = -8559877565455401808L;

    private String name;

    public static void main(String[] args) throws Exception {
        // 序列化
        serialObject();
        // 反序列化
        deserialObject();
    }

    private static void serialObject() throws IOException {
        try (ObjectOutputStream outputStream =
                new ObjectOutputStream(new FileOutputStream("Dog.txt"))) {
            Dog dog = new Dog();
            dog.name = "Black";
            dog.setAge(2);
            outputStream.writeObject(dog);
            outputStream.flush();
        }
    }

    private static void deserialObject() throws Exception {
        try (ObjectInputStream inputStream =
                new ObjectInputStream(new FileInputStream("Dog.txt"))) {
            Dog dog = (Dog) inputStream.readObject();
            System.out.println(dog.name + ":" + dog.getAge());
        }
    }
}
序列化前:
name:Black
age:2

反序列化后:
name:Black
age:0
  • 静态成员变量是不能被序列化的

序列化是针对对象属性的,而静态成员变量是属于类的
  • transient修饰的对象成员变量不参与序列化

  • 自定义序列化、反序列化方式。

    要想解决transient或者静态成员变量不能序列化或反序列化的问题,可以自定义序列化和反序列化

    public class Dog implements Serializable {
        private static final long serialVersionUID = -8559877565455401808L;
    
        private transient String value;
    
        private String name;
    
        private void writeObject(ObjectOutputStream out) throws IOException {
            // 先调用jvm默认序列化操作
            out.defaultWriteObject();
    
            out.writeObject(this.value);
        }
    
        private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
            // 使用jvm默认的反序列化操作
            input.defaultReadObject();
    
            this.value = (String) input.readObject();
        }
    
        public static void main(String[] args) throws Exception {
            // 序列化
            serialObject();
            // 反序列化
            deserialObject();
        }
    
        private static void serialObject() throws IOException {
            try (ObjectOutputStream outputStream =
                    new ObjectOutputStream(new FileOutputStream("Dog.txt"))) {
                Dog dog = new Dog();
                dog.name = "Black";
                dog.value = "transient";
                outputStream.writeObject(dog);
                outputStream.flush();
            }
        }
    
        private static void deserialObject() throws Exception {
            try (ObjectInputStream inputStream =
                    new ObjectInputStream(new FileInputStream("Dog.txt"))) {
                Dog dog = (Dog) inputStream.readObject();
                System.out.println("name:" + dog.name + "\nvalue:" + dog.value);
            }
        }
    }
    

serialVersionUID的作用

serialVersionUID是用于确保序列化与反序列化的兼容性问题,如果序列化和反序列化过程中这两个值不一样,那么将导致序列化失败。

Externalizable接口

实现Externalizable是另一种自定义序列化、反序列化的方式

public class Dog implements Externalizable {
    private static final long serialVersionUID = -8559877565455401808L;

    private transient String value;

    private String name;

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(this.name);
        out.writeObject(this.value);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.name = (String) in.readObject();
        this.value = (String) in.readObject();
    }
  
  
    public static void main(String[] args) throws Exception {
        // 序列化
        serialObject();
        // 反序列化
        deserialObject();
    }

    private static void serialObject() throws IOException {
        try (ObjectOutputStream outputStream =
                new ObjectOutputStream(new FileOutputStream("Dog.txt"))) {
            Dog dog = new Dog();
            dog.name = "Black";
            dog.value = "transient";
            outputStream.writeObject(dog);
            outputStream.flush();
        }
    }

    private static void deserialObject() throws Exception {
        try (ObjectInputStream inputStream =
                new ObjectInputStream(new FileInputStream("Dog.txt"))) {
            Dog dog = (Dog) inputStream.readObject();
            System.out.println("name:" + dog.name + "\nvalue:" + dog.value);
        }
    }
}