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,一经查实,立即删除!

相关文章

GitHub 上传项目保姆级教程

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

记录一次查询优化

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

《使用Gin框架构建分布式应用》阅读笔记:p212-p233

《用Gin框架构建分布式应用》学习第12天&#xff0c;p212-p233总结&#xff0c;总22页。 一、技术总结 1.JavaScript知识点 (1)class、method (2)function, arrow function, (3)fetch() (4)Promise, then() 2.bootstrap 第5章主要涉及前端技术的运用&#xff0c;作为后…

NVR录像机汇聚管理EasyNVR多个NVR同时管理融合汇聚管理系统

流媒体视频融合与汇聚管理系统能够实现对各类模块化服务的统一管理和配置&#xff0c;整合、管理和共享应用服务&#xff0c;并通过标准接口为业务平台和其他第三方平台提供便捷的数据调用功能。为确保该系统的成功实施和稳定运行&#xff0c;在建设方案中除了考虑基础的架构设…

服务器的免密登录和文件传输

在天文学研究中&#xff0c;通常会采用ssh登录服务器&#xff0c;把复杂的计算交给服务器&#xff0c;但是如果你没有进行额外的配置&#xff0c;那么登录服务器&#xff0c;以及和服务器进行文件传输&#xff0c;每次都要输入账号和密码&#xff0c;比较不方便&#xff0c;Win…

【Nas】X-Doc:jellyfin“该客户端与媒体不兼容,服务器未发送兼容的媒体格式”问题解决方案

【Nas】X-Doc&#xff1a;jellyfin“该客户端与媒体不兼容&#xff0c;服务器未发送兼容的媒体格式”问题解决方案 当使用Jellyfin播放视频时出现“该客户端与媒体不兼容&#xff0c;服务器未发送兼容的媒体格式”&#xff0c;这是与硬件解码和ffmpeg设置有关系&#xff0c;具体…

沪深A股上市公司数据报告分析

数据分析报 目录 数据分析报告 1.引言 1.1 背景介绍 1.2 报告目的 1.3 报告范围 1.4 关键术语定义 2. 数据收集与预处理 2.1 数据来源概述 2.2 数据收集过程 2.3 数据预处理步骤 3. 数据可视化 3.1分析地区对公司数量的影响 3.2分析行业分类是否影响公…

Mysql当中的各种log

一、MySQL日志文件类型 重做日志&#xff08;redo log&#xff09;回滚日志&#xff08;undo log&#xff09;二进制日志&#xff08;binlog&#xff09;错误日志&#xff08;errorlog&#xff09;慢查询日志&#xff08;slow query log&#xff09;一般查询日志&#xff08;g…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-29

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-29 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-29目录1. Does your LLM truly unlearn? An embarrassingly simple approach to recover unlearned knowledge摘要研究背景问题…

C1.【C++ Cont】准备

目录 1.平台 2.Dev C的使用方法 1.新建项目 2.几个常用按钮 3.修改字体 3.第一个C程序:打印Hello World! 4.注 1.平台 Dev C,VS2022 2.Dev C的使用方法 1.新建项目 选择Console Application控制台应用程序,C项目 项目的后缀dev 默认下创建了一个main.cpp,cpp为c源程…

uniapp编译多端项目App、小程序,input框键盘输入后

项目场景&#xff1a; uniapp编译后的小程序端&#xff0c;app端 在一个输入框 输入消息后&#xff0c;点击键盘上的操作按钮之后键盘不被收起&#xff0c;点击其他发送按钮时&#xff0c;键盘也不被收起。 问题描述 在编译后的app上普通的事件绑定&#xff0c;tap,click在发…