对象序列化详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cd18333612683/article/details/81258808
前言

我们在做web项目的时候,在网络传输中,序列化是绕不过去的重要一环。今天就来总结一下序列化到底能为我们做些什么

概念

序列化机制:允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。其他程序一旦获得了这种二进制流(无论磁盘还是网络),都可以将这种二进制流恢复成原来的Java对象。

如何实现一个序列化的操作

1、实现Serializable接口
2、调用ObjectInputStream
3、调用ObjectOutputStream

定义一个person类:

public class Person implements Serializable {
    private static final long serialVersionUID = -5183762729469760432L;
    private String name;

    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;
    }

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

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

客户端使用ObjectOutSteam序列化对象成为一个二进制文件,使用ObjectInputstream反序列化二进制文件,成为java对象

public class WriteObject {
    public static void main(String[] args)  {
        SerializePerson();
        DeSerializePerson();
    }

    private static void SerializePerson(){
        try {
            ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("person"));
            Person person=new Person("vincent",26);
            oos.writeObject(person);
            System.out.println("序列化成功");
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void DeSerializePerson(){
        try {
            ObjectInputStream ois= new ObjectInputStream(new FileInputStream("person"));
            Person per=(Person) ois.readObject();
            System.out.println(per);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
问题

我们见过很多可序列化的类中都有一一个私有的属性:serialVersionUID ,作用是什么

 private static final long serialVersionUID = -5183762729469760432L;

我们做个小实验,先执行序列化一个对象,然后把上边这行代码注释掉,在进行反序列化操作,会报出下边这个错误

java.io.InvalidClassException: com.SerializableDemo.Person; local class incompatible: stream classdesc serialVersionUID = -5183762729469760432, local class serialVersionUID = -2791480941090440504
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.SerializableDemo.WriteObject.DeSerializePerson(WriteObject.java:26)
    at com.SerializableDemo.WriteObject.main(WriteObject.java:8)

serialVersionUID 作用:反序列化Java对象时,必须提供该对象的class文件,随着项目的升级,系统的class文件也会升级,就会出现两个class文件的兼容问题。Java序列化机制可以通过判定serialVersionUID 的值来判定两个class文件是否是一个版本,上边的报错就是由于也就是修改过后的class,和文件流中的class不兼容了,出于安全机制考虑,程序抛出了错误,并且拒绝载入。如果没有为指定的class配置serialVersionUID,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件有任何改动,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。

所以说为了在反序列化时确保序列化版本的兼容性,我们最好自己在类中加入private static final long serialVersionUID这个类变量。即使对象被序列化后,他所对应的类修改了,该对象依然能被正确的反序列化。

其他关于序列化的小知识
  1. 序列化不保存静态变量
  2. 要想父类对象也参与序列化操作,那么必须要让父类也实现Serializable接口
  3. Transient关键字,主要是控制变量是否能够被序列化。如果没有被序列化的成员变量反序列化后,会被设置成初始值,比如String -> null

猜你喜欢

转载自blog.csdn.net/cd18333612683/article/details/81258808