java自定义外部接口_如何使用可外部化的接口在Java中自定义序列化

java自定义外部接口

在上一篇文章“用示例介绍的有关Java序列化的一切”中 ,我解释了如何使用以下方法序列化/反序列化一个对象
Serializable接口,还说明了如何使用writeObjectreadObject方法自定义序列化过程。

Java序列化过程的缺点

但是这些自定义是不够的,因为JVM可以完全控制序列化过程,而这些自定义逻辑只是默认序列化过程的补充。 我们仍然必须通过从writeObjectObjectInputStream.defaultReadObject()调用ObjectOutputStream.defaultWriteObject()ObjectInputStream.defaultReadObject()来使用默认的序列化逻辑。
readObject方法。 而且,如果不调用这些默认方法,我们的对象将不会被序列化/反序列化。

默认的序列化过程是完全递归的。 因此,每当我们尝试序列化一个对象时,序列化过程就会尝试使用我们的类( staticstatic除外)对所有字段(原始和引用)进行序列化。
transient场)。 这使得序列化过程非常缓慢。

现在,假设我们有一个对象,其中包含许多字段,由于某些原因,我们不想序列化这些字段(这些字段将始终分配有默认值)。 使用默认的序列化过程,我们将必须使所有这些字段都是瞬态的,但是它仍然不会高效,因为将进行大量检查以查看这些字段是否为瞬态的。

因此,如我们所见,使用默认序列化过程有很多弊端,例如:

  1. 序列化的定制是不够的,因为JVM可以完全控制序列化过程,而我们的定制逻辑只是默认序列化过程的补充。
  2. 默认序列化过程是完全递归且缓慢的。
  3. 为了不对字段进行序列化,我们必须将其声明为瞬态,大量瞬态字段将再次使过程变慢。
  4. 我们无法控制如何对字段进行序列化和反序列化。
  5. 默认序列化过程在创建对象时不会调用构造函数,因此它无法调用构造函数提供的初始化逻辑。

什么是外部化和外部化接口

正如我们在上面看到的那样,默认的Java序列化效率不高。 我们可以通过使用Externalizable接口而不是
Serializable接口。

我们可以通过实现
可外部化的接口并覆盖它的方法writeExternal()
readExternal() 。 但是使用这种方法,我们将无法从JVM获得任何类型的默认序列化逻辑,而是由我们来提供完整的序列化和反序列化逻辑。

因此,非常仔细地对测试这些方法进行编码非常必要,因为这可能会破坏序列化过程。 但是,如果正确实现,与默认序列化过程相比,外部化过程非常快。

我们将以下面的Employee类对象为例进行说明:

 // Using Externalizable, complete serialization/deserialization logic becomes our responsibility,  // We need to tell what to serialize using writeExternal() method and what to deserialize using readExternal(),  // We can even serialize/deserialize static and transient variables,  // With implementation of writeExternal() and readExternal(), methods writeObject() and readObject() becomes redundant and they do not get called.  Employee class implements Externalizable { // This serialVersionUID field is necessary for Serializable as well as Externalizable to provide version control, // Compiler will provide this field if we do not provide it which might change if we modify class structure of our class, and we will get InvalidClassException, // If we provide a value to this field and do not change it, serialization-deserialization will not fail if we change our class structure. private static final long serialVersionUID = 2L; private String firstName; private transient String lastName; // Using Externalizable, we can even serialize/deserialize transient variables, so declaring fields transient becomes unnecessary. private int age; private static String department; // Using Externalizable, we can even serialize/deserialize static variables according to our need. // Mandatory to have to make our class Externalizable // When an Externalizable object is reconstructed, the object is created using public no-arg constructor before the readExternal method is called. // If a public no-arg constructor is not present then a InvalidClassException is thrown at runtime. public Employee() { } // All-arg constructor to create objects manually public Employee(String firstName, String lastName, int age, String department) { this .firstName = firstName; this .lastName = lastName; this .age = age; Employee.department = department; validateAge(); } private void validateAge() { System.out.println( "Validating age." ); if (age < 18 || age > 70 ) { throw new IllegalArgumentException( "Not a valid age to create an employee" ); } } @Override // We need to tell what to serialize in writeExternal() method public void writeExternal(ObjectOutput out) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); out.writeUTF(firstName); out.writeUTF(lastName); out.writeInt(age); out.writeUTF(department); } @Override // We need to tell what to deserialize in readExternal() method // The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal public void readExternal(ObjectInput in) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); firstName = in.readUTF(); lastName = in.readUTF(); age = in.readInt(); department = in.readUTF(); validateAge(); } @Override public String toString() { return String.format( "Employee {firstName='%s', lastName='%s', age='%s', department='%s'}" , firstName, lastName, age, department); } // Custom serialization logic, It will be called only if we have implemented Serializable instead of Externalizable. private void writeObject(ObjectOutputStream oos) throws IOException { System.out.println( "Custom serialization logic invoked." ); } // Custom deserialization logic, It will be called only if we have implemented Serializable instead of Externalizable. private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println( "Custom deserialization logic invoked." ); }  } 

