一、简介

定义:

序列化:序列化是将对象转换为字节流。

反序列化:范序列化是将字节流转换为对象。

序列化的作用有:

序列化可以将对象的字节序列持久化保存在内存、文件、数据库中。

在网络上传送对象的字节序列。

RMI(远程方法调用)

二、序列化和反序列化

Java通过对象输入输出流来实现序列化和反序列化:

序列化:java.io.ObjectOutputStream类的writeObject()方法可以实现序列化。

反序列化:java.io.ObjectInputStream类的readObject()方法用于实现反序列化。

package com.zhanzhuang.test01;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

public class SerializeDemo01 {
    enum Sex {
        MALE, FEMALE
    }

    static class Person implements Serializable {
        private static final long serialBersionUID = 1L;
        private String name = null;
        private Integer age = null;
        private Sex sex;

        public Person() {
            System.out.println("call Person()");
        }

        public Person(String name, Integer age, Sex sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
        }

    }

    /**
     * 序列化
     */
    private static void serialize(String filename) throws IOException {
        File f = new File(filename); // 定义保存路径
        OutputStream out = new FileOutputStream(f); // 文件输出流
        ObjectOutputStream oos = new ObjectOutputStream(out); // 对象输出流
        oos.writeObject(new Person("Jack", 30, Sex.MALE)); // 保存对象
        oos.close();
        out.close();
    }

    /**
     * 反序列化
     */
    private static void deserialize(String filename) throws IOException, ClassNotFoundException {
        File f = new File(filename); // 定义保存路径
        InputStream in = new FileInputStream(f); // 文件输入流
        ObjectInputStream ois = new ObjectInputStream(in); // 对象输入流
        Object obj = ois.readObject(); // 读取对象
        ois.close();
        in.close();
        System.out.println(obj);
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        final String filename = "d:/text.dat";
        serialize(filename);
        deserialize(filename);
//        File f = new File("d:/zhan.txt");
    }

}

输出:Person [name=Jack, age=30, sex=MALE]

三、Serializable接口

被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。

如果不是Enum、Array的类、如果需要序列化、必须实现java.io.Serializable接口,否则将抛出NotSerializableException异常。这是因为:在序列化操作过程中会对类型进行检查,如果不满足序列化类型要求,就会抛出异常。

下面做一个小尝试,将SerializeDemo01.java中的内部类Person改为如下实现,继续执行查看运行结果。

果然不出所料,抛出了NotSerializableException异常。

四、serialVersionUID

serialVersionUID这个字段,我们在Java世界中的无数个类中看到这个字段。

serialVersionUID有什么作用,如何使用serialVersionUID?

它是Java为每个序列化类产生的版本标识。它可以用来保证在反序列化时,发送方发送和接收方接受的是可兼容的对象。

如果接收方接收的类的serialVersionUID与发送方发送的serialVersionUID不一致,会抛出InvalidClassException。

注意:如果可序列化类没有声明serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认serialVersionUID值。尽管这样,还是建议在每一个序列化的类中声名serialVersionUID的值。

因为不同的jdk编译很可能会生成不同的serialVersionUID默认值,从而导致在反序列化时抛出InvalidClassException异常。

serialVersionUID字段必须是static final long类型的。

我们来举一个例子:

①在版本信息为1L的时候进行序列化后执行反序列化(序列化为1L,反序列化为1L。我猜不会报错):

执行后输出:Person [name=Jack, age=30, sex=MALE]。

②在开发过程中修改了内容,所以要修改序列化的版本信息,但是没有进行序列化,直接进行反序列化(序列化为1L,反序列化为2L。我猜会报错):

会抛出异常:

 

 由于在②步骤中修改了内容,导致与版本不兼容,所以要修改序列化版本号

综上所述:

我们大概可以清楚:serialVersionUID 用于控制序列化版本是否兼容。若我们认为修改的可序列化类是向后兼容的,则不修改 serialVersionUID。

 五、默认序列化机制

如果仅仅只是让某个类实现Serializable接口,而没有其他任何处理的话,那么就是使用默认序列化机制。

使用默认机制,在序列化对象时,不仅会序列化当前对象本身,还会对其父类的字段以及该对象引用的其他对象也进行序列化。同样的,这些其他对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会比较复杂,开销也比较大。

注意:这里的父类和引用对象既然要进行序列化,那么他们当然也要满足序列化要求:被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。

六、非默认序列化机制

在现实应用中,有些时候不能用默认序列化机制。比如,希望在序列化过程中忽略掉敏感数据,或者简化序列化过程。下面将介绍若干影响序列化的方法。

transient关键字

当某个字段被声明为transient后,默认序列化机制就会忽略该字段。

我们将SerializeDemo01示例中的内部类Person的age字段声明为transient,如下所示:

public class SerializeDemo02 {
    static class Person implements Serializable {
        transient private Integer age = null;
        // 其他内容略
    }
    // 其他内容略
}

输出:Person [name=Jack, age=null, sex=MALE]

从输出的结果可以看出,age字段没有被序列化。

 

 

posted on 2018-06-05 17:00  帅过驴的袋鼠  阅读(129)  评论(0编辑  收藏  举报