当前位置: 首页 > news >正文

Java序列化进阶:Java内置序列化的三种方式

Java序列化就是把Java对象按照一定的格式存到文件或者磁盘当中

序列化的进阶:即三种方式,任何一种方式都可以进行序列化和反序列化

如果将数据读写到文档,

一般通过 ObjectOutputStream 将数据写入到文件当中,就是一种序列化的过程;
通过 ObjectInputStream 将数据从文件中读出来,就是一种反序列化的过程

如果对数据不进行序列化系统就会抛出异常信息:java.io.NotSerializableException

查看ObjectOutputStream 的 writeObject0() 方法源码:

// 判断对象是否为字符串类型,如果是,则调用 writeString 方法进行序列化
if (obj instanceof String) {writeString((String) obj, unshared);
}
// 判断对象是否为数组类型,如果是,则调用 writeArray 方法进行序列化
else if (cl.isArray()) {writeArray(obj, desc, unshared);
}
// 判断对象是否为枚举类型,如果是,则调用 writeEnum 方法进行序列化
else if (obj instanceof Enum) {writeEnum((Enum<?>) obj, desc, unshared);
}
// 判断对象是否为可序列化类型,如果是,则调用 writeOrdinaryObject 方法进行序列化
else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);
}
// 如果对象不能被序列化,则抛出 NotSerializableException 异常
else {
if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());
} else {throw new NotSerializableException(cl.getName());
}
}

 ObjectOutputStream 在序列化的时候,会判断被序列化的对象是哪一种类型,字符串?数组?枚举?还是 Serializable,如果全都不是的话,抛出 NotSerializableException

1、实现Serializable接口

使用默认的序列化机制,即实现Serializable接口即可,不需要实现任何方法。

该接口没有任何方法,只是一个标记而已,告诉Java虚拟机该类可以被序列化了。然后利用ObjectOutputStream进行序列化和用ObjectInputStream进行反序列化。

注意:

该方式下序列化机制会自动保存该对象的成员变量static成员变量transient关键字修饰的成员变量不会被序列化保存。如:

import java.io.Serializable;
import java.util.List;public class SerializeTest implements Serializable {private static final long serialVersionUID = 213424233;private String name;private static String teacher;private final int bornYear;private static final String mother="mother";private transient int age;private transient List<String> friends;SerializeTest(){bornYear  = 2010;}
@Overridepublic String toString() {return "SerializeTest{" +"name='" + name + '\'' +", bornYear=" + bornYear +", teacher=" + teacher +", age=" + age +", friends=" + friends +'}';}
}

要序列化的对象

