java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java序列化之serialVersionUID

Java序列化之serialVersionUID的用法解读

作者:红提口味趣多多

Java序列化之serialVersionUID:本文介绍了Java对象的序列化和反序列化过程,强调了serialVersionUID的作用,以及如何生成和使用它来保证版本兼容性,通过实例代码,展示了序列化和反序列化的具体操作

Java序列化之serialVersionUID

今天讲一讲Java对象中的serialVersionUID,先从序列化讲起。

什么是序列化

序列化,简单的说,就是将一个对象转化(编码)成可以传输的输出流(字节流)。而反序列化就是序列化的逆过程,将输入流转化(构建)成一个对象。

为什么要序列化

字节流可以用于网络传输和存储在磁盘,而对象需要转化成字节流才能在网络中传输和在磁盘上存储。

网络传输就好比打电话,声音是无法直接从电话的一端传到另一端,因此需要将声音转成电信号进行传播。

另一方面,Java对象是保存在JVM的堆内存中的,也就是说,如果JVM堆不存在了,那么对象也就跟着消失了,而序列化提供了可以把对象保存下来的方案。

serialVersionUID是个啥

说到序列化,serialVersionUID是个不得不谈的话题。

serialVersionUID 是 Java 为每个序列化类(实现java.io.Serializable接口的类)产生的版本标识, 可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。

如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。

怎么生成serialVersionUID

下载GenerateSerialVersionUID插件,就可以自动生产这个序列类的serialVersionUID了。

serialVersionUID是一成不变的吗

达咩! 

serialVersionUID 是 Java 为每个序列化类产生的版本标识!!

Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用。

如果Class文件的类名、方法名称发生改变,serialVersionUID就会改变。

如果Class文件没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID也不会变化的。

import java.io.Serializable;

public class Person implements Serializable {
    // 原本的serialVersionUID
    private static final long serialVersionUID = 904XXXXXXXXXX662L;

    private int age;
    private String name;
    private String address;

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

import java.io.Serializable;

public class Person implements Serializable {
    // 加上toSting函数的serialVersionUID
    private static final long serialVersionUID = 841XXXXXXXXXXXXX884L;

    private int age;
    private String name;
    private String address;

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

如果我手动改了serialVersionUID=11111111L会怎样?如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。

Exception in thread "main" java.io.InvalidClassException: SerializableStudy.Person; local class incompatible: stream classdesc serialVersionUID = 841XXXXXXXXXXXXX884, local class serialVersionUID = 11111111

序列化和反序列化

序列化要把对象写入输出流中,反序列化就是将输出流重新构建对象,二者为逆过程。当serialVersionUID改变时,一定要重新序列化,再进行反序列化。

话不多说,放代码。

以下是基于上面序列化类Person,做序列化和反序列化的演示:

public class SerialTest {

    public static void main(String[] args) throws IOException {
//        序列化
        Person p = new Person(0,"aaa","bbbbb");
//        指定文件生成输出流
        FileOutputStream fos = new FileOutputStream("person.txt");
//        将对象写出到指定的输出流
        ObjectOutputStream oos = new ObjectOutputStream(fos);
//        将指定的对象写入ObjectOutputStream。
        oos.writeObject(p);
//        刷新流
        oos.flush();
        oos.close();
    }
}
public class DeserialTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        反序列化
//        根据指定文件生产输入流
        FileInputStream fis = new FileInputStream("person.txt");
//        从指定的输入流中读回对象消息
        ObjectInputStream ois = new ObjectInputStream(fis);
//        从ObjectInputStream读取一个对象
        Person p = (Person) ois.readObject();
        ois.close();
        System.out.println(p.toString());
    }
}

输出的结果为:

注意:

将指定对象写入ObjectOutputStream时,存储针对对象本身而不是针对类,没有实现序列化的类不会参与序列化和反序列化!!

举个例子,如果Person中设置一个没有实现序列化的父类Home:

public class Home {
    private String home;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }
}
public class Person extends Home implements Serializable {
	......
}

在序列化和反序列化的过程中,即使定义了Person对象的home属性,由于Home中没有实现序列类,因此对象的home属性不会进行序列化处理。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文