序列化 — Java序列化

一.序列化定义

Wike中对序列化的定义如下:

In computing, serialization (or serialisation) is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a network connection link) and reconstructed later (possibly in a different computer environment).

序列化是将数据结构或者对象状态转化为另一种可以存储或者传输的格式。

二.Java序列化

在Java世界中一切皆对象Object,然而Object是Java中定义的数据格式,是语言层面表现出来的编程格式。然而Object对象无法直接存储在存储器中,也无法直接通过网络进行传递。能经过存储和传递的必然是以字节或者字符的形式表现出来。

为此Java从jdk 1.1便开始提供了序列化机制。对于Java程序员而言,Serializable一定是不陌生的,它是Java序列化的起点。本文将从以下几点说明Java序列化:

1. Serializable接口

在Java中,需要序列化的对象都需要实现Serializable接口。该接口只是一个纯粹的标记接口,其中未定义任何需要实现的接口方法。Java在序列化时检查,是否实现Serializable接口,如果未实现,在序列化时,将会抛出NotSerializableException异常。

在序列化的对象中一般需要有不重复的serialVersionUID,它用来标识序列化类的版本号。在反序列化时,需要验证反序列化对象的serialVersionUID与其是否一致。如果不一致则将会抛出InvalidClassException。

一个需要序列化的类,可以明确指定serialVersionUID。该字段是long型字段,需要是static和final。如果serialVersionUID没有明确指出,在序列化运行时,将会根据这个类的各个方面信息计算出一个默认的serialVersionUID值。

如下Person类可以被序列化:

public class Person implements Serializable {

    private static final long serialVersionUID = 914170721026336909L;

    private String name;
    private int 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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.序列化对象的方式

Java序列化对象提供了ObjectOutputStream用于序列化对象,将对象序列化的字节。提供了ObjectInputStream从二进制字节中反序列化生成对象。

具体使用方式也非常简单:

static void simpleSerialization() throws IOException, ClassNotFoundException {
    Person person = new Person();
    person.setName("huaijin");
    person.setAge(30);
    System.out.println("Serialization, " + person);

    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.bin"));
    out.writeObject(person);
    
    ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.bin"));
    Person desPerson = (Person) in.readObject();
    System.out.println("Deserialization, " + desPerson);
}

为了能够在序列化时,能够做一些特定处理,制定针对性的序列化,Java提供了

 private void writeObject(java.ioObjectOutputStream out) throws IOException;

 private void readObject(java.io.ObjectInputStream in) throws IOException,
    ClassNotFoundException;

writeObject和readObject方法可供序列化类实现,定制在序列化过程中自身特殊的方式。如下:

 private void writeObject(ObjectOutputStream out) throws IOException {
     out.writeUTF(this.name);
     out.writeInt(this.age);
 }
 
private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
    this.name = in.readUTF();
    this.age = in.readInt();
}

可以根据需求,为特殊的场景定制对象相应的序列化方式。比如,可以可以对对象的某个字段进行特殊处理后再进行序列化,也可以忽略某个字段不进行序列化,对安全字段进行加密处理等等。

在整个序列化过程中,会写入对象的类信息、类签名、所有的非transient和非static域。对于多次引用同一个对象,采用共享的序列化机制。


3. Java序列化弊端

虽然Java序列化是JDK提供的基础能力,但是它并不是很优秀:

  1. Java的序列化性能不足
  2. 序列化后的字节体积较大

这两点对于网络应用而言,非常致命。序列化带来的应用延迟和序列化的数据体积较大造成网络传输延迟,内存消耗较高等等,与互联网应用要求的低延迟,高响应相悖。

参考

Java 序列化的高级认识
Java Object Serialization Specification

posted @ 2020-03-17 16:37  怀瑾握瑜XI  阅读(138)  评论(0编辑  收藏