Java 输入与输出(I\O)之对象流与对象序列化

什么是Java的对象流?

Java对象流是用于存储和读取基本数据类型数据或对象数据的输入输出流。
Java的对象流可分为两种:

  • 1,对象输入流类ObjectInputStream 用于从数据源读取对象数据,它是可以读取基本数据类型数据或对象数据的输入流。
  • 2,对象输出流类ObjectOutputStream 用于把对象写入到数据源,它是可以输出基本数据类型数据或对象数据的输出流。

Java中提供了ObjectInputStream、ObjectOutputStream这两个类用于对象序列化操作,这两个类是用于存储和读取对象的输入输出流类,只要把对象序列化转换成平台无关的二进制数据存储起来,就等于保存了这个对象;之后根据需要再把保存的对象的二进制数据读取进来就可以创建并使用此对象。
ObjectInputStream、ObjectOutputStream是可以帮助开发者完成保存和读取对象的二进制数据的输入输出流。但前提是对象必须实现Serializable接口。

什么是对象的序列化和反序列化?

序列化与反序列化

  • 序列化:序列化是将内存中的对象转换成与平台无关的二进制流(二进制字节数组)的过程。序列化的二进制流可通过网络传输到别的网络节点;或者保存为二进制的磁盘文件,从而达到持久化的目的。
  • 反序列化:反序列化则是把从网络接收或者从磁盘文件读取的二进制流,重新构建为对象的过程。
    在这里插入图片描述

序列化的作用 序列化是将可序列化的Java对象转换为字节序列,这此序列可存储在磁盘中,或者通过网络传输,以便在需要的时候可重新构建为Java对象。序列化机制使得Java对象可以脱离程序而独立存在,从而实现持久化。

序列化版本UID

序列化版本UID,是指可序列化的类的serialVersionUID参数。
Java序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,Java虚拟机会把传过来的字节流中的serialVersionUID和本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的实体类,可以进行反序列化;否则Java虚拟机会拒绝对这个实体类进行反序列化并抛出异常。

serialVersionUID的三种生成方式:

  • 1、默认的serialVersionUID是1L(长整型数 1)。
  • 2、用户指定一个长整型数作为serialVersionUID。
  • 3、根据类名、接口名、成员方法以及属性等通过散列(Hash)生成一个64位的长整型数作为serialVersionUID。

如果实现java.io.Serializable接口的实体类没有显式定义一个名为serialVersionUID、类型为long的属性时,Java序列化机制会根据编译的.class文件自动散列(Hash)生成一个serialVersionUID。如果.class文件没有改变,那么多次编译,其serialVersionUID也不变。

对象中有三种属性不可序列化:
1、static 修饰的属性。因为,序列化存储的是对象的属性状态,而static属性是属于类的,不属于对象,所以序列化时不会被序列化。
2、transient 修饰的属性,在序列化时不会被序列化。例如,有些敏感信息,如密码等可用transient修饰来避开序列化。
3、未实现序列化(serializable)接口的属性,无法被序列化。

一、默认序列化

一个类实现了java.io.Serializable接口,才能支持对象序列化。

默认序列化:Java语言为用户定义了默认的序列化、反序列化方法,其实就是ObjectOutputStream的defaultWriteObject方法和ObjectInputStream的defaultReadObject方法。

我们先来看一个默认序列化的演示程序:
类PersonSerial
默认序列化测试演示使用的类PersonSerial,实现了Serializable接口,但没有重写writeObject()和readObject()方法。

package IOStream.ObjectSerialization;
import java.io.Serializable;
public class PersonSerial implements Serializable {private String     name;private int     age;private String address;public PersonSerial(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}@Overridepublic String toString() {return "PersonSerial {" +"姓名='" + name + '\'' +", 年龄=" + age +", 地址='" + address + '\'' +'}';}
}

默认序列化的测试演示程序:

