转载

「OpenJdk-11 源码」Serializable

Serializable 是一种用来处理对象流的机制。对象流就是将运行时对象的状态进行流化,即转换成二进制。说白了,就是将正在运行中程序的对象的状态/内容转换成二进制进行传输,持久化的操作。

「OpenJdk-11 源码」Serializable

在 Java 中,序列化是在 jdk 1.1 时引入的特性。Serializable 的定义

public interface Serializable {
}
复制代码

只有这么个接口,一旦某类实现了该接口,那么该类就有了序列化的能力,但严格来说,不一定能序列化/反序列化成功,为撒呐?先来看个 demo

Quickly start

举个栗子:chestnut:,有一 Women 类,该类有三个属性,分别是 age , weightname ,我们来试试怎么序列/反序列化。

先来个 Women class

//Women.class
public class Women implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Integer age;
    private String name;
    private Float weight;

    public Women(String name, int age, float weight) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }
    
   '''
   setter/getter    
   '''
}
复制代码

序列化

public static void main(String[] args) throws IOException {
        Women women = new Women("xiao ju",18, 45.1f);
        File file = new File("xiaoju.txt");
        FileOutputStream output = new FileOutputStream(file);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(output);
        objectOutputStream.writeObject(women);
        output.close();
        objectOutputStream.close();
    }
复制代码

反序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {
        File file = new File("xiaoju.txt");
        FileInputStream input= new FileInputStream(file);
        ObjectInputStream objectInputStream = new ObjectInputStream(input);
        Women women = (Women) objectInputStream.readObject();
        input.close();
        objectInputStream.close();
    }
复制代码

这就是序列化/反序列化的过程。可以看到我们在 Woemen 类中,不仅实现了 Serializable 接口外,还加了一个 serialVersionUID ,当然了,如果我们不手动加,那么系统也会根据当前类的结构会初始化一个 UID ,它的作用是什么呢?

当进行序列化的时候, UID 会和当前对象的状态一同持续久化,在反序列化的时候,会检测当前类的 UID 是否和持久化的 UID 一致,如果一致,那么反序列化成功,否则失败。

如果我们没有指定 UID 的化,系统在初始化一个 UID 后,随着对象的持久化,如果我们这个时候改了该类的数据结构,那么这个类的 UID 就会发生改变(系统初始化的 UID 是根据类的数据结构算出来的),所以在反序列化的时候,就会检测到 UID 不一致,那么就会失败。所以,一般我们都会加上一个 UID

那问题来了,全部类都支持序列化不好嘛?

确实不好,不要你觉得,要我觉得,不是所有的类都想要序列化的,有些类是比较敏感的,比如用户类,卡号类... 对于序列化的对象的信息是很容易被破解的,不能保证其安全性。

问题又来了,我只想要序列化部分怎么办

两种方式:

  1. 在不想要被序列化的属性上添加 static 修饰符。前面我们说到,序列化/反序列化的都是程序正在运行中的对象的状态,而被 static 修饰的属性/方法其实是属于类的状态,不属于处于内存中的对象。
  2. 在不想要被序列化的属性上添加 transient 修饰, transient 不能修饰方法/类。一旦属性被 transient 修饰,那么该属性就不会被序列/反序列化。

序列化规则

  1. 类成员

    为了减少存储,传输空间,从提高效率,那么在进行序列化的时候,可以将不需要序列化的属性通过 statictransient 关键字修饰排除掉。

  2. 继承关系

    Serializable
    Serializable
    
  3. 引用关系

    如果一个类实现了序列化,并且引用了一个对象。该对象所属类实现了 Serializable 接口,那么会改对象也会被序列化,否则会抛出 java.io.NotSerializableExeception

Serializable 子接口 Externalizable

前面我们说到,实现了 Serializable 接口的类,可以将整个或部分对象的状态进行序列化/反序列化。那么如果我们想要更加定制化的序列化/反序列化一些东西的话,那么我们就需要用到 Externalizable ,比如:在序列化的时候我想保存一个时间,反序列化的时候我希望获取到这个时间,但是这个时间我不想放到类中。

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

复制代码

可以看到 Externalizable 除了实现了 Serializable 接口外,还新增了两个方法,这两个方法就是可以在序列化和反序列化的时候做一些定制的东西。还是拿 Women 来看。

//Women.class
public class Women implements Externalizable {
    private static final long serialVersionUID = 1L;
    
    private Integer age;
    private String name;
    private Float weight;

    public Women(String name, int age, float weight) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(new Date());
        out.writeObject(this.name);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println(in.readObject());
        this.name = (String) in.readObject();
    }
    
   '''
   setter/getter    
   '''
}
复制代码
原文  https://juejin.im/post/5d6d4107e51d453b5f1a04dc
正文到此结束
Loading...