美文网首页
十五、序列化

十五、序列化

作者: 大虾啊啊啊 | 来源:发表于2021-06-08 08:56 被阅读0次

1、概念

(1)序列化
将数据结构或者对象转换成二进制序列的过程
(2)反序列化
将序列化过程所生成的二进制序列转换成数据结构或者对象的过程
(3)持久化
将数据结构或者对象存储起来,如内存、磁盘。

2、序列化的作用

进程之间、客户端和服务器之间数据的传输。因为传输过程只能传输二进制序列。
例如如下图:


image.png

在客户端和服务器之间网络数据传输的时候,我们可以选择Serializable序列化或者广义的json,xml,protbuf ,在安卓的进程之间通信的时候,可以选择Parcelable 序列化。

3、序列化的类型

(1)Serializable
Java独有的序列化
(2) Parcelable
Android独有的序列
(3)广义的序列化
json,xml,protbuf .

4、Serializable序列化方式

对象通过实现Serializable接口或者Externalizable接口,Externalizable接口也是实现了Serializable接口

关于Serializable的几个问题

(1)serialVersionUID

serialVersionUID通常是对象的哈希码,主要用于对象的版本控制唯一标识。
序列化和反序列化的时候类中的serialVersionUID一定要一致。如果序列化时的serialVersionUID和反序列化时的serialVersionUID不一样,将会抛出异常。举个例子
我们定义了Student 类中的serialVersionUID = 1,然后将其序列化到磁盘在反序列化出来的时候,不修改serialVersionUID

package com.it.test.seriv;


import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1;
    private String name;
    private String sex;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
    public Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    public static void main(String[] args) {
        //序列化
        Student student =  new Student("小明","男");
        System.out.println("写入....");
        SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");

        //反序列化
        Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
        System.out.println("读出....");
        System.out.println(student1.toString());
    }
}

序列化和反序列化都成功

写入....
读出....
Student{name='小明', sex='男'}

我们将serialVersionUID 改成2,再反序列化出来 试试

    private static final long serialVersionUID = 2;
    public static void main(String[] args) {
        //序列化
//        Student student =  new Student("小明","男");
//        System.out.println("写入....");
//        SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");

        //反序列化
        Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
        System.out.println("读出....");
        System.out.println(student1.toString());
    }

提示serialVersionUID 不一致的异常。

java.io.InvalidClassException: com.it.test.seriv.Student; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    at com.it.test.seriv.SerializeableUtils.readObject(SerializeableUtils.java:71)
    at com.it.test.seriv.Student.main(Student.java:42)
读出....
Exception in thread "main" java.lang.NullPointerException
    at com.it.test.seriv.Student.main(Student.java:44)

因此我们知道serialVersionUID控制了对象的版本,序列化和反序列化的时候serialVersionUID一定要一致。
当然我们也可以不写serialVersionUID,这样JVM会自动给我们添加上serialVersionUID的值。当类没有任何改变的时候,我们直接序列化和反序列化是正常的,但是假如我们在序列化之后,删除或者添加了类的某个成员,JVM就会修改serialVersionUID的值,就会导致和序列化时候的serialVersionUID不一致。这样我们在反序列化的时候就还会报serialVersionUID不一致的错误。因此当我们需要序列化对象的时候,一般手动设置serialVersionUID的值。

(2)希望某个成员不被序列化

使用 transient 关键字修饰成员

   private transient String name;

(3)如果类中的一个成员没实现序列化接口,我们去序列化这个类的对象会怎么样?

如果序列化的对象没有包含这个成员,则正常,如果包含,则会报抛异常。
举例
Student中包含2个构造方法,其中User成员没有实现Serializable接口


    private  String name;
    private String sex;
    private User user;

 public Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
        this.user = user;
    }
    public Student(String name, String sex, User user) {
        this.name = name;
        this.sex = sex;
        this.user = user;
    }

我们先调用第一个构造方法创建对象然后序列化

 public static void main(String[] args) {
        //序列化
        Student student =  new Student("小明","男");
        System.out.println("写入....");
        SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");

        //反序列化
        Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
        System.out.println("读出....");
        System.out.println(student1.toString());
    }

没问题

写入....
读出....
Student{name='小明', sex='男'}

调用第二个构造方法

  public static void main(String[] args) {
        //序列化
        Student student =  new Student("小明","男",new User());
        System.out.println("写入....");
        SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");

        //反序列化
        Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
        System.out.println("读出....");
        System.out.println(student1.toString());
    }

直接抛出 java.io.NotSerializableException异常,我们只需将User实现Serializable接口即可

写入....
java.io.NotSerializableException: com.it.test.seriv.User
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at com.it.test.seriv.SerializeableUtils.saveObject(SerializeableUtils.java:42)
    at com.it.test.seriv.Student.main(Student.java:64)
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.it.test.seriv.User
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1577)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2287)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2211)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    at com.it.test.seriv.SerializeableUtils.readObject(SerializeableUtils.java:71)
    at com.it.test.seriv.Student.main(Student.java:67)
Caused by: java.io.NotSerializableException: com.it.test.seriv.User
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at com.it.test.seriv.SerializeableUtils.saveObject(SerializeableUtils.java:42)
    at com.it.test.seriv.Student.main(Student.java:64)
读出....
Exception in thread "main" java.lang.NullPointerException
    at com.it.test.seriv.Student.main(Student.java:69)

(4)当一个类实现了Serializable接口,父类没有实现Serializable接口,将子类序列化会怎么样?

  • 如果父类没有定义有参的构造方法,子类的构造方法中就不需要调用其构造函数,子类可以正常序列化。只会把子类序列化。
  • 如果父类带有有参的构造方法,子类的构造方法就需要调用父类的构造方法,这样相当于序列化子类的时候父类也要序列化,因此父类也要实现Serializable接口。

(5)反序列化成对象之后,会调用构造函数吗?

不会,反序列化生成对象是从二进制直接读出来的,然后再用Object 进行强转,不是原来的那个对象。

(6)序列化和反序列化后的对象关系?

是一个深拷贝,前后对象的引用地址不一样

5、Android中序列化方式parcelable

image.png

相关文章

网友评论

      本文标题:十五、序列化

      本文链接:https://www.haomeiwen.com/subject/eahceltx.html