加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

数据处理---Java数据处理之序列化

发布时间:2020-12-14 02:18:32 所属栏目:大数据 来源:网络整理
导读:既然上一篇已经引出了序列化的问题,这里我们就来专门说说这个。 序列化的目的: ??????? 实现简单的持久化 ,说到持久化,我们很多时候想到的是保存到数据库中,没错,对象也可以保存到数据库中或者是文件中,这样可以保存对象的状态等等信息。 ??????? 实

既然上一篇已经引出了序列化的问题,这里我们就来专门说说这个。

序列化的目的:

??????? 实现简单的持久化,说到持久化,我们很多时候想到的是保存到数据库中,没错,对象也可以保存到数据库中或者是文件中,这样可以保存对象的状态等等信息。

??????? 实现对象的远程传输,从而完成远程调用。在RMI中,服务端与客户端之间传递的Java对象必须是可序列化的对象,不可序列化的对象不能在对象流中进行传递,否则会抛出异常(上篇我们已经看到了这点)。在对象序列化过程中,支持把对象编码以及将通过它们可访问到的对象编码变成字节流;同时也支持流中对象图形的互补重构造。序列化用于轻型持久性和借助于套接字或远程方法调用(RMI)进行的通信。

序列化的实现:

???????? 一般来说序列化包括两部分:序列化和反序列化 。序列化是首先将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。ObjectOutputStream中的序列化过程与字节流连接,包括对象类 型和版本信息。反序列化时,JVM用头信息生成对象实例,然后将对象字节流中的数据复制到对象数据成员中。

??????? 由于Java提供了良好的默认支持,实现基本的对象序列化是件比较简单的事。待序列化的Java类只需要实现Serializable接口即可。Serializable仅是一个标记接口,并不包含任何需要实现的具体方法。实现该接口只是为了声明该Java类的对象是可以被序列化的。实际的序列化和反序列化工作是通过ObjectOuputStream和ObjectInputStream来完成的。ObjectOutputStream的writeObject方法可以把一个Java对象写入到流中,ObjectInputStream的readObject方法可以从流中读取一个Java对象。在写入和读取的时候,虽然用的参数或返回值是单个对象,但实际上操纵的是一个对象图,包括该对象所引用的其它对象,以及这些对象所引用的另外的对象。Java会自动帮你遍历对象图并逐个序列化。除了对象之外,Java中的基本类型和数组也是可以通过 ObjectOutputStream和ObjectInputStream来序列化的。

??????? ? 贴上JDK(1.7) ObjectOutputStream.java 中 writeObject0方法代码片段,readObject0()方法类似。

????????? // remaining cases
??????????? if (obj instanceof String) {
??????????????? writeString((String) obj,unshared);
??????????? } else if (cl.isArray()) {
??????????????? writeArray(obj,desc,unshared);
??????????? } else if (obj instanceof Enum) {
??????????????? writeEnum((Enum) obj,unshared);
??????????? } else if (obj instanceof Serializable) {
??????????????? writeOrdinaryObject(obj,unshared);
??????????? } else {
??????????????? if (extendedDebugInfo) {
??????????????????? throw new NotSerializableException(
??????????????????????? cl.getName() + "n" + debugInfoStack.toString());
??????????????? } else {
??????????????????? throw new NotSerializableException(cl.getName());
??????????????? }
??????????? }
??????? } finally {
??????????? depth--;
??????????? bout.setBlockDataMode(oldMode);
??????? }

????? 实际使用的时候,可以这样做,例如序列化到文件:

FileOutputStream fos = new FileOutputStream("t.tmp");

ObjectOutputStream oos = new ObjectOutputStream(fos);

oos.writeObject(new Date());

oos.close();

在默认的序列化实现中,Java对象中的非静态和非瞬时域都会被包括进来,而与域的可见性声明没有关系。对于不需要序列化的域可以声明为瞬时的,即使用transient关键词。另外一种做法是添加一个serialPersistentFields 域来声明序列化时要包含的域。虽然Serializable只是一个标记接口,但它其实是包含有不少隐含的要求。下面的代码给出了 serialPersistentFields的声明示例,即只有firstName这个域是要被序列化的。

private static final ObjectStreamField[] serialPersistentFields = {
??? new ObjectStreamField("firstName",String.class)
};?

??? 如果有事有特别的要求,可以自定义方法来实现序列化。可以在需要序列化的类中实现writeObject和对应的 readObject方法。这两个方法属于前面提到的序列化机制的隐含契约的一部分。在通过ObjectOutputStream的 writeObject方法写入对象的时候,如果这个对象的类中定义了writeObject方法,就会调用该方法,并把当前 ObjectOutputStream对象作为参数传递进去。writeObject方法中一般会包含自定义的序列化逻辑,比如在写入之前修改域的值,或是写入额外的数据等。对于writeObject中添加的逻辑,在对应的readObject中都需要反转过来,与之对应。

???? 另外也可以通过java.io.Externalizable(which extends java.io.Serializable)来实现。实现过程也不复杂,可以试试。

???? 当然一个完整的序列化还包括很多问题,比如:

  • ???? 版本(不同版本是否兼容,serialVersionUID)
  • ??? 对象重建(有些字段没有序列化,反序列化后部分域没有初始化)
  • ???? 数据加密(数据既然可以传输,就要保证安全性)
  • ? ?? 数据压缩(序列化可能数据比较大,为保证效率,可以对数据进行压缩)

