【Java笔记】 深入理解序列化和反序列化

news2025/1/12 3:43:01

深入理解序列化和反序列化

文章目录

  • 深入理解序列化和反序列化
    • 1.是什么
    • 2.为什么
    • 3.怎么做
      • 3.1 实现Serializable接口
      • 3.2 实现Externalizable接口
      • 3.3 注意知识点
      • 3.4 serialVersionUID的作用
    • 4 扩展

1.是什么

序列化:就是讲对象转化成字节序列的过程。

反序列化:就是讲字节序列转化成对象的过程。

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。

2.为什么

那么为什么要去进行序列化操作呢?有两个原因:

  1. 持久化:对象是存储在 JVM 中的堆区的,但是如果 JVM 停止运行了,对象也就不存在了。序列化可以将对象转换成字节序列,可以写进硬盘中实现持久化。在新开期的 JVM 中可以读取字节序列进行反序列化成对象。
  2. 网络传输:网络直接传输数据,但是无法直接传输对象,可在传输前序列化,传输完成后反序列化对象。所以所有可以在网络中传输的对象都必须是可序列化的。也就是我们大家熟悉的Json

3.怎么做

怎么去实现对象的序列化呢?

Java为我们提供了对象序列化的机制,规定了要实现序列化对象的类要满足的条件和实现方法。

  1. 对于要序列化对象的类必须实现Serializable接口或者Externalizable接口
  2. 实现方法:JDK提供的ObjectOutputStreamObjectInputStream来实现序列化和反序列化

3.1 实现Serializable接口

public class Student implements Serializable {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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

序列化

ObjectOutputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法

  • public ObjectOutputStream(OutputStream out) : 创建一个指定OutputStream的ObjectOutputStream。

构造举例,代码如下:

FileOutputStream fileOut = new FileOutputStream("stu.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);

2.写出对象方法

  • public final void writeObject (Object obj) : 将指定的对象写出。
public class SerializeDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("stu.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        Student student = new Student();
        student.setAge(18);
        student.setName("xiaoxin");
        oos.writeObject(student);
        
        fos.close();
        oos.close();
    }
}

反序列化

ObjectInputStream类

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法

  • public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。
  • 如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

  • public final Object readObject () : 读取一个对象。

public class DeserializationDemo {
    public static void main(String[] args) throws Exception {

        FileInputStream fis = new FileInputStream("stu.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);

        Student student = (Student) ois.readObject();

        fis.close();
        ois.close();

        System.out.println(student);// Student{name='xiaoxin', age=18}
    }
}

3.2 实现Externalizable接口

实现Externalizable接口必须重写连个方法

  • writeExternal(ObjectOutput out)
  • readExternal(ObjectInput in)

如果序列化时一个字段没有序列化,那反序列化是要注意别给为序列化的字段反序列化了

举个栗子

public class Student implements Externalizable {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(age);
        out.writeObject(name);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.age=in.readInt();
        this.name= (String) in.readObject();
    }
}

序列化

public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("stu.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Student student = new Student();
        student.setAge(18);
        student.setName("xiaoxin");
        oos.writeObject(student);
        oos.close();
        fos.close();
    }

反序列化

public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("stu.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);

        Student student = (Student) ois.readObject();
        System.out.println(student);
        ois.close();
        fis.close();
    }

3.3 注意知识点

  1. 一个对象要进行序列化,如果该对象成员类型是引用类型的,那这个引用类型也一定要是可序列化的,否则会报错

  2. 同一个对象多次序列化成字节序列,这多个字节序列反序列化成的对象还是一个(使用==判断为true)(因为所有序列化保存的对象都会生成一个序列化编号,当再次序列化时会去检查此对象是否已经序列化了,如果是,那序列化只会输出上个序列化的编号)

  3. 如果序列化一个可变对象,序列化之后,修改对象属性值,再次序列化,只会保存上次序列化的编号(这是个坑注意下)

  4. 对于不想序列化的字段可以再字段类型之前加上transient关键字修饰(反序列化时会被赋予默认值)

  5. 另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配

    • 该类包含未知数据类型

