IO流(4)- 序列化流与反序列化流

news2024/12/27 15:05:54

目录

1. 序列化流与反序列化流的基本介绍

2. 序列化流的基本用法? 

3. 序列化流的作用?

4. 反序列化流的基本用法?

5. 反序列化流的作用

6. 序列化流与反序列化流使用时需要注意的细节(非常重要)

6.1  被序列化的JavaBean类必须实现 Serializable 接口,

6.2 javaBean类尽量在定义时加上"版本号"

6.3 transient 关键字的使用

6.4 序列化流得到的文件不要修改


1. 序列化流与反序列化流的基本介绍

如上图所示,序列化流与反序列化流也是IO流中会用到的一种高级流,也是用来包装基本流对象的,它们的继承关系如上图中所示。

序列化流是字节流的一种,它负责输出数据。

反序列化流也是字节流的一种,它负责输入数据。

2. 序列化流的基本用法? 

如下图所示为序列化流的构造器,序列化流也是一个高级流,它的参数需要传递一个基本流对象;

序列化流地写方法也很简单,名为writeObject(),参数需要传递一个对象,如下所示;

 我来简单给大家演示一下它的使用

(1)既然它是用来写对象的,我们就先创建一个JavaBean类,如下我定义了一个银行账户类

// 这里面要记住,一定要记得实现 Serializable 接口,否则会序列化失败
public class Account implements Serializable {
    // 账户id
    private Integer accountId;
    // 账户人名称
    private String accountName;
    // 账户密码
    private String accountPassword;

    @Override
    public String toString() {
        return "Account{" +
                "accountId=" + accountId +
                ", accountName='" + accountName + '\'' +
                ", accountPassword='" + accountPassword + '\'' +
                '}';
    }

    public Integer getAccountId() {
        return accountId;
    }

    public void setAccountId(Integer accountId) {
        this.accountId = accountId;
    }

    public String getAccountName() {
        return accountName;
    }

    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public String getAccountPassword() {
        return accountPassword;
    }

    public void setAccountPassword(String accountPassword) {
        this.accountPassword = accountPassword;
    }
}

接着我们来到另一个测试类,创建一个 main 方法,测试序列化流的使用,我把注释也标注进去