??????? 对于这些问题,有兴趣的可以自己试试找到答案。其实说了这么多,在这里还是想引出来再搭建Hive文件格式的博文的时候说到的Avro之类的对象序列化的方法。


Protocol Buffer????

摘抄一段网络上的介绍:

Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 等多种语言的 API。

?相比较一些其他的XML技术而言,该技术的一个明显特点就是更加节省空间(以二进制流存储)、速度更快以及更加灵活。

?? ? 通常,编写一个protocol buffers应用需要经历如下三步:

?? ? 1、定义消息格式文件,最好以proto作为后缀名

?? ? 2、使用Google提供的protocol buffers编译器来生成代码文件,一般为.h和.cc文件,主要是对消息格式以特定的语言方式描述

?? ? 3、使用protocol buffers库提供的API来编写应用程序?

Avro

?????? 是Apache的一个项目,它是一个数据序列化系统。支持:

???????? 丰富数据结构

???????? 紧缩的快速的二进制数据格式

???????? 存储持久数据的容器文件

???????? 远程过程调用

???????? 和动态语言的简单集成。读写数据文件或者使用RPC的时候不需要代码生成(Code Generation).代码生成作为一个优化选项,仅仅在有意义的实现类型语言统计时候使用。

??????? 与ProtocolBuffers(出自Google) Thrift(出自Facebook)相比,Avro有几个主要的不同:

??????? 动态类型,Avro不需要代码生成,这点有利于构建通用的数据处理系统和语言

??????? Untagged data,数据在读的时候就可以了解其模式,相应地在数据中需要更少的类型信息,而系列化的数据更少。

??????? 不需要手动分配字段ID。通过在数据中的字段名来解决数据模式

Maven中使用
<dependency>
? <groupId>org.apache.avro</groupId>
? <artifactId>avro</artifactId>
? <version>1.7.7</version>
</dependency>
?????

需要代码生成的时候,可以Avro Maven plugin
<plugin>
? <groupId>org.apache.avro</groupId>
? <artifactId>avro-maven-plugin</artifactId>
? <version>1.7.7</version>
? <executions>
??? <execution>
????? <phase>generate-sources</phase>
????? <goals>
??????? <goal>schema</goal>
????? </goals>
????? <configuration>
??????? <sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
??????? <outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
????? </configuration>
??? </execution>
? </executions>
</plugin>
<plugin>
? <groupId>org.apache.maven.plugins</groupId>
? <artifactId>maven-compiler-plugin</artifactId>
? <configuration>
??? <source>1.6</source>
??? <target>1.6</target>
? </configuration>
</plugin>
???? ?

使用Avro步骤
1.? 定义schema

Avro schemas 是使用JSON来定义的. Schemas是有原子类型(null,boolean,int,long,float,double,bytes,and string) 和复杂类型(record,enum,array,map,union,and fixed)组成的.下面是一个简单的例子user.avsc:
{"namespace": "example.avro",
?"type": "record",
?"name": "User",
?"fields": [
???? {"name": "name","type": "string"},
???? {"name": "favorite_number",? "type": ["int","null"]},
???? {"name": "favorite_color","type": ["string","null"]}
?]
}
一个schema文件只能定义单个模式。一个定义最少必须包括类型("type": "record")名称("name": "User")和字段(favorite_number,and favorite_color)。上面的例子包括了namespace ("namespace": "example.avro"),它的全名类似example.avro.User

生成代码(如果使用Avro Maven plugin会自动产生代码,无需手动执行):
java -jar /path/to/avro-tools-1.7.7.jar compile schema <schema file> <destination>

下面试试怎么使用吧:
User user1 = new User();
user1.setName("Alyssa");
user1.setFavoriteNumber(256);
// Leave favorite color null

// Alternate constructor
User user2 = new User("Ben",7,"red");

// Construct via builder
User user3 = User.newBuilder()
???????????? .setName("Charlie")
???????????? .setFavoriteColor("blue")
???????????? .setFavoriteNumber(null)
???????????? .build();
?????? ?

// Serialize user1,user2 and user3 to disk 序列化到磁盘
DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
dataFileWriter.create(user1.getSchema(),new File("users.avro"));
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
???? ?



// Deserialize Users from disk? 从磁盘读出数据
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = new DataFileReader<User>(file,userDatumReader);
User user = null;
while (dataFileReader.hasNext()) {
// Reuse user object by passing it to next(). This saves us from
// allocating and garbage collecting many objects for files with
// many items.
user = dataFileReader.next(user);
System.out.println(user);
}

输出类似:
{"name": "Alyssa","favorite_number": 256,"favorite_color": null}
{"name": "Ben","favorite_number": 7,"favorite_color": "red"}
{"name": "Charlie","favorite_number": null,"favorite_color": "blue"}
?

??? 其他的序列化方式还有:Thrift (facebook贡献到apache)?,Hadoop自己实现的Writable

??? 说一千道一万,序列化有很多实现方式,都各有特色,但是很多思想或者说思路是相同的。

??? 今天,你序列化了没有?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读