Java基础系列8:Java的序列化与反序列化(修)

2023-11-05

一 简介

对象序列化就是把一个对象变成二进制的数据流的一种方法,通过对象序列化可以方便地实现对象的传输和存储。

把对象转换为字节序列的过程称为对象的序列化

把字节序列恢复为对象的过程称为对象的反序列化

对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。

对象序列化包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。

对象反序列化包括如下步骤:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象。

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。


二 Java中的序列化API

如果一个类的对象想被序列化,那么该对象所在的类必须实现java.io.Serializable接口,此接口的定义如下:

public interface Serializable{}

可以发现此接口并没有定义任何的方法,只是一个标识接口,表示一个类可以被序列化,如:

package cn.zifangsky.serializable;

import java.io.Serializable;

public class Person implements Serializable {
	private static final long serialVersionUID = 2651243789670519969L;

	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}

就像上面这个Person类一样,实现了序列化接口表明此类的对象可以经过二进制的数据流进行传输了。但是如果想要完成对象的输入和输出,还需要借助对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回

只有实现了Serializable或Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。


三 使用Serializable接口实现的实例

首先定义了一个实现了Serializable接口WebSite的实体类

package cn.zifangsky.serializable;

import java.io.Serializable;

public class WebSite implements Serializable {
	private static final long serialVersionUID = 1835573222135484360L;

	private String siteName;
	private String siteUrl;
	private String webMaster;

	public WebSite() {

	}

	public WebSite(String siteName, String siteUrl, String webMaster) {
		this.siteName = siteName;
		this.siteUrl = siteUrl;
		this.webMaster = webMaster;
	}

	public String getSiteName() {
		return siteName;
	}

	public void setSiteName(String siteName) {
		this.siteName = siteName;
	}

	public String getSiteUrl() {
		return siteUrl;
	}

	public void setSiteUrl(String siteUrl) {
		this.siteUrl = siteUrl;
	}

	public String getWebMaster() {
		return webMaster;
	}

	public void setWebMaster(String webMaster) {
		this.webMaster = webMaster;
	}

	public String toString() {
		return "WebSite [siteName=" + siteName + ", siteUrl=" + siteUrl + ", webMaster=" + webMaster + "]";
	}

}

然后进行序列化和反序列化测试:

package cn.zifangsky.serializable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class TestSerialize {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		TestSerialize.serializeWebSite();
		TestSerialize.deserializeWebSite();
	}
	
	/**
	 * 使用ObjectOutputStream 序列化WebSite
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 * 
	 * */
	public static void serializeWebSite() throws FileNotFoundException, IOException{
		WebSite webSite = new WebSite();
		webSite.setSiteName("zifangsky的个人博客");
		webSite.setSiteUrl("http://www.zifangsky.cn");
		webSite.setWebMaster("zifangsky");
		
		//序列化
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("C:/Users/Administrator/Desktop/test.txt")));
		objectOutputStream.writeObject(webSite);
		objectOutputStream.flush();
		objectOutputStream.close();
	}
	
	/**
	 * 使用ObjectInputStream 反序列化WebSite
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 * @throws ClassNotFoundException 
	 * 
	 * */
	public static void deserializeWebSite() throws FileNotFoundException, IOException, ClassNotFoundException{
		ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("C:/Users/Administrator/Desktop/test.txt")));
		//反序列化
		WebSite webSite = (WebSite) objectInputStream.readObject();
		objectInputStream.close();

		System.out.println(webSite);
	}
	
}

输出:

WebSite [siteName=zifangsky的个人博客, siteUrl=http://www.zifangsky.cn, webMaster=zifangsky]

四 使用Externalizable接口实现的实例

被Serializable接口声明的类的对象的内容都将被序列化,如果现在用户希望自己制定序列化的内容,则可以让一个类实现Externalizable接口,此接口的定义如下:

public interface Externalizable extends java.io.Serializable {
 
    void writeExternal(ObjectOutput out) throws IOException;

    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

其中,这两个方法的作用是:

i)writeExternal(ObjectOutput out) :在此方法中制定要保存的属性信息,对象序列化时调用

ii)readExternal(ObjectInput in) : 在此方法中读取被保存的信息,对象反序列化时调用