package IOStream.ObjectSerialization;
import java.io.*;
public class ObjectSerialization {// 序列化对象方法public static void serializable(File file) throws Exception{OutputStream outputFile = new FileOutputStream(file);ObjectOutputStream oos = new ObjectOutputStream(outputFile);oos.writeObject(new PersonSerial("张三丰", 108,"五台山"));oos.writeObject("海内存知己,天涯若比邻。");oos.writeDouble(3.1416D);oos.close();}// 反序列化对象方法public static void deserializable(File file) throws Exception{InputStream inputFile = new FileInputStream(file);ObjectInputStream ois = new ObjectInputStream(inputFile);PersonSerial p = (PersonSerial)ois.readObject();System.out.println(p);String txt = (String)ois.readObject();double pi = (double)ois.readDouble();System.out.println(txt);System.out.println("PI = "+pi);ois.close();}public static void main(String[] args)  throws Exception{File file = new File("D:/temp/对象序列化.txt");serializable(file);deserializable(file);}}

默认序列化的测试演示结果图:
在这里插入图片描述
说明:

1,序列化的效果:文本文件“对象序列化.txt”中的内容是程序序列化后写入的与平台无关的二进制流(二进制字节数据)。这 个序列化产生的文件,其文件头,包括序列化协议、序列化协议版本等信息,后面才是真正对象序列化后的二进制字节数据。
2,反序列化的效果:文本文件上面控制台上显示的三行信息,是程序从文本文件“对象序列化.txt”中读入的信息,再创建的对象、字符串和双精度的浮点数。

测试结果说明:序列化和反序列化成功了,类PersonSerial只实现了Serializable接口,并没有重写writeObject()和readObject()方法,但类PersonSerial具备了序列化的能力。因此,序列化和反序列化过程中,默认调用的是ObjectOutputStream的defaultWriteObject()以及ObjectInputStream的defaultReadObject()方法。

注意事项: 无论是默认序列化,还是定制序列化,writeObject()方法存储属性变量的顺序都应该和readObject()方法恢复属性变量的顺序一致,否则将不能正确恢复该Java对象。

二、定制序列化

定制序列化的实现:当一个类实现java.io.Serializable接口时,就可以支持对象序列化。如果用户有特殊需求,可以通过在类的自定义writeObject()和readObject()方法来达到定制的目的。两个方法的方法签名如下所示:

