Java 序列化与反序列化

Java 序列化与反序列化


1 序列化与反序列化的概念

  • Java 序列化是指:将对象转化成一个字节序列(二进制数据)的过程。

  • 将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化。

  • Java 反序列化是指:将一个对象的字节序列恢复成 Java 对象的过程。

  • 一个平台中序列化的对象,可以在另一个平台中进行反序列化,因为这个过程是在 JVM 中独立完成的,可以依赖于 Java 的可移植性。


2 核心类与关键字总览

  • ObjectOutputStream:IO 类,包含序列化对象的方法,writeObject()

  • ObjectInputStream:IO 类,包含反序列化对象的方法,readObject()

  • 上面两个 IO 流类是高层次的数据库,需要借助文件流进行序列化与反序列化操作。

  • Serializable ,接口,是一个标志性接口,标识可以在 JVM 中进行序列化,JVM 会为该类自动生成一个序列化版本号。参与序列化与反序列化的类必须实现 Serializable 接口。

  • serialVersionUID,类属性,序列化版本号,用于给 JVM 区别同名类,没有提供版本号,JVM会默认提供序列化版本号。

  • transient,关键字,当序列化时,不希望某些属性参与,则可以使用这个关键字标注该属性。


3 序列化与反序列化的过程

  • 内存中的数据信息被拆分成一小块一小块的部分,为每个小块设置编号,然后存放到硬盘文件中,也就是将 Java 对象对象的状态保存下来存储到文件中的过程就叫做序列化。

  • 将硬盘中保存了 Java 对象状态的字节序列按照编号组装成对象恢复到内存中,这个过程称为反序列化。


3 应用示例

参与序列化和反序列化的 Java 类

public class Student implements Serializable {
    private String name;
    private int age;
    //  以下省略有参构造、无参构造、set、get、toString
}
  • 参与序列化和反序列化的类必须实现 Serializable 接口。

序列化操作

public static void main(String[] args) throws Exception {
    //  创建 Java 对象
    Student student = new Student("张三",22);
    //  对象输出流
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student"));
    // 使用 writeObject 序列化对象
    oos.writeObject(student);
    // 刷新
    oos.flush();
    //  关闭流
    oos.close();
}
  • 序列化后的二进制文件会被保存到文件输出流指定的路径。

反序列化操作

public static void main(String[] args) throws Exception {
    //  对象输入流
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"));
    //  使用 readObject() 反序列化  
    Object obj = ois.readObject();
    //  使用对象
    System.out.println(obj);
    //  关闭流
    ois.close();
}
  • 反序列化需要借助文件输入流读取指定路径的二进制文件。

4 序列化版本号的作用 serialVersionUID

  • JVM 首先会通过类名来区分 Java 类,类名不同,则不是同一个类。当类名相同时,JVM 就会通过序列化版本号来区分 Java 类,如果序列化版本号相同就为同一个类,序列化版本号不同就为不同的类。

  • 在序列化一个对象时,如果没有指定序列化版本号,后期对该类的源码进行修改并重新编译后,会导致修改前后的序列化版本号不一致,因为 JVM 会提供一个新的序列化版本号给该类对象。

  • 此时再用以往的反序列化代码去反序列化该类的对象,就会抛出异常 java.io.InvalidClassException ,所以序列化一个类时最好指定一个序列化版本号,或者永远不修改此类。

public class Student implements Serializable {
    private static final Long serialVersionUID = 1L;
}
  • 由 JVM 提供序列化版本号的好处是,同名却不同功能的类,会有两个不同的序列化版本号,JVM 可以通过序列化版本号加以区分,缺点是一旦修改源码,会重新提供序列化版本号,导致修改前后的序列化版本号不一致,进行反序列化时会出现运行出现异常。

  • 由 开发人员 手动提供序列化版本号的好处是,当修改了被序列化类的源码后,以往写的反序列化代码依然可以使用,如 JDK 中的 String 类。以便后期进行增强和维护不会影响使用。

在这里插入图片描述


5 transient 关键字

  • 这个关键字表示游离的,不参与序列化的。

  • 在序列化一个对象时,如果不希望某个属性参加序列化,可以使用 transient 修饰该属性。

  • 被该关键字修饰的属性不会参与到序列化中。

public class Student implements Serializable {

    private static final Long serialVersionUID = 1L;

    private String name;
    private transient int age;
}
  • 如上类,在序列化时就不会保存 age 属性,在反序列化时就不能会付出该属性,默认恢复成 null 或 0 ,由属性类型决定。

6 序列化的好处及应用场景

  • 序列化会将内存中对象的状态转换成二进制文件保存到磁盘当中,当再次使用时会从磁盘中读取该二进制文件,将 Java 对象的状态恢复到内存中。

  • 当你想把内存中的对象保存到磁盘文件或数据库中时可以使用序列化。

  • 当你想在网络传输中传送 Java 对象时,可以使用序列化。

  • 当你想通过 RMI 传输对象时,可以使用序列化。


7 序列化注意事项

  • 序列化只会保存对象的属性状态,不会保存对象中的方法。

  • 父类实现了 Serializable 接口,则其子类也自动实例化了该接口,也就是说子类不用显式实现 Serializable 接口也能参与序列化和反序列化。

  • 一个对象 A 的实例变量引用了其他对象 B,在 A 对象实例化的过程中 ,也会序列化 B ,前提是 A、B 两个类都实现了 Serializable 接口。

  • 当一个类实现 Serializable 接口时,最好手动指定一个序列化版本号(serialVersionUID),避免修改源代码后导致反序列化出现异常。

  • 当一个类对象会被多次重复使用,且一般不会对其属性做修改,就可以对其进行序列化。例如数据库操作中的实体类。


参考博文:

  • 10
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值