浅析Java Object Serialization与 Hadoop 序列化

科多大数据带你来看Java Object Serialization与 Hadoop 序列化。

一、Java Object Serialization

1什么是序列化(Serialization)

序列化是指将结构化对象转化为字节流,用于在网络上传输或者写到磁盘永久存储的过程。反序列化是序列化的反向过程,指将字节流转回结构化对象。简单的理解就是对象转换为字节流用来传输和保存,字节流转换为对象将对象恢复成原来的状态。

2.序列化(Serialization)的作用

(1)一种持久化机制,把的内存中的对象状态保存到一个文件中或者数据库。

(2)一种通信机制,用套接字在网络上传送对象。

(3)Java远程方法调用(RMI)需要调用对象时,

3.实现了Serializable接口的对象的序列化

在java.io包中,接口Serialization用来作为实现对象串行化的工具 ,只有实现了Serialization的类的对象才可以被序列化。 Serializable接口中没有任何的方法,当一个类声明要实现Serializable接口时,只是表明该类参加序列化协议,而不需要实现任何特殊的方法。

要序列化一个对象,首先要创建OutputStream对象,然后将其封装在一个ObjectOutputStream对象内。此时,调用writeObject()方法将对象序列化并发送给OutputStream。在反序列化时,需要将一个InputStream封装在ObjectInputStream内,然后调用readObject(),得到的结果是一个Object对象,需要进行转型得到最后所需的对象。需要注意的是,在对一个Serializable对象进行反序列化的过程中,没有调用任何构造器,包括缺省的构造器,整个对象都是通过从InputStream中取得数据恢复过来的。对象序列化是面向字节的,因此采用InputStream和OutputStream层次结构。

对Student序列化

package cn.test.serializable;

/**

* 序列化对象实现Serializable接口

*/

import java.io.Serializable;