序列化如何与可外部化接口一起使用

如上面在示例Employee类中所看到的,我们可以通过实现Externalizable接口并覆盖其方法writeExternal()readExternal()来编写自己的序列化逻辑。

通过调用DataOutput的原始值对象或调用ObjectOutput的writeObject方法对象,字符串和数组,对象可以实现writeExternal方法来保存其内容。

该对象可以实现readExternal方法来恢复其内容,方法是为原始类型调用DataInput的方法,为对象,字符串和数组调用readObject的方法。 readExternal方法必须按与writeExternal相同的顺序和相同的类型读取值。

 // We need to tell what fields to serialize in writeExternal() method  public void writeExternal(ObjectOutput out) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); out.writeUTF(firstName); out.writeUTF(lastName); out.writeInt(age); out.writeUTF(department);  }  // We need to tell what fields to deserialize in readExternal() method  // The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal  public void readExternal(ObjectInput in) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); firstName = in.readUTF(); lastName = in.readUTF(); age = in.readInt(); department = in.readUTF(); validateAge();  } 

要将对象序列化和反序列化为文件,我们需要遵循与Serializable示例相同的过程,这意味着调用
如以下代码所示,完成ObjectOutputStream.writeObject()ObjectInputStream.readObject()

 public class ExternalizableExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Employee empObj = new Employee( "Shanti" , "Sharma" , 25 , "IT" ); System.out.println( "Object before serialization => " + empObj.toString()); // Serialization serialize(empObj); // Deserialization Employee deserializedEmpObj = deserialize(); System.out.println( "Object after deserialization => " + deserializedEmpObj.toString()); } // Serialization code static void serialize(Employee empObj) throws IOException { try (FileOutputStream fos = new FileOutputStream( "data.obj" ); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(empObj); } } // Deserialization code static Employee deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream( "data.obj" ); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Employee) ois.readObject(); } }  } 

Externalizable接口是Serializable的子接口,即
Externalizable extends Serializable 。 因此,如果我们实现Externalizable接口并覆盖其writeExternal()
然后,将使用readExternal()方法优先于这些方法,而不是由JVM提供的默认序列化机制。 这些方法取代了writeObjectreadObject方法的自定义实现,因此,如果我们还提供writeObject()readObject() ,则将忽略它们。

在序列化过程中,将针对要序列化的每个对象的Externalizable接口进行测试。 如果对象支持Externalizable,则调用writeExternal方法。 如果对象不支持Externalizable并且实现了Serializable,则使用ObjectOutputStream保存该对象。

重建Externalizable对象时,将使用公共的无参数构造函数创建一个实例,然后调用readExternal方法。 可序列化的对象通过从ObjectInputStream读取来恢复。

  1. 重建Externizable对象时,在调用readExternal方法之前,使用公共的无参数构造函数创建对象。 如果不存在公共的无参数构造函数,则在运行时引发InvalidClassException。
  2. 使用Externalizable,我们甚至可以序列化/反序列化瞬态变量,因此无需声明瞬态字段。
  3. 使用Externalizable,我们甚至可以根据需要对静态变量进行序列化/反序列化。

Externalizable实例可以通过Serializable接口中记录的writeReplace和readResolve方法指定替换对象。

Java 序列化还可以用于深度克隆对象 。 Java克隆是Java社区中最有争议的话题,它的确有其缺点,但是在对象完全满足Java克隆的强制条件之前,它仍然是创建对象副本的最流行和最简单的方法。 我在长达3篇文章的Java克隆系列中详细介绍了克隆 ,其中包括Java克隆和克隆类型(浅和深)等文章, 并带有示例 , Java克隆–复制构造器与克隆 , Java克隆–甚至复制构造器都不一样如果您想了解更多有关克隆的知识,请充分阅读它们。

可外部化与可序列化之间的差异