    • 该类没有可访问的无参数构造方法

3.4 serialVersionUID的作用

Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

先讲述下序列化的过程:在进行序列化时,会把当前类的serialVersionUID写入到字节序列中(也会写入序列化的文件中),在反序列化时会将字节流中的serialVersionUID同本地对象中的serialVersionUID进行对比,一致的话进行反序列化,不一致则失败报错(报InvalidCastException异常)

serialVersionUID的生成有三种方式(private static final long serialVersionUID= XXXL ):

  1. 显式声明:默认的1L
  2. 显式声明:根据包名、类名、继承关系、非私有的方法和属性以及参数、返回值等诸多因素计算出的64位的hash值
  3. 隐式声明:未显式的声明serialVersionUID时java序列化机制会根据Class自动生成一个serialVersionUID(最好不要这样,因为如果Class发生变化,自动生成的serialVersionUID可能会随之发生变化,导致匹配不上)

序列化类增加属性时,最好不要修改serialVersionUID,避免反序列化失败

public class Employee implements java.io.Serializable {
     // 加入序列版本号
     private static final long serialVersionUID = 1L;
     public String name;
     public String address;
     // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
     public int eid; 

     public void addressCheck() {
         System.out.println("Address  check : " + name + " -- " + address);
     }
}

4 扩展

其实对于对象转化成json字符串和json字符串转化成对象,也是属于序列化和反序列化的范畴,相对于JDK提供的序列化机制,各有各的优缺点:

  • JDK序列化/反序列化:原生方法不依赖其他类库、但是不能跨平台使用、字节数较大
  • json序列化/反序列化:json字符串可读性高、可跨平台使用无语言限制、扩展性好、但是需要第三方类库、字节数较大

如果文章对您有帮助,希望点个赞/收藏/关注! O(∩_∩)O~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/94575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++开发,这些GUI库一定不要错过

程序员宝藏库:https://gitee.com/sharetech_lee/CS-Books-Store 如果问Python这类集成度非常高的编程语言GUI开发用什么库,可以列举出很多不错的第三方库。 但是,如果这个问题放在C这种基础的编程语言上,很多同学估计一时间都无从…

用Gurobi+python求解设施选址问题(facility location)

参考:Gurobi 官方资源 设施选址(Facility Location) 1.背景介绍 设施选址问题在许多工业领域如物流,通信等都有应用,在本案例中展示如何解决设施选址问题,决策出仓库的数量和地点,为一些超市…

Crash Consistency on File Systems: 文件系统一致性保证 (1) Journaling File System

文件系统是操作系统中管理用户数据的重要模块。其中一项重要的任务就是确保用户数据的在系统突然崩溃之后,系统能够恢复出完整、一致的用户数据。本文将会分析两种流行的文件系统,Journaling File System 和 Log-structured File System是如何确保数据的…

dataFactory连接mysql详细配置教程

场景:最近项目提出机构用户中其中一个部门下用户人数有20万,加载的时候十分缓慢,本地想重现的一下,这就需要在本地表中生成>20万的数据,搜索了网上的教程写的都是很粗略。 目录 dataFactory连接mysql配置 安装包下…

第二证券|“20cm”涨停!盘中暴涨110%,又有港股暴力拉升

A股商场今日上午窄幅动摇,电子等板块领涨。北向资金半响净买入额到达26.10亿元。 港股商场今日上午动摇也较为温和。不过,仍有个股剧烈动摇。比如浦江世界上午暴升,盘中涨幅一度超过110%。 A股窄幅动摇 电子板块领涨 今日上午A股商场全体体…

STL六大组件之算法

文章目录56、STL六大组件之遍历算法57、STL六大组件之查找算法158、STL六大组件之查找算法259、STL六大组件之统计算法60、STL六大组件之合并算法61、随机数(rand)和随机数种子(srand)的理解62、STL六大组件之随机算法(洗牌算法)6…

javaweb笔记

javaweb数据库jdbcmaven数据库 1.chart定长 2.分组查询:where>聚合函数>having 3.分页查询: select 字段列表 from limit 起始索引, 查询条目数 计算公式: 起始索引(当前页码-1)每页显示的条数 不同数据库分页查询不一样 4.like模糊查…

8种常见python运行错误,看看你中招了没?

人生苦短 我用python 对于刚入门Python的新手同学来说, 在运行代码时总免不了报错。 如何通过报错查找错误代码? 今天给大家总结了一些常见的报错类型, 每种报错都会有标有错误细节和错误行。 大家以后看到了,就更容易找出自…

使用navicat工具生成表的新增字段sql

1、在需要的表右键,设计表 2、点击【添加字段】 3、创建字段及注释,不要点【保存】和CtrlS 4、点击【SQL预览】 5、复制生成的sql语句

iframe 标签

一. 什么是 iframe 1. iframe 是 HTML元素,用于在网页中内嵌另外一个网页. 2. iframe 默认有一个宽高,存在边界. 3. iframe 是一个行内块级元素,可以通过 display 修改. 二. iframe 元素属性 1. src : 指定内联网页的地址 2. frameborder : iframe 默认有个边界,可以设置fram…

深入剖析Linux RCU原理(一)初窥门径

说明: Kernel版本:4.14ARM64处理器,Contex-A53,双核使用工具:Source Insight 3.5, Visio 1. 概述 RCU, Read-Copy-Update,是Linux内核中的一种同步机制。RCU常被描述为读写锁的替代品&#xf…

Openssl 1024bit RSA算法---公私钥获取和处理(一)

1.简介 使用OpenSSL生成公私钥文件,然后再将文件中的信息读出的操作。 由于要对设备升级,需要用到RSA算法对一部分验证信息进行加密. 2.使用OpenSSL获取公私钥 我在window系统尝试安装OpenSSL,但是安装不上,我们可以使用linux…

模式识别 第7、8章 特征的选择和提取

基本概念 问题的提出 特征→ 特征空间: 每一个特征对应特征空间的一个维度 ;特征越多,特征空间的维度越高原则:在保证分类效果的前提下用尽量少的特征来完成分类基本概念 (1)特征形成:由仪器…

如何去除图片雾化?给你推荐图片去雾怎么去除的方法

小伙伴们会不会和我一样喜欢外出爬山呢?为了留住美好记忆,我们会在途中拍照记录。但是山上很经常会有雾气,会容易导致我们拍出来的图片模糊不清。那应该怎么办呢?其实,我们只要对图片进行去雾处理就可以很好解决这个问…

[附源码]Nodejs计算机毕业设计基于的校园疫情防控管理Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分…

Web(十一)JavaScript知识训练-数学对象

1、Math.ceil(-3.14)的结果是( B)。 A、 -3.14 B、 -3 C、 -4 D、 3.14 2、Math.floor(-3.14)的结果是( C)。 A、 -3.14 B、 -3 C、 -4 D、 3.14 3、Math.round(-3.14)的结果是( B)。 A、 -3.14 B、 -3 C…

Spring Cloud Openfeign微服务接口调用与Hystrix集成实战

关于openfeign 可以认为OpenFeign是Feign的增强版,不同的是OpenFeign支持Spring MVC注解。OpenFeign和Feign底层都内置了Ribbon负载均衡组件,在导入OpenFeign依赖后无需专门导入Ribbon依赖,用做客户端负载均衡,去调用注册中心服务…

dataFactory向mysql批量插入测试数据

目录 第一步:准备阶段:datafactory已连接app_user的表 第二步: 点击原表app_user,其中属性界面properties中配置含义如下: 第三步:根据需要设置插入字段 第四步:设置每个字段的规则后,点击se…

病毒之Worm.Win32.AutoRun

题外话:在被奥密克戎包围的我(两个室友和我,一个低烧、一个咳嗽、就差我了,这属实是真被包围了丫)在和Worm.Win32.AutoRun决一死战… 本次Worm.Win32.AutoRun的来源: windows电脑上重装vscode,然后没有 mingw-get-setu…

浏览器兼容模式如何设置?只需要跟着下面的步骤设置

许多考生在报考教师资格证或者其他的考试报名,会遇到浏览器兼容设置的问题。与其到时急急忙忙来设置浏览器的兼容模式,不如提前设置好,免了后顾之忧。浏览器兼容模式怎么设置?一起来看看关于浏览器兼容模式的含义以及设置方式吧&a…