public class Student implements Serializable {

private String id;

private String name;

public Student (String id,String name){

this.id=id;

this.name=name;

}

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

序列化

package cn.test.serializable;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectOutputStream;

/**

* 序列化

*/

public class TestSerializable {

public static void main(String[] args) {

// TODO Auto-generated method stub

Student stu=new Student("201441413110","yang");

try {

FileOutputStream out=new FileOutputStream("d:\\student");//创建OutputStream对象

ObjectOutputStream ob=new ObjectOutputStream(out);//封装在一个ObjectOutputStream对象内

ob.writeObject(stu);//调用writeObject()方法将对象序列化并发送给OutputStream,保存在d:\\student中

ob.close();

out.close();

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

反序列化

package cn.test.serializable;

/**

* 反序列化

*/

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.ObjectInputStream;

public class TestDeserializable {

public static void main(String[] args) {

// TODO Auto-generated method stub

FileInputStream in;

Student stu;

try {

in = new FileInputStream("d:\\student");

ObjectInputStream ob=new ObjectInputStream(in);//InputStream封装在ObjectInputStream内

stu=(Student) ob.readObject();//调用readObject(),得到的结果是一个Object对象,需要进行转型得到最后所需的对象.

ob.close();

in.close();

System.out.println(stu.getId());

System.out.println(stu.getName());

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

序列化机制写到流中的数据有

1.声明和标记,比如声明使用了序列化协议、序列化协议版本、声明这是一个新的对象、声明这里开始一个新Class、结束标记等。

2.对象所属的类 ,类的长度,

3.SerialVersionUID, 序列化ID,如果没有指定,则会由算法随机生成一个8byte的ID。

4.所有的非transient和非static的属性的个数以及名称。名称长度,属性的值。

这只是简单对象序列化后写到流中的数据。如果是复杂对象序列化就会包含更多的信息。比如引用其他对象作为成员变量,这是会将引用的对象也进行序列化。还有序列化时,只对对象的状态进行保存,而不管对象的方法;当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化。

二、Hadoop 序列化

hadoop在节点间的内部通讯使用的是RPC,RPC协议把消息翻译成二进制字节流发送到远程节点,远程节点再通过反序列化把二进制流转成原始的信息。

Hadoop使用自己的序列化格式Writable,它绝对紧凑、高速,但不太容易用Java以外的语言进行拓展和使用。

1.Writable接口

Writable接口定义了两个方法:一个将其状态写到DataOutput二进制流,另一个从DataInput二进制流读取其状态:

实现代码

package Hadoop.writable;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Writable;

/**

* hadoop 序列化与反序列化

*

*/

public class Serialize {

public static byte[] serialize(Writable writable) throws IOException{

ByteArrayOutputStream out = new ByteArrayOutputStream();

DataOutputStream dataout = new DataOutputStream(out);

try {

writable.write(dataout);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

dataout.close();

}

return out.toByteArray();

}

public static byte[] deserialize(Writable writable ,byte[] bytes) throws IOException{

ByteArrayInputStream in = new ByteArrayInputStream(bytes);

DataInputStream datain = new DataInputStream(in);

try {

writable.readFields(datain);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

finally{

datain.close();

}

return bytes;

}

public static void main(String[] args) throws IOException {

// TODO Auto-generated method stub

IntWritable intwritable = new IntWritable(20);

IntWritable newwriatble =new IntWritable();

byte[] bytes=Serialize.serialize(intwritable);

deserialize(newwriatble,bytes);

System.out.println(newwriatble.get());

}

}

2.Hadoop序列化机制中还包含另外几个重要的接口:WritableComparable、RawComparator 和 WritableComparator

WritableComparable提供类型比较的能力,继承自Writable接口和Comparable接口,其中Comparable进行 类型比较。ByteWritable、IntWritable、DoubleWritable等java基本类型对应的Writable类型,都继承自 WritableComparable。

WritableComparable接口

package org.apache.hadoop.io;

import org.apache.hadoop.classification.InterfaceAudience.Public;

import org.apache.hadoop.classification.InterfaceStability.Stable;

import org.apache.hadoop.io.Writable;

@Public

@Stable

public interface WritableComparable extends Writable, Comparable {

}

对于MapReduce来说,类型的比较非常重要,因为中间有个基于键的排序阶段。Hadoop提供了一个具有高效比较能力的RawComparator接口。

RawComparator接口

package org.apache.hadoop.io;

import java.util.Comparator;

import org.apache.hadoop.classification.InterfaceAudience.Public;

import org.apache.hadoop.classification.InterfaceStability.Stable;

@Public

@Stable

public interface RawComparator extends Comparator {

int compare(byte[] arg0, int arg1, int arg2, byte[] arg3, int arg4, int arg5);

}

该接口允许其实现直接比较数据流中的记录,无须先把数据流反序列化为对象,这样避免了新建对象而外的开销。

WritableComparator 是对继承自WritableComparable类的RawComparator类的一个通用实现。提供两个主要功能。1,提供对原始的compare()方法的一个默认实现,该方法能够反序列化将在流中进行比较的对象,并调用对象的compare()方法。2,它充当的是RawComparator实例的工厂,例如为了获得IntWritable的comparator,可以直接调用

RawComparatorcomparator=WritableComparator.get(IntWritable.class);

下面是《Hadoop权威指南》的例子

package cn.serialization;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.RawComparator;

import org.apache.hadoop.io.Writable;

import org.apache.hadoop.io.WritableComparator;

public class TestWritable {

//序列化

public static byte[] serialize(Writable writable) throws IOException{

ByteArrayOutputStream out = new ByteArrayOutputStream();

DataOutputStream dataOut= new DataOutputStream(out);

writable.write(dataOut);

dataOut.close();

return out.toByteArray();

}

//反序列化

public static byte[] deserialize(Writable writable, byte[] bytes) throws IOException{

ByteArrayInputStream in = new ByteArrayInputStream(bytes);

DataInputStream dataIn = new DataInputStream(in);

writable.readFields(dataIn);

dataIn.close();

return bytes;

}

public static void main(String[] args) throws IOException {

@SuppressWarnings("unchecked")

RawComparator comparator = WritableComparator.get(IntWritable.class);

IntWritable w1 = new IntWritable(163);

IntWritable w2 = new IntWritable(67);

byte[] b1 = serialize(w1);

byte[] b2 = serialize(w2);

System.out.println(b1);

System.out.println(b2);

System.out.println(comparator.compare(w1, w2));//WritableComparator的 compare(),w1>w2 -> 1;w1 -1 ; w1=w2 -> 0

System.out.println(comparator.compare(b1,0,b1.length,b2,0,b2.length));

}

}

三,Hadoop 序列化框架

尽管大多数MapReduce程序使用Writable类型的key,value,但是不是所有MapReduce API 强制使用。事实上,可以使用任何类型,只要能有一种机制将每个类型进行类型与二进制的来回转换。为此Hadoop提供了一个序列化框架来支持,他们在org.apache.hadoop.io.serializer包中,WritableSerialization类是对Writable类型的Serialization实现。

WritableSerialization类

public class WritableSerialization extends Configured implements Serialization {...}

Serializer接口

定义了open()接口,序列化接口,close接口

public interface Serializer {

void open(OutputStream arg0) throws IOException;

void serialize(T arg0) throws IOException;

void close() throws IOException;

}

Deserializer接口

定义了open()接口,反序列化接口,close接口

public interface Deserializer {

void open(InputStream arg0) throws IOException;

T deserialize(T arg0) throws IOException;

void close() throws IOException;

}

Serialization接口

定义了一组接口,判断是否支持输入的类,根据输入的类给出序列化接口和反序列化接口

public interface Serialization {

boolean accept(Class arg0);

Serializer getSerializer(Class arg0);

Deserializer getDeserializer(Class arg0);

}

尽管这可以方便我们在MapReduce使用Java类型,比如String,Integer。但是这还是不如Writable高效。

Hadoop中不使用java Object Serialization的原因

1.Java Object Serialization不够精简,就如本文前面提到的Java Object Serialization序列化后的字节流会包含许多信息,比如类名与对象等。

2.对象在序列化中只存引用,引用可以出现的位置很随机,既可以在序列化的对象前,也可以在其后面,这样就对随机访问和排序造成影响,一旦出错,整个后面的序列化就会全部错误,Writable支持随机访问和排序。因为流中的记录是彼此独立的。

3.Java序列化每次反序列化都要重新创建对象,内存消耗大,而Writable是可以重用的。

以上内容参考了网上资料和《Hadoop权威指南》,请指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • JAVA序列化机制的深入研究 对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整...
    时待吾阅读 10,734评论 0 24
  • 序列化从头说 在面向对象程序设计中,类是个很重要的概念。所谓“类”,可以将它想像成建筑图纸,而对象就是根据图纸盖的...
    起个什么呢称呢阅读 1,570评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 1. 看看电影 2. 听听歌 3. 翻翻朋友圈 4. 听听网络课程 5. 整理护肤知识
    你说我听ai阅读 228评论 0 0