    private void writeObject(java.io.ObjectOutputStream s) throws Exception {//在这里编写定制的代码}private void readObject(java.io.ObjectInputStream s) throws Exception {//在这里编写定制的代码}

进行序列化、反序列化时,虚拟机会首先试图调用对象里的writeObject()和readObject()方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,那么默认调用的是ObjectOutputStream的defaultWriteObject()以及ObjectInputStream的defaultReadObject()方法。换言之,利用自定义的writeObject()方法和readObject()方法,用户可以自己控制序列化和反序列化的过程。
这是非常有用的。比如:
1、有些场景下,某些字段我们并不想要使用Java提供给我们的序列化方式,而是想要以自定义的方式去序列化它,比如ArrayList的elementData、HashMap的table(至于为什么在之后写这两个类的时候会解释原因),就可以通过将这些字段声明为transient,然后在writeObject()和readObject()中去使用自己想要的方式去序列化它们
2、因为序列化并不安全,因此有些场景下我们需要对一些敏感属性进行加密再序列化,然后再反序列化的时候按照同样的方式进行解密,这样会有更高的安全性。要这么做,就必须自定义编写writeObject()和readObject(),writeObject()方法在序列化前对字段加密,readObject()方法在序列化之后对字段解密。

三、另一种定制序列化机制,使用Externalizable接口

对象的序列化和反序列化非常复杂,Java语言还有另一种自定义序列化机制,这种方式完全由程序员决定如何存储和恢复Java对象数据。这种机制要求Java类必须实现Externalizable接口
Externalizable接口提供了更高级的自定义序列化能力,我们可以控制序列化的过程,选择序列化哪些属性,以及如何序列化。

Externalizable接口
Externalizable接口是Java提供的自定义序列化接口,它继承自Serializable接口,并添加了两个方法:writeExternal(ObjectOutput out)和readExternal(ObjectInput in)。这两个方法分别用于序列化和反序列化对象。

public interface Externalizable extends Serializable {void writeExternal(ObjectOutput out) throws IOException;void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

在某些场景,例如,在处理大型对象或复杂对象图形时,使用Externalizable接口进行序列化定制可能会获得更好的性能。

我们来看一个简单的Externalizable序列化与反序列化例子:
在这里插入图片描述

首先,我们来定义一个PersonExter类,该类是实现了Externalizable接口的Person,并重写实现writeExternal()方法和readExternal()方法。

package IOStream.ObjectSerialization;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class PersonExter implements Externalizable {private String name;private int age;private String address;public PersonExter() { //无参数的构造器是必须的。}public PersonExter(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {// 序列化name、age和address属性out.writeObject(name);out.writeInt(age);out.writeObject(address);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {// 反序列化name、age和address属性name = (String) in.readObject();age = in.readInt();address = (String) in.readObject();}@Overridepublic String toString() {return "PersonExter {" +"姓名='" + name + '\'' +", 年龄=" + age +", 地址='" + address + '\'' +'}';}
}

Externalizable接口实现序列化的注意事项:

  • 必须有默认构造器:这个类定义中,无参数构造器是必须的,不定义,会出现“无有效构造器”例外。
    public PersonExter() { //无参数的构造器是必须的。}
  • 必须重写实现writeExternal()方法和readExternal()方法。
@Override
public void readExternal(ObjectInput arg0) throws IOException, ClassNotFoundException {}@Override
public void writeExternal(ObjectOutput arg0) throws IOException {}

一个简单的使用Externalizable接口序列化与反序列化演示例程
下面是Externalizable接口序列化与反序列化的简单演示例程:

package IOStream.ObjectSerialization;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class ExternalizableDemo {public static void main(String[] args) {String path = "D:/temp/person.txt";PersonExter person = new PersonExter("左中堂", 80, "湖南");// 序列化try (FileOutputStream foS = new FileOutputStream(path);ObjectOutputStream out = new ObjectOutputStream(foS)) {out.writeObject(person);} catch (IOException e) {e.printStackTrace();}// 反序列化try (FileInputStream fis = new FileInputStream(path);ObjectInputStream in = new ObjectInputStream(fis)) {PersonExter restoredPerson = (PersonExter) in.readObject();System.out.println(restoredPerson);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}

例程测试结果图:
在这里插入图片描述
对象序列化与反序列化有一些经典的应用场景:
1,在联机游戏中各参与方中的场景处理,;
2,网络电子画板中各个画板内容的同步显示;
3,线上教育中电子黑板上内容的同步显示。

参考文献:

  • JAVA中的对象流ObjectInputStream
  • JAVA中的ObjectOutputStream类
  • java 序列化 之 externalizable
  • Java IO流详解(七)----对象流(序列化与反序列化)

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

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

相关文章

Spearman相关系数和P值计算的MATLAB代码

% 导入数据 data readtable(data.xlsx);% 获取列名 columnNames data.Properties.VariableNames; numCols width(data);% 初始化Spearman相关系数和P值矩阵 spearmanCorr zeros(numCols); spearmanPValues zeros(numCols);% 计算Spearman相关系数及P值 for i 1:numColsfo…

fdisk - Linux下的磁盘分区利器

文章目录 前言一、安装和启动二、基本命令2.1 查看分区表2.2 删除分区2.3 创建新分区2.4 更改分区类型2.5 其他指令 三、注意事项四、其他相关工具 前言 在Linux系统中,磁盘管理是维护系统性能和数据安全的重要环节。fdisk 是一个强大的命令行工具,专门…

【解决】Linux环境中mysqlclient安装失败问题

问题描述 在Linux系统下安装myslclient报异常。系统为Centos 8 使用 pip install mysqlclient 报出下面的异常 error: subprocess-exited-with-error Getting requirements to build wheel did not run successfully.│ exit code: 1╰─> [30 lines of output]/bin/sh: pkg…

GitHub 上传项目保姆级教程

构建项目仓库 登录 GitHub 并进入主页。点击右上角的 New 按钮,进入创建新仓库页面。输入仓库名称和描述(可选),选择是否公开(Public)或私有(Private)。可以选择是否初始化仓库&…

WPF中设置DataGrid的常用样式

1. 设置DataGrid控件的外观和行为 <!-- 定义一个名为DataGridDefault的样式&#xff0c;专门用于设置DataGrid控件的外观和行为 --> <Style x:Key"DataGridDefault" TargetType"DataGrid"><!-- 设置DataGrid在Grid布局中的行索引为1 -->…

计算机网络:网络层 —— 路由选择与静态路由配置

文章目录 路由选择路由选择的基本概念路由选择算法路由选择策略 路由器的工作原理路由表静态路由配置默认路由特定主机路由 路由选择 路由选择&#xff08;Routing&#xff09;是网络层的一个关键功能&#xff0c;负责在源和目的地之间选择最佳路径&#xff0c;以确保数据包高…

Pytest-Bdd-Playwright 系列教程(5):仅执行测试用例的收集阶段

Pytest-Bdd-Playwright 系列教程&#xff08;5&#xff09;&#xff1a;仅执行测试用例的收集阶段 一、为什么需要仅收集测试用例二、应用场景三、方法详解【方法1】&#xff1a;添加pytest.ini文件的addopts配置项【方法2】&#xff1a;通过命令行参数运行 四、CI/CD 环境下的…

联想笔记本电脑睡眠后打开黑屏解决方法

下载联想机器睡眠无法唤醒修复工具 下载地址&#xff1a;https://tools.lenovo.com.cn/exeTools/detail/id/233/rid/6182522.html 使用完后重启电脑&#xff0c;问题解决。

(11)(2.1.6) Hobbywing DroneCAN ESC(一)

文章目录 前言 1 连接和配置 2 参数说明 前言 具有 CAN 接口&#xff08;including these&#xff09;的业余 ESC 支持 DroneCAN&#xff0c;它允许自动驾驶仪通过 CAN 控制 ESC /电机&#xff0c;并检索单个转速、电压、电流和温度。 具有 CAN 接口&#xff08;including …

AI助力医疗:未来的医生会是机器人吗?

内容概要 在这一场医疗科技的新浪潮中&#xff0c;AI医疗正以前所未有的速度渗透到各个角落。随着技术的飞速进步&#xff0c;人工智能成为了推动医疗领域革新的重要力量。从精准诊断到个性化治疗&#xff0c;AI正在帮助医生们更快速、准确地分析患者的病情&#xff0c;提高了…

HTMLCSS:3D旋转动画机器人摄像头

效果演示 创建了一个3D机器人摄像头效果。 HTML <div class"modelViewPort"><div class"eva"><div class"head"><div class"eyeChamber"><div class"eye"></div><div class&quo…

用 css 实现空列表自动提示 “空状态”

css实现 /* 空列表状态通用css */ .list-auto-empty:empty::after {content: attr(empty);color: gray;margin: 50px auto 0;background-image: url(empty_data.png);background-size: 100%;background-repeat: no-repeat;width: 224px;height: 140px;padding-top: 140px;text…

计算机科学与技术-毕业设计选题推荐

基于特定技术的系统设计与实现 基于深度学习的图像识别系统设计与实现基于区块链的数据安全保护技术研究与实现基于云计算的大数据处理平台设计与开发基于物联网的智能家居系统设计与实现基于机器学习的推荐算法研究与实现 面向实际应用的需求分析与开发 智慧医疗信息系统设…

如何在Linux系统中使用Git进行版本控制

如何在Linux系统中使用Git进行版本控制 Git简介 安装Git 在Debian/Ubuntu系统中安装 在CentOS/RHEL系统中安装 初始化Git仓库 配置全局用户信息 基本的Git命令 添加文件到暂存区 查看状态 提交更改 查看提交历史 工作流 分支管理 切换分支 合并分支 远程仓库 添加远程仓库 推…

大型商场应急响应系统开发:SpringBoot篇

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

记录一次查询优化

一.背景描述 1.1问题和情况 生产环境&#xff0c;有一张按每天一份数据的表&#xff08;下面简称表1&#xff09;&#xff0c;跨天查询较慢&#xff0c;跨月查询甚至超时查询一天内的数据速度不怎么慢查询是分页的表1按照日期做了子分区&#xff0c;一个月一个子分区 1.2造成…

【网络安全 | 漏洞挖掘】逻辑漏洞+无限制爆破实现业务瘫痪

未经许可,不得转载。 文章目录 前言正文前言 目标:target.com,是一个为设计团队服务的工作平台。 该程序允许用户创建账户并组建团队,指定的领导者担任管理员。团队类型包括: 1、免费团队:限于一个项目,最多三份文件。 2、学生团队:项目和文件无限制,学生可免费获…

头歌C语言数据结构课程实验(栈的应用)

第1关&#xff1a;利用栈实现整数的十进制转八进制 本关必读 栈是基础的数据结构&#xff0c;元素操作遵循后进先出的原理。本关卡基于数组存储实现了栈的基本操作。 该方案将栈存储在一片连续空间里&#xff0c;并通过data、top和max三个属性元素。组织成为一个结构&#xf…

Java | Leetcode Java题解之第521题最长特殊序列I

题目&#xff1a; 题解&#xff1a; class Solution {public int findLUSlength(String a, String b) {return !a.equals(b) ? Math.max(a.length(), b.length()) : -1;} }

机器学习、深度学习和强化学习的区别

在当今的人工智能领域&#xff0c;机器学习、深度学习和强化学习是三个重要的概念&#xff0c;它们各自有着独特的特点和应用场景。下面我们来详细了解一下这些概念的区别。 一、定义和基本原理 机器学习&#xff1a;是一种让计算机通过数据学习和发现规律的方法。它使用各种…