Java 序列化Serializable

Java 序列化Serializable

————————————————
版权声明:本文为CSDN博主「ratelfu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weter_drop/article/details/84660173

一 、基本概念

1.1 概念

序列化:将对象的状态信息转换为可以存储或传输的形式的过程

反序列化:将字节序列恢复为对象的过程

1.2 基本用途

  • 把对象的字节序列永久存储在硬盘上,通常是一个文件
  • 在网络上传输对象的字节序列

二、JDK中的序列化API

FileOutputStream fileOutputStream = new FileOutputStream("person.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(person);

FileInputStream fileInputStream = new FileInputStream("person.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
person1 = (Person) objectInputStream.readObject();

Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的

在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

UID显式的生成方式:

  • private static final long serialVersionUID = 1L;
  • 根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段

没有显式地定义一个serialVersionUID变量时候,Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,如果Class文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID也不会变化的。

代码示例:

普通实体类 Person.java

import java.io.Serializable;

public class Person implements Serializable {//序列化必须继承Serializable接口
    private static final long serialVersionUID = 123l;
    private int id;
    private String name;

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

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

序列化和反序列化 SerialTest.java

import java.io.*;
public class SerialTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person(123, "zhan");
        System.out.println(person.toString());
        FileOutputStream fileOutputStream = new FileOutputStream("person.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(person);
        objectOutputStream.flush();
        objectOutputStream.close();

        Person person1;
        FileInputStream fileInputStream = new FileInputStream("person.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        person1 = (Person) objectInputStream.readObject();
        objectInputStream.close();
        System.out.println(person1.toString());
    }
}

三、 四种情形

  1. 假设Person被序列化后,从A传到B,A和B中都有同一个类,但是他们的serialVersionUID不一样。

    报错:

    Exception in thread "main" java.io.InvalidClassException: com.sf.code.serial.Person; local class incompatible: stream classdesc serialVersionUID = 1234567890, local class serialVersionUID = 123456780
    
  2. 假设AB中UID一样,但是A端增加一个字段,然后序列化,B端不变。

    结果:正常执行序列化,反序列化正常,但是A端增加的字段丢失(被B端忽略)

  3. 假设AB中UID一样,如果B端减少一个字段,A端不变。

    结果:序列化、反序列化正常,B端字段少于A端,A端多的字段丢失(被B端忽略)

  4. 假设AB中UID一样,如果B端增加一个字段,A端不变。

    结果:序列化、反序列化正常,B端新增字段被赋予默认值。

四、静态变量序列化

静态变量不会被序列化,因为序列化是保存对象的状态,而静态变量是类的状态。

五、父类的序列化

情境:一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象,然后反序列化后输出父类定义的某变量的数值,该变量数值与序列化时的数值不同。

解决:

  • 要想将父类对象也序列化,就需要让父类也实现Serializable 接口。

  • 如果父类不实现的话的,就 需要有默认的无参的构造函数。在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

六、Transient关键字

作用: 控制变量的序列化。在变量声明加上该关键字,可以阻止该变量被序列化。在反序列之后,transient变量的值被初始为默认值。int->0, 对象->null

不使用transient使得字段不被序列化的方法:将不要被序列化的字段抽取出来放到弗雷中,子类实现serializable接口,父类不实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值