下面将以一个具体的实例来简单说明序列化和反序列化过程:

(1)实现了Externalizable接口的实体类:

package cn.zifangsky.serializable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class ExternalizableDemo implements Externalizable{
	private String name;
	static {
		System.out.println("调用静态代码块");
	}

	public ExternalizableDemo() {
		System.out.println("调用无参构造方法");
	}

	public ExternalizableDemo(String name) {
		this.name = name;
		System.out.println("调用有参构造方法");
	}

	public String getName() {
		return name;
	}

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

	public String toString() {
		return "ExternalizableDemo [name=" + name + "]";
	}
	
	/**
	 * ObjectOutputStream会调用writeExternal(ObjectOutput out))这个方法进行序列化
	 * */
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(name);
	}

	/**
	 * ObjectInputStream会调用readExternal(ObjectInput in)这个方法进行反序列化
	 * */
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		name = (String) in.readObject();	
	}

}

(2)然后进行序列化和反序列化测试:

package cn.zifangsky.serializable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class TestExternalizable {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		ExternalizableDemo demo = new ExternalizableDemo("hello");
		// 序列化
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(
				new FileOutputStream(new File("C:/Users/Administrator/Desktop/test2.txt")));
		objectOutputStream.writeObject(demo);
		objectOutputStream.flush();
		objectOutputStream.close();
		
		//反序列化
		ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("C:/Users/Administrator/Desktop/test2.txt")));
		ExternalizableDemo demo2 = (ExternalizableDemo) objectInputStream.readObject();
		objectInputStream.close();
		System.out.println(demo2);		
	}

}

输出:

调用静态代码块
调用有参构造方法
调用无参构造方法
ExternalizableDemo [name=hello]

注:

(1)使用Externalizable进行序列化时,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中

(2)Externalizable接口与Serializable接口实现序列化的区别:

区别 Serializable Externalizable
实现负责度 实现简单,Java对其有内建支持 实现负责,由开发人员自己完成
执行效率 所有对象由Java统一保存,性能较低 开发人员自己决定保存那些对象,可能造成速度提升
保存信息 保存时占用空间大 部分存储,可能造成空间减小

(3)一个对象被序列化后,到底哪些内容被保存了下来,是属性还是方法?

答:只有属性被序列化。因为每个对象都有相同的方法,但是每个对象的属性却不一定相同,因此对象保存的只有属性信息,那么同样道理在进行序列化操作时也只有属性被序列化


五 transient关键字

当使用Serializable接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化,那么就可以使用transient关键字进行声明

package cn.zifangsky.serializable;
import java.io.Serializable;

public class WebSite implements Serializable {
	private static final long serialVersionUID = 1835573222135484360L;

	private String siteName;
	private String siteUrl;
	private transient String webMaster;

	public WebSite(String siteName, String siteUrl, String webMaster) {
		this.siteName = siteName;
		this.siteUrl = siteUrl;
		this.webMaster = webMaster;
	}

	public String toString() {
		return "WebSite [siteName=" + siteName + ", siteUrl=" + siteUrl + ", webMaster=" + webMaster + "]";
	}

}

在上面这个类中,不希望webMaster这个属性被序列化,因此把它用transient关键字进行修饰,接下来就是序列化与反序列化测试:

package cn.zifangsky.serializable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class TestSerialize {

	public static void main(String[] args) throws FileNotFoundException,
			IOException, ClassNotFoundException {
		TestSerialize.serializeWebSite();
		TestSerialize.deserializeWebSite();
	}

	/**
	 * 使用ObjectOutputStream 序列化WebSite
	 * 
	 * @throws IOException
	 * @throws FileNotFoundException
	 * 
	 * */
	public static void serializeWebSite() throws FileNotFoundException,
			IOException {
		WebSite webSite = new WebSite("zifangsky的个人博客","http://www.zifangsky.cn","zifangsky");

		// 序列化
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(
				new FileOutputStream(new File(
						"C:/Users/Administrator/Desktop/test.txt")));
		objectOutputStream.writeObject(webSite);
		objectOutputStream.flush();
		objectOutputStream.close();
	}

	/**
	 * 使用ObjectInputStream 反序列化WebSite
	 * 
	 * @throws IOException
	 * @throws FileNotFoundException
	 * @throws ClassNotFoundException
	 * 
	 * */
	public static void deserializeWebSite() throws FileNotFoundException,
			IOException, ClassNotFoundException {
		ObjectInputStream objectInputStream = new ObjectInputStream(
				new FileInputStream(new File(
						"C:/Users/Administrator/Desktop/test.txt")));
		// 反序列化
		WebSite webSite = (WebSite) objectInputStream.readObject();
		objectInputStream.close();

		System.out.println(webSite);
	}

}

输出:

WebSite [siteName=zifangsky的个人博客, siteUrl=http://www.zifangsky.cn, webMaster=null]

从上面的输出可以看出,webMaster这个属性并没有被序列化


PS:

如果想要进一步了解,可以参考这篇文章:http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html


参考文章:

(1)http://www.cnblogs.com/xdp-gacl/p/3777987.html

(2)http://www.cnblogs.com/rollenholt/archive/2012/11/26/2789445.html


转载于:https://blog.51cto.com/983836259/1758247

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java基础系列8:Java的序列化与反序列化(修) 的相关文章

随机推荐

  • 【安全开发】C/C++安全编码规范

    C本质上是不安全的编程语言 例如如果不谨慎使用的话 其大多数标准的字符串库函数有可能被用来进行缓冲区攻击或者格式字符串攻击 但是 由于其灵活性 快速和相对容易掌握 它是一个广泛使用的编程语言 下面是针对开发安全的C语言程序的一些规范 1 1
  • windows11百度网盘下载,win11iso镜像百度云下载

    windows11百度网盘下载 win11iso镜像百度云 windows11百度网盘下载 win11iso镜像百度云链接 https pan baidu com s 1r96WDwsDURdrRt1nl Z0PQ 提取码 6666 上面是
  • 在vant组件下阻止手机自带键盘弹起

    废话 van field输入框伴随着小键盘的弹起 但是在已经定义了的van popup中 小键盘的弹起显得多余 尤其在地区 籍贯等的选择上 所以利用 focus 在获取焦点的时候进行阻止 代码
  • 局域网配置网站DNS服务器的安装

    小提示 要想成功部署DNS服务 运行Windows Serve 2003的计算机中必须拥有一个静态IP地址 只有这样才能让DNS客户端定位DNS服务器 另外如果希望该DNS服务器能够解析Internet上的域名 还需保证该DNS服务器能正常
  • STM32单片机示例:多个定时器级联使用

    文章目录 目的 基础说明 关键配置与代码 示例链接 目的 有些情况下会遇到单片机的定时器位数不够用 这时候可以使用低定时器级联的方式来处理 这里将对此做个示例说明 基础说明 这里说的定时器级联是指一个定时器正常计数工作 然后在发生溢出时发送
  • PYthon 转换HTML到Text纯文本

    今天项目需要将HTML转换为纯文本 去网上搜了一下 发现Python果然是神通广大 无所不能 方法是五花八门 拿今天亲自试的两个方法举例 以方便后人 方法一 1 安装nltk 可以去pipy装 注 需要依赖以下包 numpy PyYAML
  • AT24C02的使用说明和完整代码-51单片机

    AT24C02的使用说明和完整代码 51单片机 简述 at24c02为存储器芯片 可以使用单片机将数据存入其中 同时也可以任意读取 at24c02的原理及使用方法在其说明资料中已有充分的讲述 本篇仅对其使用的关键步骤进行罗列 以及说明一下具
  • MySQL主从复制的实现

    MySQL主从复制的理解图 MySQL Replication原理 主从复制 也称 AB 复制 允许将来自一个MySQL数据库服务器 主服务器 的数据复制到一个或多个MySQL数据库服务器 从服务器 复制是异步的 从站不需要永久连接以接收来
  • React中文文档之Lifting State Up

    Lifting State Up 提升状态 经常的 几个组件需要映射相同的数据改变 我们推荐提升共享的state状态到它们最近的公共祖先元素 让我们看看这是如何实现的 在这个章节 我们将创建一个温度计算器 计算在一个给定的温度 水是否会沸腾
  • 20200331 --【Python】-- selenium 登录练习

    学习 python 的第59天 模拟腾讯课堂 自动化 登录案例 简单 coding utf 8 Time 2020 3 31 10 52 Author admin Site File 腾讯课堂登录 py Software PyCharm i
  • Allegro显示飞线和隐藏飞线

    隐藏飞线 显示飞线
  • C++ 标准库中数据类型转换

    头文件引用
  • 延迟队列的方案设计

    延迟队列的实现方案 一 应用场景 什么是延时队列 顾名思义 首先它要具有队列的特性 再给它附加一个延迟消费队列消息的功能 也就是说可以指定队列中的消息在哪个时间点被消费 延时队列在项目中的应用场景是比较多的 尤其像电商类平台 1 订单成功后
  • 基于js利用经纬度进行两地的距离计算

    根据地球上两点之间的经纬度计算两点之间的直线距离 经纬度到距离的计算在通信工程中应用比较广泛 所以cosbeta通过搜索找到了一个js的计算脚本 其实是google map的计算脚本 应该算是比较准确了 做成了这个经纬度算距离的工具 今天有
  • 全到哭,阿里新产2023版Java架构核心宝典,涵盖Java进阶所有主流技术

    导言 什么是架构师 对于程序员来说 聊架构是一个永不过时的话题 实际上 每一家公司都有自己对架构师不同的定位 因为不同的公司 所处的阶段 业务模式以及应用场景都不一样 因此对架构师的要求不一样 所以定位也就不同 但是 无论如何 架构师除了优
  • STM32------TFTLCD原理

    目录 TFTLCD简介 一 知识点 1 TFTLCD驱动原理 ALINETEK TFTLCD模块介绍 2 2 8寸TFLCD模块特点 3 TFTLCD模块原理图 4 TFTLCD接口说明 5 并口驱动简介 6 ILI9341驱动时序 7 驱
  • 过滤器使用与bean注入

    1 web xml中各元素启动顺序 在项目启动时 监听器listener最先初始化 然后是过滤器filter 最后是servlet Spring监听器在启动时会读取spring配置文件 进行spring容器的初始化 springMVC的di
  • springBoot上传文件时MultipartFile报null 空 问题解决方法

    1 问题描述 之前用spring MVC 转成spring boot之后发现上传不能用 网上参考说是spring boot已经有CommonsMultipartResolver了 但是我的上传后台接收的还是null 2 第一种解决方法 加入
  • Jenkins中使用火线进行Android静态代码扫描

    背景 火线 是360Qtest测试团队在公司内部经过半年实践后向外推出的一款针对Android代码的静态扫描工具 本文主要介绍如何在Jenkins下植入火线扫描并实时查看结果的配置 环境配置 Jenkins 推荐使用最新的版本 本文使用的是
  • Java基础系列8:Java的序列化与反序列化(修)

    一 简介 对象序列化就是把一个对象变成二进制的数据流的一种方法 通过对象序列化可以方便地实现对象的传输和存储 把对象转换为字节序列的过程称为对象的序列化 把字节序列恢复为对象的过程称为对象的反序列化 对象的序列化主要有两种用途 1 把对象的