public static void main(String[] args) {
        // 定义一个账户对象并赋予初始值
        Account account = new Account(101,"张三","123456");

        // 创建一个 ObjectOutputStream 的流对象
        ObjectOutputStream oos = null;
        try {
            // 对 oos 赋值
            // 将 account 的而对象的结果写道 account.txt 文件中去
            oos = new ObjectOutputStream(new FileOutputStream("user-service/account.txt"));

            // 将 account 对象写出
            oos.writeObject(account);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 保证给程序的健壮性,关闭之前先做判空操作,否则会空指针异常
            try {
                if (oos != null)
                    oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

我们运行此main 方法,在项目目录下会得到名为 account.txt 的文件,里面就存放着我们 account 对象的信息,我们打开查阅如下所示

可以看到,我们打开所看到的是一大堆乱码,这是为什么呢?

其实这是正常现象,序列化流可以把Java文件写到本地文件中去通常情况下,写到本地文件的系列化流文件我们都是看不懂的,因为这本来就不是给我们看的,而是用于给程序看的。下面我们就此来引出序列化流的作用。

3. 序列化流的作用?

通过刚才的一个小例子,我们简单了解了序列化流的使用,也留下了一个问题。既然写出的文件看不懂,我们要他来做什么用呢?

怎么说呢,我举个最简单的例子,假如你电脑上有一个单机游戏,比如植物大战僵尸吧,大家不知道玩过没有,里面就有金币对吧,当我们不想玩了之后,关闭游戏游戏是会把我们的金币数额和所有的通关进度通过IO流的方式写到我们的本地磁盘上去的,当我们再次打开要玩的时候,它会再次读取到本地文件中的内容,将我们的金币数额和通关进度在读到内存中,我们就可以接着上次的进度游玩了。

游戏将金币数额与游戏进度保存的这个过程,就和序列化很相似;

游戏会将金币数额和游戏进度再次读取到内存中去,就和反序列化有些相似。

那么现在就有一个问题了,如果你是这个游戏的设计者,你在序列化的时候,序列化的得到的游戏进度文件希望玩家能看懂能修改吗?肯定不能吧,如果任意玩家都可以看得懂并且能修改序列化得到的游戏进度文件,那和开外挂有什么区别?我直接把金币改成9999999999,不用再打金币了。

这样举例子各位应该就有些明白了吧!

这也只是序列化流的用途之一,序列化流与反序列化流的用途还有很多,比如互联网传输用户数据,我们通常都是采用二进制传输,你总不能将数据原样进行传输吧,特别是如果传输的数据带有敏感信息,比如账户名称密码,银行账户密码,这些是绝对不能让用户看得懂可以修改的。一旦被不法人员拦截攻击,造成的损失会非常大,因此就可以使用我们的序列化流,将要传输的数据进行序列化处理。

4. 反序列化流的基本用法?

如上图所示,即为反序列化流的构造方法与常用方法,我们来简单的测试一下吧

如下代码,我们把刚才序列化的 account 对象在反序列化回来,我们试一试

public static void main(String[] args) {
        // 定义一个账户对象并赋予初始值
        Account account = new Account(101,"张三","123456");

        // 创建一个 ObjectOutputStream 的流对象
        ObjectInputStream ois = null;
        try {
            // 对 ois 赋值
            // 将 account.txt 文件中保存的对象数据再读出来
            ois = new ObjectInputStream(new FileInputStream("user-service/account.txt"));

            // 将 account 对象读出
            Object o = ois.readObject();

            // 输出独到的 account 对象数据
            System.out.println(o);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 保证给程序的健壮性,关闭之前先做判空操作,否则会空指针异常
            try {
                if (ois != null)
                    ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 运行 main 方法,就可以得到如下图所示的结果

可以看到,原本我们创建的对象 account 就又被我们读取出来了,而且还能还得懂。

5. 反序列化流的作用

通过刚才的演示与对序列化流作用的说明,这里就不需要我在多做解释了,总而言之就一句话。

反序列化流可以将序列化的数据载读取为我们可以看的懂可以操作的原本模样。

6. 序列化流与反序列化流使用时需要注意的细节(非常重要)

6.1  被序列化的JavaBean类必须实现 Serializable 接口,

我们可以来看一下 Serializable 接口的源码,如下所示

可以看到,再序列化流接口中,没有任何东西,这种借口有一个专业名词,被称为标记型接口,备有实际含义,只是作为一个标记,只有被标记的JavaBean类才能参与序列化。如果不实现该接口,就会报错,这里应该也不用多解释,各位在做 Spring 项目的时候,通常都会在javaBean类中是实现 Serializable 接口,因为我们项目运行时的数据就是在互联网之间进行传输的,所以需要实现 Serializable 接口。

6.2 javaBean类尽量在定义时加上"版本号"

有些小伙伴可能不知道,当你对一个JavaBean类添加了 Serializable 接口要参与序列化时,该JavaBean类是有版本号的,如果你对javaBean类做了修改,版本号也会修改,一旦版本号修改,再序列化与反序列化时,就会出现错误,因此我们最好在JavaBean类中添加上版本号,这个我们可以借鉴 ArrayList 的源码设计哦,我们来看一下

在ArrayList 源码中我们可以发现,它也定义了一个版本号,而且各位要注意,这个版本号变量不是乱起的,必须是 serialVersionUID,不能是别的;如果不添加 serialVersionUID 变量,我们的javaBean类一旦发生修改,版本号也会修改,就是因为版本号不一致导致无法序列化成功,因此我们需要让版本号使用为一个,就需要定义为 final,又因为它是该类属性中共享属性,需要定义为 static ,serialVersionUID 最好定义为 long 类型。

因此,在定义版本号时,最好定义为

private static final long serialVersionUID = ?

至于取什么值,无所谓,只要遵守该变量的定义原则即可。

6.3 transient 关键字的使用

还有一些时候,我们不想让JavaBean 类中的一些变量参与序列化与反序列化,该怎么办呢?

这个时候就可以用到我们的 transient 关键字。

被 transient 关键字修饰的变量,不会参与到类的序列化与反序列化,我来给大家演示一下,还拿刚才的例子举例说明,如下图所示

我把 accountpassword 账户密码这一属性不参与数列化,因为太不安全,我们就可以在JavaBean类中 该变量前面加上 transient 关键字,一旦加上,就不会参与序列化,我们再回到 main 方法作出修改

public static void main(String[] args) {
        // 定义一个账户对象并赋予初始值
        Account account = new Account(101,"张三","123456");

        // 创建一个 ObjectOutputStream 的流对象
        ObjectOutputStream oos = null;
        // 创建一个 ObjectOutputStream 的流对象
        ObjectInputStream ois = null;
        try {
            // 对 oos 赋值
            oos = new ObjectOutputStream(new FileOutputStream("user-service/account.txt"));

            // 对 ois 赋值
            // 将 account.txt 文件中保存的对象数据再读出来
            ois = new ObjectInputStream(new FileInputStream("user-service/account.txt"));

            // 先将 account 对象写出
            oos.writeObject(account);

            // 再将 account 对象读出
            Object o = ois.readObject();

            // 输出独到的 account 对象数据
            System.out.println(o);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 保证给程序的健壮性,关闭之前先做判空操作,否则会空指针异常
            try {
                if (oos != null)
                    oos.close();
                if (ois != null)
                    ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 然后再次运行得到如下结果

可以发现,即便我们初始为 accountPassword 赋值,但因为添加了 transient 关键字的缘故,他就不会参与序列化与反序列化,系统胡自动给一个默认值为 null。

各位小伙伴有些时候再看源码时的时候可能也见过 transient 关键字,就是用来做标记的,不让其被标记的变量参与序列化。

6.4 序列化流得到的文件不要修改

我们在通过序列化得到的文件,虽然是一堆乱码看不懂,但时不要轻易去修改,因为一旦修改,再次反序列化时,程序就读不出来了,会报错,这也是序列化流中需要注意的一个点哦!

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

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

相关文章

90%的测试工程师是这样使用Postman做接口测试的

一:接口测试前准备 接口测试是基于协议的功能黑盒测试,在进行接口测试之前,我们要了解接口的信息,然后才知道怎么来测试一个接口,如何完整的校验接口的响应值。 那么问题来了,那接口信息从哪里获取呢&…

中国AI大模型峰会“封神之作”!开发者不容错过这场夏季盛会

年度最强大模型顶会来袭!喊话中国数百万AI开发者,速来! 硬核来袭!中国AI大模型峰会“封神之作”,开发者们不容错过! 前瞻大模型发展趋势,紧跟这场大会! 中国科技超级碗,大模型最新前…

SpringCloud Alibaba分布式集群要点

1、可通过nginxkeepalived实现nginx高可用集群。 2、nacos集群,在nacos/conf/cluster.conf配置IP:8848,nginx中配置nacos负载均衡,yml文件使用其对应域名即可。 注:服务器之间内网不通 举例:腾讯服务器之间就存在内网不通的现象…

关于bigemap在土地行业的一些应用

选择Bigemap的原因: 由于我们是国营企业单位,管理六万多亩的国有土地,必须要有这样的软件套图 客户之前用的谷歌,后来不能访问了,通过百度搜索到这款软件 使用场景: 使用软件一般都用于套坐标以及空间规…

未来将有可能是元宇宙发展的数字化时代

近年来,元宇宙环境的概念近年来引起了广泛关注,并引发了巨大的舆论浪潮。然而,经济形式的放缓和行业向人工智能的转变所带来的挫折引发了人们对这一 " 雄心勃勃 " 的概念的可行性和时机的质疑。 很明显,一些挑战阻碍了…

Python元编程-装饰器介绍、使用

目录 一、Python元编程装饰器介绍 二、装饰器使用 1. 实现认证和授权功能 2.实现缓存功能 3.实现日志输出功能 三、附录 1. logging.basicConfig介绍 2. 精确到毫秒,打印时间 方法一:使用datetime 方法二:使用time 一、Python元编程…

Eureka 学习笔记4:EurekaClient

版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口&#xff0c;拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能&#xff1a; 获取注册表根据应用名称获取应用根据实例 id 获取实例信息 public interface LookupService<…

【MySQL】数据库基本使用

文章目录 一、数据库介绍二、数据库使用2.1 登录MySQL2.2 基本使用2.2.1 显示当前 MySQL 实例中所有的数据库列表2.2.2 创建数据库2.2.3 创建数据库表2.2.4 在表中插入数据2.2.5 在表中查询数据 三、服务器、数据库、表之间的关系四、SQL语句分类五、存储引擎 一、数据库介绍 …

多线程案例(2)

文章目录 多线程案例二二、阻塞式队列 大家好&#xff0c;我是晓星航。今天为大家带来的是 多线程案例二 相关的讲解&#xff01;&#x1f600; 多线程案例二 二、阻塞式队列 阻塞队列是什么 阻塞队列是一种特殊的队列. 也遵守 “先进先出” 的原则. 阻塞队列能是一种线程…

设计走查指南:提升设计质量的关键步骤

在产品设计过程中&#xff0c;确保产品设计质量是至关重要的。设计走查作为一种关键的质量控制方法&#xff0c;能够帮助设计团队发现问题并采取相应措施来提升设计质量。通过有效地进行设计走查&#xff0c;团队可以提高设计作品的一致性、可用性和用户满意度&#xff0c;从而…

正则表达式在格式校验中的应用以及包装类的重要性

文章目录 正则表达式&#xff1a;做格式校验包装类&#xff1a;在基本数据类型与引用数据类型间的桥梁总结 在现代IT技术岗位的面试中&#xff0c;掌握正则表达式的应用以及理解包装类的重要性是非常有益的。这篇博客将围绕这两个主题展开&#xff0c;帮助读者更好地面对面试挑…

Elasticsearch Java客户端和Spring data elasticsearch-Elasticsearch文章三

文章目录 官网版本组件版本说明实现代码地址es Spring Data Repositories例子&#xff1a;ElasticsearchRepository分析 es Spring Data Repositories 关键字es Spring Data Repositories client 加载rest风格客户端直接执行dsl例子响应式客户端-ReactiveElasticsearchClientpo…

【SLAM】LoFTR知多少

1. LoFTR: Detector-Free Local Feature Matching with Transformers PAPER 论文 | LoFTR: Detector-Free Local Feature Matching with Transformers 代码 | CODE: 关键词 | detector-free, local feature matching LoFTR知多少 1. LoFTR: Detector-Free Local Feature M…

DirectX SDK下载安装及开发环境设置

1 DirectX DirectX&#xff08;Direct eXtension&#xff0c;简称DX&#xff09;是由微软公司创建的多媒体编程接口&#xff0c;是一种应用程序接口&#xff08;API&#xff09;。DirectX可以让以windows为平台的游戏或多媒体程序获得更高的执行效率&#xff0c;加强3D图形和声…

15、两个Runner初始化器(ApplicationRunner接口和CommandLineRunner接口的实现类)

两个Runner初始化器 两个Runner初始化器——主要作用是对component组件来执行初始化 这里的Component组件我理解为是被Component注解修饰的类 Component //用这个注解修饰的类&#xff0c;意味着这个类是spring容器中的一个组件&#xff0c;springboot应用会自动加载该组件。 …

python-网络爬虫.regular

regular 正则表达式 (regular expression) 正则表达式(regular expression)描述了一种字符串匹配的模式 &#xff08;pattern&#xff09;&#xff0c; 可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串 中取出符合某个条件的子串等。 正则表达式是由普通…

gitlab配置webhook

一.前言 当需要做jenkins的自动化触发构建时&#xff0c;就需要配置gitlab的webhook功能&#xff0c;以下来展示以下如何配置gitlab的webhook&#xff0c;jenkins的配置就不在这里展示了&#xff0c;可以去看我devops文章的完整配置 二.配置 在新版本的gitlab中&#xff0c…

MySQL对表的操作以及数据类型

文章目录 创建删除表查看修改重命名表新增列修改列的属性删除列修改列名插入数据 数据类型enum和setenum和set的查找 创建 create table table_name ( field1 datatype, field2 datatype, field3 datatype ) charset 字符集 collate 校验规则 engine 存储引擎;其中field 表示列…

Linux - 环境变量

1.基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但 是照样可以链接成功&#xff0c;生…

超级个体新时代Web3space西南旗舰合伙人招募活动圆满落幕

7月30日&#xff0c;一场备受瞩目的超级个体新时代—Web3space西南旗舰合伙人招募活动在四川省成都市G1咖啡馆会议室成功举办。本次活动吸引了30余位Web3领域的从业者和爱好者参与&#xff0c;现场氛围十分热烈。 首先&#xff0c;CyberDAO执行合伙人JR老师主持了Web3space商业…