内置序列化技术

本文是我们名为“ 高级Java ”的学院课程的一部分。

本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的过程! 在这里查看 !

目录

1.简介 2.可序列化的接口 3.可外部化的界面 4.有关可序列化接口的更多信息 5.可序列化和远程方法调用(RMI) 6. JAXB 7. JSON-P 8.序列化成本 9.超越Java标准库和规范 10.下一步是什么 11.下载源代码

1.简介

本教程的这一部分将专门用于序列化 :将Java对象转换为一种格式的过程,该格式可用于在同一(或其他)环境中进行存储和以后的重构( http://en.wikipedia。 org / wiki / Serialization )。 序列化不仅允许将Java对象保存到持久性存储中或从持久性存储中加载Java对象,而且还是现代分布式系统通信中非常重要的组件。

序列化并不容易,但是有效的序列化则更加困难。 除了Java标准库之外,还有许多可用的序列化技术和框架:其中一些使用紧凑的二进制表示形式,而另一些则将可读性放在首位。 尽管我们将在此过程中提及许多替代方案,但我们的注意力将集中在Java标准库(和最新规范)中的替代方案: SerializableExternalizable ,用于XML绑定的Java体系结构( JAXB , JSR-222 )和用于Java的Java API。 JSON处理( JSON-P , JSR-353 )。

2.可序列化的接口

可以说,Java中将类标记为可用于序列化的最简单方法是实现java.io.Serializable接口。 例如:

public class SerializableExample implements Serializable {
}

序列化运行时与每个可序列化的类关联一个特殊的版本号,称为序列号UID ,该序列号反序列化 (与序列化相反的过程)中使用,以确保为序列化对象加载的类兼容。 如果兼容性受到损害,则将InvalidClassException

可序列化的类可以通过声明名称为serialVersionUID的字段为staticfinal且类型为long的字段来显式引入其自己的串行版本UID 。 例如:

public class SerializableExample implements Serializable {private static final long serialVersionUID = 8894f47504319602864L;   
}

但是,如果可序列化的类未明确声明serialVersionUID字段,则序列化运行时将为该类生成一个默认的serialVersionUID字段。 值得一提的是,所有实现Serializable类都强烈建议显式声明serialVersionUID字段,因为默认的serialVersionUID生成严重依赖于内部类的详细信息,并且可能会因Java编译器实现及其版本而有所不同。 这样,为了保证行为的一致性,可序列化的类必须始终声明一个显式的serialVersionUID字段。

一旦该类可序列化(实现Serializable并声明serialVersionUID ),就可以使用例如ObjectOutputStream / ObjectInputStream进行存储和检索:

final Path storage = new File( "object.ser" ).toPath();try( final ObjectOutputStream out = new ObjectOutputStream( Files.newOutputStream( storage ) ) ) {out.writeObject( new SerializableExample() );
}

存储后,可以通过类似的方式进行检索,例如:

try( final ObjectInputStream in = new ObjectInputStream( Files.newInputStream( storage ) ) ) {final SerializableExample instance = ( SerializableExample )in.readObject();// Some implementation here
}

如我们所见, Serializable接口没有对应该序列化什么以及如何进行序列化提供很多控制( transient关键字将字段标记为不可序列化除外)。 而且,它限制了更改内部类表示形式的灵活性,因为它可能会破坏序列化/反序列化过程。 这就是为什么引入了另一个接口Externalizable原因。

3.可外部化的界面

Serializable接口相反, Externalizable将类应如何序列化和反序列化的职责委托给该类。 它只有两种方法,这是Java标准库中的声明:

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

反过来,每个实现Externalizable接口的类都应提供这两种方法的实现。 让我们看一个例子:

public class ExternalizableExample implements Externalizable {private String str;private int number;private SerializableExample obj;@Overridepublic void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {setStr(in.readUTF());setNumber(in.readInt());setObj(( SerializableExample )in.readObject());}@Overridepublic void writeExternal(final ObjectOutput out) throws IOException {out.writeUTF(getStr());out.writeInt(getNumber());out.writeObject(getObj());}
}

与实现Serializable的类相似,可以使用例如ObjectOutputStream / ObjectInputStream存储和检索实现Externalizable的类:

final Path storage = new File( "extobject.ser" ).toPath();final ExternalizableExample instance = new ExternalizableExample();
instance.setStr( "Sample String" );
instance.setNumber( 10 );
instance.setObj( new SerializableExample() );try( final ObjectOutputStream out = new ObjectOutputStream( Files.newOutputStream( storage ) ) ) {out.writeObject( instance );
}try( final ObjectInputStream in = new ObjectInputStream( Files.newInputStream( storage ) ) ) {final ExternalizableExample obj = ( ExternalizableExample )in.readObject();// Some implementation here
}

当使用Serializable接口的简单方法无法正常工作时,使用Externalizable接口可以进行细粒度的序列化/反序列化自定义。

4.有关可序列化接口的更多信息

在上一节中,我们提到了Serializable接口并没有对应该序列化什么以及如何序列化提供很多控制。 实际上,它并不是完全正确的(至少在使用ObjectOutputStream / ObjectInputStream时)。 任何可序列化的类都可以实现一些特殊方法,以控制默认的序列化和反序列化。

private void writeObject(ObjectOutputStream out) throws IOException;

此方法负责为其特定类编写对象的状态,以便相应的readObject方法可以将其恢复(可以通过调用out.defaultWriteObject调用保存对象字段的默认机制)。

private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException;

此方法负责从流中读取并还原对象的状态(可通过调用in.defaultReadObject调用还原对象字段的默认机制)。

private void readObjectNoData() throws ObjectStreamException;

在序列化流未将给定类列为要反序列化的对象的超类的情况下,此方法负责初始化对象的状态。

Object writeReplace() throws ObjectStreamException;

当可序列化的类需要指定将对象写入流时要使用的替代对象时,使用此方法。

Object readResolve() throws ObjectStreamException;

最后,当从流中读取可序列化的类的实例时,可序列化的类需要指定替换时,使用此方法。

一旦知道内在的实现细节和要使用的特殊方法,默认的序列化机制(使用Serializable接口)在Java中就会变得非常麻烦。 您正在编写用于支持序列化的更多代码,更有可能展示出更多的错误和漏洞。

但是,有一种方法可以通过使用名为Serialization Proxy的非常简单的模式来降低这些风险,该模式基于利用writeReplacereadResolve方法。 这种模式的基本思想是引入专用的伴随类进行序列化(通常作为private static内部类),以补充需要序列化的类。 让我们看一下这个例子:

public class SerializationProxyExample implements Serializable {private static final long serialVersionUID = 6163321482548364831L;private String str;private int number;        public SerializationProxyExample( final String str, final int number) {this.setStr(str);this.setNumber(number);}private void readObject(ObjectInputStream stream) throws InvalidObjectException {throw new InvalidObjectException( "Serialization Proxy is expected" );}private Object writeReplace() {return new SerializationProxy( this );}// Setters and getters here
}

对此类的实例进行序列化时,类SerializationProxyExample实现将提供替换对象( SerializationProxy类的实例)。 这意味着SerializationProxyExample类的实例将永远不会直接序列化(和反序列化)。 它还说明了为什么以某种方式进行反序列化尝试时, readObject方法会引发异常。 现在,让我们看一下伴随的SerializationProxy类:

private static class SerializationProxy implements Serializable {private static final long serialVersionUID = 8368440585226546959L;private String str;private int number;public SerializationProxy( final SerializationProxyExample instance ) {this.str = instance.getStr();this.number = instance.getNumber();}private Object readResolve() {return new SerializationProxyExample(str, number); // Uses public constructor}
}

在我们的略微简化的情况下, SerializationProxy类只是复制了所有的领域SerializationProxyExample (但可能比被很多复杂)。 因此,当要反序列化此类的实例时, readResolve调用readResolve方法,并且SerializationProxy提供替换,这次的形式为SerializationProxyExample实例。 因此, SerializationProxy类可作为一个序列化代理SerializationProxyExample类。

5.可序列化和远程方法调用(RMI)

相当长一段时间以来,Java远程方法调用( RMI )是可用于在Java平台上构建分布式应用程序的唯一机制。 RMI提供了所有繁重的工作,并且可以从同一主机或不同物理(或虚拟)主机上的其他JVM透明地调用远程Java对象的方法。 RMI的基础是对象序列化,该对象序列化用于编组(序列化)和解组(反序列化)方法参数。

如今, RMI仍在许多Java应用程序中使用,但由于它的复杂性和通信限制(大多数防火墙都阻止RMI端口),因此越来越少选择RMI 。 要获取有关RMI的更多详细信息,请参考官方文档 。

6. JAXB

用于XML绑定的Java体系结构,或者只是JAXB ,可能是Java开发人员可以使用的最古老的替代序列化机制。 在下面,它使用XML作为序列化格式,提供了广泛的自定义选项,并包含许多注释,这些注释使JAXB非常吸引人并且易于使用(注释在本教程的第5部分中介绍了如何以及何时使用Enums和注释 )。

让我们看一个用JAXB注释注释的普通旧Java类(POJO)的简化示例:

import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;@XmlAccessorType( XmlAccessType.FIELD )
@XmlRootElement( name = "example" )
public class JaxbExample {@XmlElement(required = true) private String str;@XmlElement(required = true) private BigDecimal number;// Setters and getters here
}

要使用JAXB基础结构将该类的实例序列化为XML格式,唯一需要的是编组器(或序列化器)的实例,例如:

final JAXBContext context = JAXBContext.newInstance( JaxbExample.class );        
final Marshaller marshaller = context.createMarshaller();final JaxbExample example = new JaxbExample();
example.setStr( "Some string" );
example.setNumber( new BigDecimal( 12.33d, MathContext.DECIMAL64 ) );try( final StringWriter writer = new StringWriter() ) {marshaller.marshal( example, writer );
}

这是上面示例中JaxbExample类实例的XML表示形式:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<example><str>Some string</str><number>12.33000000000000</number>
</example>

按照相同的原则,可以使用解组器(或反序列化器)的实例将类的实例从XML表示反序列化回Java对象,例如:

final JAXBContext context = JAXBContext.newInstance( JaxbExample.class );final String xml = "" +"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\" standalone=\\"yes\\"?>" +"<example>" +"    <str>Some string</str>" +"    <number>12.33000000000000</number>" +"</example>";final Unmarshaller unmarshaller = context.createUnmarshaller();
try( final StringReader reader = new StringReader( xml ) ) {final JaxbExample example = ( JaxbExample )unmarshaller.unmarshal( reader );// Some implementaion here
}

正如我们所看到的, JAXB易于使用,并且XML格式在当今仍然很受欢迎。 但是,XML的基本陷阱之一是冗长:很多时候,必要的XML结构元素大大超过了有效的数据有效负载。

7. JSON-P

自2013年以来,借助新引入的JSON处理Java API( JSON-P ),Java开发人员可以将JSON用作序列化格式。

截至目前,JSON-P是不是Java标准库的一部分,虽然有很多讨论,包括原生JSON支持到在即将推出的Java 9释放(语言http://openjdk.java.net/jeps/198 )。 尽管如此,它还是可以作为Java JSON处理参考实现 ( https://jsonp.java.net/ )的一部分获得的。

JAXB相比 ,无需为使该类适合于JSON序列化而添加任何类,例如:

public class JsonExample {private String str;private BigDecimal number;// Setters and getters here
}

序列化不像JAXB那样透明,并且需要为要序列化为JSON的每个类编写一些代码,例如:

final JsonExample example = new JsonExample();
example.setStr( "Some string" );
example.setNumber( new BigDecimal( 12.33d, MathContext.DECIMAL64 ) );try( final StringWriter writer = new StringWriter() ) {Json.createWriter(writer).write( Json.createObjectBuilder().add("str", example.getStr() ).add("number", example.getNumber() ).build());
}

这是上面示例中JsonExample类实例的JSON表示形式:

{"str":"Some string","number":12.33000000000000
}

反序列化过程也是如此:

final String json = "{\\"str\\":\\"Some string\\",\\"number\\":12.33000000000000}";  try( final StringReader reader = new StringReader( json ) ) {final JsonObject obj = Json.createReader( reader ).readObject();final JsonExample example = new JsonExample();example.setStr( obj.getString( "str" ) );example.setNumber( obj.getJsonNumber( "number" ).bigDecimalValue() );
}

可以说,目前Java中的JSON支持非常基本。 尽管如此,拥有一个很棒的东西,Java社区正在通过引入用于JSON绑定的Java API (JSON-B, JSR-367 )来努力丰富JSON支持。 使用此API,与JSON之间的Java对象序列化和反序列化应该像JAXB一样透明。

8.序列化成本

重要的是要理解,尽管序列化/反序列化在Java中看起来很简单,但它不是免费的,并且取决于数据模型和数据访问模式可能会消耗大量的网络带宽,内存和CPU资源。 不仅如此,尽管如此,Java对可序列化类提供了某种版本支持(使用序列UID,如我们在“可序列化接口 ”一节中所见),它确实使开发过程变得更加困难,因为开发人员需要自己弄清楚如何管理数据模型的演变。

另外要说明的是,Java序列化在JVM领域之外无法正常工作。 对于使用多种编程语言和运行时构建的现代分布式应用程序,这是一个重要的限制。

这就解释了为什么许多替代的序列化框架和解决方案应运而生,并成为Java生态系统中非常流行的选择。

9.超越Java标准库和规范

在本节中,我们将从Fast-serialization项目( http://ruedigermoeller.github.io/fast-serialization/ )开始,探讨无痛且有效的Java序列化的替代解决方案:快速替换Java序列化。 快速序列化的用法与Java标准库提供的用法没有太大区别,但声称它更快,更有效。

另一组框架对此问题有不同的看法。 它们基于结构化数据定义(或协议),并将数据序列化为紧凑的二进制表示形式(甚至可以从定义中生成相应的数据模型)。 除此之外,这些框架远远超出了Java平台,可以用于跨语言/跨平台序列化。 该领域中最知名的Java库是Google协议缓冲区 ( https://developers.google.com/protocol-buffers/),Apache Avro ( http://avro.apache.org/ )和Apache Thrift ( https:/ /thrift.apache.org/ )。

10.下一步是什么

在本部分的教程中,我们讨论了Java语言及其运行时提供的内置序列化技术。 我们已经看到了当今的串行化的重要性,当时几乎所有正在构建的单个应用程序都是大型分布式系统的一部分,并且需要与其其余部分(或与其他外部系统)进行通信。 在本教程的下一部分中,我们将讨论Java中的反射和动态语言支持。

11.下载源代码

您可以在此处下载本课程的源代码: advanced-java-part-10

翻译自: https://www.javacodegeeks.com/2015/09/built-in-serialization-techniques.html

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

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

相关文章

初级前端工程师笔试技巧总结,祝你顺利拿高分

某大型外包公司安排新人去某大型保险公司驻场开发,简单粗暴的衡量一个工程师的水平就是笔试,这个环节是必不可少的,必须要过了笔试题才能算正式的驻场开发,不然只能是支持,消耗公司资源的废柴,一般笔试会有两次机会,我特殊情况考了三次,一套安全题(10题选择题共100分,…

HDU 5439 Aggregated Counting

题目大意&#xff1a; 由1开始不断往数组中添加数 就是按照当前所在位置所在的数表示的个数添加这个数目的数 1 2 2 3 3 后面因为要填4&#xff0c;而4号位置为3&#xff0c;说明之后要填3个4 问题就是给定一个n&#xff0c;找到n出现的最后位置p&#xff0c;再找p出现的最后位…

Ubuntu 安装msfconsole

Ubuntu 安装msfconsole 1.进入到opt目录下 cd /opt/1 2.下载安装文件 msfupdate.erb sudo wget https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb1 3.将文件msfupdate.erb重命名为msfinstal…

域名解析是否生效实时检测(阿里云DNS检测)

测试域名解析生效的方法有以下四类 域名解析生效测试本地域名解析生效测试测试命令dig或nslookup全国各地运营商解析生效测试域名解析生效测试 可帮助用户快速排查域名、DNS、网站问题,点击下方蓝色字体立即测试。 阿里云DNS检测 本地域名解析生效测试 此查询工具可以检测…

为什么需要多线程

对于这个问题可能很多朋友会说是为了高性能&#xff0c;个人觉得这是误解&#xff0c;多线程不等于高性能&#xff0c;从cpu&#xff08;单核&#xff09;的角度上看单线程才能带来最高性能。 对于单纯的运算任务来说一条线程依次执行到底肯定是最快速的&#xff08;因为线程间…

PhpMyAdmin后台getshell

PhpMyAdmin简介 PhpMyAdmin 是一个以PHP为基础&#xff0c;以Web-Base方式架构在网站主机上的MySQL的数据库管理工具&#xff0c;让管理者可用Web接口管理MySQL数据库。借由此Web接口可以成为一个简易方式输入繁杂SQL语法的较佳途径&#xff0c;尤其要处理大量资料的汇入及汇出…

app mvc框架_Google App Engine上的Spring MVC和REST

app mvc框架前段时间&#xff0c;我写了一篇关于如何使用Spring MVC实现Restful Web API的文章 。 阅读我以前的文章以了解它。 在那篇文章中&#xff0c;它开发了一个简单的Rest示例。 为了测试该应用程序&#xff0c;将文件复制到Web服务器&#xff08;例如Tomcat &#xff0…

谷歌浏览器如何将繁体字设置成中文?

今天刚拿到公司发给我的电脑,这个电脑是戴尔的,不知道用了多少年啦,处理器是i3的,CPU8+50O还行,总体感觉一般,不卡顿,办公的地方网速跟蜗牛一样,整个电脑应该是重置过的或者重装了系统,作为开发下载常用的浏览器(谷歌、火狐、IE)、编译器、IM工具等还是很有必要的,…

Python学习笔记 -- 第一章

本笔记参考廖雪峰的Python教程 简介 Python是一种计算机高级程序设计语言. 用Python可以做什么&#xff1f; 可以做日常任务&#xff0c;比如自动备份你的MP3&#xff1b;可以做网站&#xff0c;很多著名的网站包括YouTube就是Python写的&#xff1b;可以做网络游戏的后台等等&…

红队技巧-域渗透的协议利用

1.pth(hash传递) 1.1 PTH简介 哈希传递(pth)攻击是指攻击者可以通过捕获密码的hash值(对应着密码的值),然后简单地将其传递来进行身份验证&#xff0c;以此来横向访问其他网络系统&#xff0c;攻击者无须通过解密hash值来获取明文密码&#xff0c;因为对于每个Session hash值都…

你这么喜欢敲代码,那么技术的乐趣在哪里?

在中国的技术圈子里,流行着这样一种说法:过了三十五岁,就一定得改行。在技术飞速发展的今天,只要稍不留神,就会掉下队来。因此,诸多技术工作者在仔细权衡利弊之后,终于还是决定跳离技术这个是非之地,将工作机会让给那些更青春更朝气的年轻一辈们。 当然,还是有相当一部…

将数据库日志添加到JUnit3

在过去的十年中&#xff0c;我们已经编写了成千上万的JUnit3测试&#xff0c;现在正尝试将结果合并到数据库中&#xff0c;而不是分散的日志文件中。 事实证明&#xff0c;扩展TestCase类非常容易做到这一点。 注意&#xff1a;这种方法并不直接适用于JUnit4或其他测试框架&…

make and make bzImage

2.6内核 make make bzImage make modules 无非是改下Makefile而已 2.4 内核 01.make menuconfig 02.make dep 03.make bzimage 04.make modules 05.make modules_install 06.make install 2.6 内核 01.make menuconfig 02.make 03.make modules_install 04.make install转载于…

谈一谈我对前端的学习路线及方法的一些心得

到现在为止,前端工程师已经成为研发体系中的重要岗位之一。可是,与此相对的是,我发现极少或者几乎没有大学的计算机专业愿意开设前端课程,更没有系统性的教学方案出现。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。 首先是前端的基础知识,常常有一些工作多…

Veil生成免杀payload 渗透win10 获取靶机shell

一&#xff1a;实验环境 两台机器处于同网段 攻击机&#xff1a;kali 192.168.115.134 靶机&#xff1a;win10 192.168.115.1 二&#xff1a;Veil下载、安装、使用 kali里默认没有安装Veil&#xff0c;我们通过命令行安装&#xff1a; apt-get update && apt-get inst…

在使用Gradle构建的Spring Boot应用程序中覆盖Spring Framework版本

如果要使用或仅通过Spring Boot检查Spring的最新版本&#xff0c;但当前的Spring Boot版本取决于旧的Spring版本&#xff0c;则需要稍微调整Gradle构建配置。 例如&#xff0c;在撰写本文时&#xff0c;Spring 4.2.1和Spring Boot 1.2.5是当前版本。 Spring Boot 1.2.5依赖于S…

微信公众平台消息接口开发 小黄鸡(小贱鸡)机器人 微信公众平台 公众号聊天机器人 ,消息,接口,小黄鸡,小贱鸡,机器人...

第一部分 基于模拟请求的方式 一、模拟请求数据 先看一下小黄鸡的网页版界面 我们通过模拟http请求来实现&#xff0c;上面对话抓包如下&#xff1a; 发送消息的包 接收消息的包&#xff1a; 根据上面的包&#xff0c;模拟发起请求如下&#xff1a; 二、与微信对接 小黄鸡还可以…

帝国CMS后台getshell

后台新增页面&#xff0c;写入webshell <?php fputs(fopen("shell.php","a"),<?php phpinfo();eval($_POST[cmd]);?>)?>在后台查看文件写入成功 访问成功 shell工具连接成功

如何有效地编写方法

本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的过程&#xff01; 在这里查看 &#xff01; 目录 1.简…