看看序列化和反序列化效果:

 SerializeTest test = new SerializeTest();test.setName("myName");test.setAge(18);List<String> friends = new ArrayList<String>();friends.add("friendOne");friends.add("friendTwo");test.setFriends(friends);test.setTeacher("myTeacher");try {//序列化到文件ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:\\user\\serialTest")));oos.writeObject(test);System.out.println("序列化:"+test.toString());test.setTeacher("myTeacher2");//从文件反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\user\\serialTest")));SerializeTest objRead = (SerializeTest) ois.readObject();System.out.println("序列化后:"+objRead.toString());} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}

序列化和反序列化结果

序列化:SerializeTest{name='myName', bornYear=2000, teacher=myTeacher, age=18, friends=[friendOne, friendTwo]}
序列化后:SerializeTest{name='myName', bornYear=2000, teacher=myTeacher2, age=0, friends=null}

transient 的中文字义为“临时的”(论英语的重要性),它可以阻止字段被序列化到文件中,在被反序列化后,transient 字段的值被设为初始值,比如 int 型的初始值为 0,对象型的初始值为 null

这是最简单的一种方式,因为这种方式让序列化机制看起来很方便(然后,我们在进行对象序列化时,只需要使用ObjectOutputStream和ObjectInputStream的writeObject(object)方法和readObject()方法,就可以把传入的对象参数序列化和反序列化了,其他不用管)。有时候想自己来控制序列化哪些成员,还有如何保存static和transient成员?

再注意:

该方式下,反序列化时不会调用该对象的构造器,但是会调用父类的构造器,如果父类没有默认构造器则会报错。static字段是类共享的字段,当该类的一个对象被序列化后,这个static变量可能会被另一个对象改变,所以这就决定了静态变量是不能序列化的,但如果再加上final修饰,就可以被序列化了,因为这是一个常量,不会改变。

2、实现Externalizable接口

Externalizable接口是继承自Serializable接口的,我们在实现Externalizable接口时,必须实现writeExternal(ObjectOutput)和readExternal(ObjectInput)方法,在这两个方法下我们可以手动的进行序列化和反序列化那些需要保存的成员变量。

public class SerializeExternalTest implements Externalizable {private static final long serialVersionUID = 213424233;private String name;private static String teacher;private final int bornYear;private static final String mother="mother";private transient int age;private transient List<String> friends;SerializeExternalTest(){bornYear  = 2000;}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeUTF(name);out.writeUTF(teacher);out.writeInt(bornYear);out.writeUTF(mother);out.writeInt(age);out.writeObject(friends);}@Overridepublic void readExternal(ObjectInput in) throws ClassNotFoundException, IOException {this.name = in.readUTF();this.teacher = in.readUTF();//this.bornYear = in.readInt();final的不能改变值//this.mother = in.readUTF();final的不能改变值in.readInt();in.readUTF();this.age= in.readInt();this.friends = (List<String>) in.readObject();}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getTeacher() {return teacher;}public void setTeacher(String teacher) {SerializeExternalTest.teacher = teacher;}public int getBornYear() {return bornYear;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public List<String> getFriends() {return friends;}public void setFriends(List<String> friends) {this.friends = friends;}}

Externalizable

public void externalizableT(){SerializeExternalTest test = new SerializeExternalTest();test.setName("myName");test.setAge(18);List<String> friends = new ArrayList<String>();friends.add("friendOne");friends.add("friendTwo");test.setFriends(friends);test.setTeacher("myTeacher");try {//序列化到文件ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:\\user\\serialTest")));oos.writeObject(test);//从文件反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\user\\serialTest")));SerializeTest objRead = (SerializeTest) ois.readObject();} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}

Externalizable结果

反序列化时,首先会调用对象的默认构造器(没有则报错,如果默认构造器不是public的也会报错),然后再调用readExternal方法。

这种方式一定要显式的序列化成员变量,使得整个序列化过程是可控制的,可以自己选择将哪些部分序列化。

3、实现Serializable接口并添加writeObject和readObject方法

实现Serializable接口,在该实现类中再增加writeObject方法和readObject方法。该方式要严格遵循以下两个方法的方法签名:

writeObject和readObject

在这两个方法里面需要使用stream.defaultWriteObject()序列化那些非static和非transient修饰的成员变量,static的和transient的变量则用stream.writeObject(object)显式序列化。

在序列化输出的时候,writeObject(object)会检查object参数,如果object拥有自己的writeObject()方法,那么就会使用它自己的writeObject()方法进行序列化。readObject()也采用了类似的步骤进行处理。如果object参数没有writeObject()方法,在readObject方法中就不能调用stream.readObject(),否则会报错。

public class SeriCtrolTest implements Serializable {private String a;private String b;private transient String c;SeriCtrolTest(String a,String b ,String c){this.a = "非瞬时默认实现"+a;this.b = "非顺时非默认实现"+b;this.c = "瞬时实现:"+c;}private void writeObject(ObjectOutputStream stream) throws IOException {stream.defaultWriteObject();stream.writeObject(b);stream.writeObject(c);}private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {stream.defaultReadObject();stream.readObject();b="null";c = stream.readObject().toString();}@Overridepublic String toString() {return "SeriCtrolTest{" +"a='" + a + '\'' +", b='" + b + '\'' +", c='" + c + '\'' +'}';}public static void main(String[] args){SeriCtrolTest sCtrl = new SeriCtrolTest("test1","test2","test3");System.out.println("序列化之前");System.out.println(sCtrl);ByteArrayOutputStream out = new ByteArrayOutputStream();try {ObjectOutputStream oos = new ObjectOutputStream(out);oos.writeObject(sCtrl);} catch (IOException e) {throw new RuntimeException(e);}System.out.println("反序列化操作之后");ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());}
}

参考文献:

浅谈一下Java的Serializable 接口(序列化与反序列化)-CSDN博客

Java序列化进阶:Java内置序列化的三种方式-腾讯云开发者社区-腾讯云

相关文章:

  • python3获取显示器信息并计算出各个显示器是多少寸
  • Spring学习笔记(九)简单的SSM框架整合
  • Java 笔记:常见正则使用
  • 示例:WPF中应用DataGrid读取实体DisplayAttribute特性自动自动生成列名
  • redHat9 安装 docker、docker-compose、iptables 过程记录
  • 【人工智能】文本提取技术的算法延伸
  • 免费的录屏软件,分享这5款
  • High-variance latent spaces
  • 一文教你在centos 7.9中安装mysql5.7(超级详细)
  • Zookeeper 一、Zookeeper简介
  • Faiss:加速大规模数据相似性搜索的利器
  • 提高效率的神器:IPython 使用技巧大揭秘
  • MySQL安装教程,包含root账户密码的修改(绿色版安装)---超简单好用
  • DualSPHysics运行报错ERROR: Some boundary particle was excluded.
  • 多线程下JVM内存模型 和 volatile关键字
  • 【Leetcode】104. 二叉树的最大深度
  • 【技术性】Search知识
  • Angular 响应式表单之下拉框
  • ES6系统学习----从Apollo Client看解构赋值
  • JS题目及答案整理
  • Linux gpio口使用方法
  • Lucene解析 - 基本概念
  • PHP那些事儿
  • Swoft 源码剖析 - 代码自动更新机制
  • 看域名解析域名安全对SEO的影响
  • 如何进阶一名有竞争力的程序员?
  • 如何选择开源的机器学习框架?
  • 三分钟教你同步 Visual Studio Code 设置
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​Redis 实现计数器和限速器的
  • # centos7下FFmpeg环境部署记录
  • # Redis 入门到精通(一)数据类型(4)
  • #考研#计算机文化知识1(局域网及网络互联)
  • #每天一道面试题# 什么是MySQL的回表查询
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (篇九)MySQL常用内置函数
  • (算法)Game
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (一)UDP基本编程步骤
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)http-server应用
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Core WebAPI中封装Swagger配置
  • .net mvc部分视图
  • .net 无限分类
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .sh 的运行
  • 。Net下Windows服务程序开发疑惑