让我们列出Java中Externalizable接口和Serializable接口之间的主要区别。

您可以在此找到本文的完整源代码。
Github存储库 ,请随时提供宝贵的反馈。

翻译自: https://www.javacodegeeks.com/2019/08/customize-serialization-java-using-externalizable-interface.html

java自定义外部接口

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

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

相关文章

python训练营朋友圈留言_用Python发一个高逼格的朋友圈【附代码】

今天二胖要给大家介绍一个Python库&#xff1a;PIL(Python Image Library)下面我们用一个实际的例子看看50行python代码可以做什么神奇的事情这是二胖发的一个朋友圈切图前是一张图切图后就是九张图啦成功霸屏除了可以处理规整的正方形图片还可以处理非规则的图片比如下面这张宽…

vim 编辑器的快捷键

文章目录命令终端界面滚屏命令终端页签切换缓存区切换/文件切换分割窗口/打开新窗口切换窗口移动/旋转/移出窗口关闭窗口调整窗口大小底行命令模式下的编辑快捷键vim 很多指令或者快捷键是大小写敏感。命令终端界面滚屏 快捷键说明Fn ←向上滚屏到开始处Fn →向下滚屏到末尾…

关于二叉堆(优先队列)的其他操作及其应用

【0】README 0.1&#xff09;本文总结于 数据结构与算法分析&#xff1b;源代码均为原创&#xff0c; 旨在了解到我们学习了优先队列后&#xff0c;还能干些什么东西出来&#xff0c; 增加学习的interest&#xff1b; 0.2&#xff09;以下列出了 关于二叉堆&#xff08;优先队…

gradle junit5_JUnit 5和Selenium –使用Gradle,JUnit 5和Jupiter Selenium设置项目

gradle junit5Selenium是一组支持浏览器自动化的工具和库&#xff0c;主要用于Web应用程序测试。 Selenium的组件之一是Selenium WebDriver&#xff0c;它提供客户端库&#xff0c;JSON有线协议&#xff08;与浏览器驱动程序进行通信的协议&#xff09;和浏览器驱动程序。 Sele…

ubuntu 两块硬盘挂载不上_win10 轉 Ubuntu

目前用了win10兩三年。發現越來越慢&#xff0c;況且已習慣mac OS&#xff0c;所以想用自己的機子來裝個雙系統Linux&#xff0c;慢慢的將win的東西都轉到Ubuntu上。已清空一個磁盤300G,打算就是在這300G裡裝一個Ubuntu&#xff0c;不知道是否夠用&#xff08;雖然很想裝在三星…

包+类导入+静态导入+类放入包中+包作用域

【0】README 0.1&#xff09;本文转自 core java volume 1&#xff0c; 旨在理清 包和类导入的相关知识&#xff1b; 【1】 包 1.1&#xff09; java 允许使用包将类组织起来&#xff0c;包可以方便组织代码&#xff0c;并将自己的代码与别人提供的代码库分开管理&#xff1b…

selenium自动化测试_使用Selenium自动化测试处理多个浏览器选项卡

selenium自动化测试使用Selenium进行自动化测试一直是将萌芽的自动化测试人员培养为专业人员的生命线。 Selenium是开源的&#xff0c;在全球范围内被广泛采用。 结果&#xff0c;您会得到社区的大力支持。 提供了与Selenium绑定的不同语言的多种框架。 因此&#xff0c;您已经…

qt和c#怎么选_请问目前做windows桌面应用程序,MFC、QT、C#哪个更好?

回答问题之前&#xff0c;先装个逼——没有主导过生命周期三年以上的桌面软件项目的&#xff0c;闭嘴。你连一个桌面软件项目的生命周期都没经历过&#xff0c;你凭什么做技术选型&#xff1f;凭信仰吗&#xff1f;装逼结束&#xff0c;正文开始。首先&#xff0c;非主流技术和…

java 白皮书的关键术语

【0】README 0.1&#xff09; 本文转自 core java volume 1&#xff0c;仅供了解&#xff0c;所谓爱屋及乌嘛&#xff1b; 0.2&#xff09; java的设计者编写了颇有影响力的白皮书&#xff0c;用来解释设计的初衷以及完成的情况&#xff0c;并发布了一个摘要&#xff1b;【1】…

当集合a为空集时a的取值范围_高中数学必修一第一章集合分节练习和章末测试题含答案[1] 2...

高中数学必修1 第一章 集合 分节练习和章末综合测试题含答案1 集合的含义与表示1、下列各组对象能否组成一个集合&#xff1f;(1)接近于0的数的全体&#xff1b; (2)2的近似值的全体&#xff1b; (3)平面上到点O 的距离等于1的点的全体&#xff1b; (4)正三角形的全体&#xff…

spring jpa 流式_从响应式Spring Data存储库流式传输实时更新

spring jpa 流式这篇文章详细介绍了从数据库到对该数据感兴趣的任何其他组件进行流更新的幼稚实现。 更准确地说&#xff0c;如何更改Spring Data R2DBC存储库以向相关订阅者发出事件。 对R2DBC和Spring的一点背景知识将对这篇文章有所帮助。 我以前的著作《 使用 Microsoft S…

弹窗页面交互_UI进阶知识-信息提交类弹窗该如何设计?

原文作者&#xff1a;风筝KK 信息提交类弹窗大家应该都比较熟悉&#xff0c;和其他弹窗的区别在于他有输入、选择等操作&#xff0c;比如我们常见的输入验证码、留言回复、充值转账、任务设置等。看上去设计都比较简单&#xff0c;但是当你验收时就会发现问题&#xff0c;为什么…

selenium并行_如何在不同的浏览器中设置Selenium网格以并行执行

selenium并行到目前为止&#xff0c;Selenium是最常用的Web自动化测试工具。 如此受欢迎的原因之一是Selenium的自动跨浏览器测试功能。 Selenium自动化测试可以帮助您在所有主要浏览器&#xff0c;所有主要操作系统甚至移动设备浏览器上进行测试。 您可以在所有功能测试中获得…

java 发展简史

【0】README 0.1&#xff09; 本文转自 core java volume 1&#xff0c;仅供了解Java 的发展历史&#xff0c;它的前世今生&#xff0c;所谓知己知彼&#xff0c;百战不殆&#xff08;just a joke&#xff09; &#xff1b; 【1】java 发展简史 1.1&#xff09;java的历史要…

axios代理跨域 cli4_跨域本质及解决办法

1、什么是跨域&#xff1f;2、如何解决&#xff1f;跨域是前端所独有的&#xff0c;后端不存在跨域问题。是浏览器的一种安全保护手段&#xff0c;为了防止别人抓取、篡改你的网站数据信息。遵循同源策略、同协议&#xff08;http&#xff09;、同域名、同端口&#xff0c;少一…

如何使用eclemma插件_如何集成和使用EclEmma插件来获得良好的Junit覆盖率

如何使用eclemma插件你好朋友&#xff0c; 如果编写好的代码很重要&#xff0c;那么编写覆盖所有业务逻辑的优良Junit测试用例也同样重要。通过编写覆盖业务逻辑的Junit测试用例&#xff0c;我们实际上确保代码的每种方法都能正常工作按照预期进行&#xff0c;因此减少了在软…

Java 相关术语

【0】README 0.1&#xff09; 本文转自 core java volume 1&#xff0c;仅供了解Java 的相关术语&#xff0c;包括像JDK、JRE等 &#xff1b;而且我感觉&#xff0c;了解了这些过后&#xff0c;你会更懂 Java&#xff0c; 更热爱它&#xff1b; 【2】Java术语 2.1&#xff09;…

一般试卷的纸张大小是多少_pdf试卷怎么打印在A3纸上

一般的PDF试卷的纸张大小都是A4大小&#xff0c;没有现成A3大小Word文档的试卷时&#xff0c;需要将PDF试卷转换Word文档之后进行排版&#xff0c;如何实现这一操作呢&#xff1f;请接着往下学习吧~一、将试卷的PDF格式转换为Word1.进入PDF快转官网&#xff0c;点击下载按钮下载…

设置 JDK环境变量(Windows)

【0】README 0.1&#xff09; 本文转自 core java volume 1&#xff0c;旨在说明如何设置 JDK环境变量&#xff0c;以及为什么要设置的问题&#xff1b;【1】JDK目录树 Attention&#xff09;就Java 而言&#xff0c; docs 和 src 是两个最有用的子目录&#xff1a;因为 docs …

光流法测试代码_高效的企业测试-工作流和代码质量(4/6)

光流法测试代码本文的这一部分将讨论在开发过程中拥有有效工作流程的影响&#xff0c;以及适当的测试代码质量如何使我们能够创建可维护的测试&#xff0c;尤其是对于复杂项目。 开发工作流程和管道 编程是一项流程活动&#xff0c;我们开发人员应该对保持工作流程高效和缩短…