深入淺析Java Object Serialization與 Hadoop 序列化
一,Java Object Serialization
1,什么是序列化(Serialization)
序列化是指將結(jié)構(gòu)化對(duì)象轉(zhuǎn)化為字節(jié)流以便在網(wǎng)絡(luò)上傳輸或者寫到磁盤永久存儲(chǔ)的過(guò)程。反序列化指將字節(jié)流轉(zhuǎn)回結(jié)構(gòu)化對(duì)象的逆過(guò)程。簡(jiǎn)單的理解就是對(duì)象轉(zhuǎn)換為字節(jié)流用來(lái)傳輸和保存,字節(jié)流轉(zhuǎn)換為對(duì)象將對(duì)象恢復(fù)成原來(lái)的狀態(tài)。
2,序列化(Serialization)的作用
(1)一種持久化機(jī)制,把的內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件中或者數(shù)據(jù)庫(kù)。
(2)一種通信機(jī)制,用套接字在網(wǎng)絡(luò)上傳送對(duì)象。
(3)Java遠(yuǎn)程方法調(diào)用(RMI)需要調(diào)用對(duì)象時(shí),
3,實(shí)現(xiàn)了Serializable接口的對(duì)象的序列化
在java.io包中,接口Serialization用來(lái)作為實(shí)現(xiàn)對(duì)象串行化的工具 ,只有實(shí)現(xiàn)了Serialization的類的對(duì)象才可以被序列化。 Serializable接口中沒(méi)有任何的方法,當(dāng)一個(gè)類聲明要實(shí)現(xiàn)Serializable接口時(shí),只是表明該類參加序列化協(xié)議,而不需要實(shí)現(xiàn)任何特殊的方法。
要序列化一個(gè)對(duì)象,首先要?jiǎng)?chuàng)建OutputStream對(duì)象,然后將其封裝在一個(gè)ObjectOutputStream對(duì)象內(nèi)。此時(shí),調(diào)用writeObject()方法將對(duì)象序列化并發(fā)送給OutputStream。在反序列化時(shí),需要將一個(gè)InputStream封裝在ObjectInputStream內(nèi),然后調(diào)用readObject(),得到的結(jié)果是一個(gè)Object對(duì)象,需要進(jìn)行轉(zhuǎn)型得到最后所需的對(duì)象。需要注意的是,在對(duì)一個(gè)Serializable對(duì)象進(jìn)行反序列化的過(guò)程中,沒(méi)有調(diào)用任何構(gòu)造器,包括缺省的構(gòu)造器,整個(gè)對(duì)象都是通過(guò)從InputStream中取得數(shù)據(jù)恢復(fù)過(guò)來(lái)的。對(duì)象序列化是面向字節(jié)的,因此采用InputStream和OutputStream層次結(jié)構(gòu)。
對(duì)Student序列化
package cn.test.serializable;
/**
* 序列化對(duì)象實(shí)現(xiàn)Serializable接口
* @author Young
* created on 2017-5-25
*/
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;
/**
* 序列化
* @author Young
* created on 2017-5-25
*/
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");//創(chuàng)建OutputStream對(duì)象
ObjectOutputStream ob=new ObjectOutputStream(out);//封裝在一個(gè)ObjectOutputStream對(duì)象內(nèi)
ob.writeObject(stu);//調(diào)用writeObject()方法將對(duì)象序列化并發(fā)送給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;
/**
* 反序列化
* @author Young
* created on 2017-5-25
*/
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內(nèi)
stu=(Student) ob.readObject();//調(diào)用readObject(),得到的結(jié)果是一個(gè)Object對(duì)象,需要進(jìn)行轉(zhuǎn)型得到最后所需的對(duì)象.
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();
}
}
}
序列化機(jī)制寫到流中的數(shù)據(jù)有
1,聲明和標(biāo)記,比如聲明使用了序列化協(xié)議、序列化協(xié)議版本、聲明這是一個(gè)新的對(duì)象、聲明這里開始一個(gè)新Class、結(jié)束標(biāo)記等。
2,對(duì)象所屬的類 ,類的長(zhǎng)度,
3,SerialVersionUID, 序列化ID,如果沒(méi)有指定,則會(huì)由算法隨機(jī)生成一個(gè)8byte的ID。
4,所有的非transient和非static的屬性的個(gè)數(shù)以及名稱。名稱長(zhǎng)度,屬性的值。
這只是簡(jiǎn)單對(duì)象序列化后寫到流中的數(shù)據(jù)。如果是復(fù)雜對(duì)象序列化就會(huì)包含更多的信息。比如引用其他對(duì)象作為成員變量,這是會(huì)將引用的對(duì)象也進(jìn)行序列化。還有序列化時(shí),只對(duì)對(duì)象的狀態(tài)進(jìn)行保存,而不管對(duì)象的方法;當(dāng)一個(gè)父類實(shí)現(xiàn)序列化,子類自動(dòng)實(shí)現(xiàn)序列化,不需要顯式實(shí)現(xiàn)Serializable接口;當(dāng)一個(gè)對(duì)象的實(shí)例變量引用其他對(duì)象,序列化該對(duì)象時(shí)也把引用對(duì)象進(jìn)行序列化。
二,Hadoop 序列化
hadoop在節(jié)點(diǎn)間的內(nèi)部通訊使用的是RPC,RPC協(xié)議把消息翻譯成二進(jìn)制字節(jié)流發(fā)送到遠(yuǎn)程節(jié)點(diǎn),遠(yuǎn)程節(jié)點(diǎn)再通過(guò)反序列化把二進(jìn)制流轉(zhuǎn)成原始的信息。
Hadoop使用自己的序列化格式Writable,它絕對(duì)緊湊、高速,但不太容易用Java以外的語(yǔ)言進(jìn)行拓展和使用。
1,Writable接口
Writable接口定義了兩個(gè)方法:一個(gè)將其狀態(tài)寫到DataOutput二進(jìn)制流,另一個(gè)從DataInput二進(jìn)制流讀取其狀態(tài):
實(shí)現(xiàn)代碼
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 序列化與反序列化
* @author Young
* created by 2017-6-9
*/
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序列化機(jī)制中還包含另外幾個(gè)重要的接口:WritableComparable、RawComparator 和 WritableComparator
WritableComparable提供類型比較的能力,繼承自Writable接口和Comparable接口,其中Comparable進(jìn)行 類型比較。ByteWritable、IntWritable、DoubleWritable等java基本類型對(duì)應(yīng)的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<T> extends Writable, Comparable<T> {
}
對(duì)于MapReduce來(lái)說(shuō),類型的比較非常重要,因?yàn)橹虚g有個(gè)基于鍵的排序階段。Hadoop提供了一個(gè)具有高效比較能力的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<T> extends Comparator<T> {
int compare(byte[] arg0, int arg1, int arg2, byte[] arg3, int arg4, int arg5);
}
該接口允許其實(shí)現(xiàn)直接比較數(shù)據(jù)流中的記錄,無(wú)須先把數(shù)據(jù)流反序列化為對(duì)象,這樣避免了新建對(duì)象而外的開銷。
WritableComparator 是對(duì)繼承自WritableComparable類的RawComparator類的一個(gè)通用實(shí)現(xiàn)。提供兩個(gè)主要功能。1,提供對(duì)原始的compare()方法的一個(gè)默認(rèn)實(shí)現(xiàn),該方法能夠反序列化將在流中進(jìn)行比較的對(duì)象,并調(diào)用對(duì)象的compare()方法。2,它充當(dāng)?shù)氖荝awComparator實(shí)例的工廠,例如為了獲得IntWritable的comparator,可以直接調(diào)用
RawComparator<IntWritable>comparator=WritableComparator.get(IntWritable.class);
下面是《Hadoop權(quán)威指南》的例子
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 <IntWritable> 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<w2 -> -1 ; w1=w2 -> 0
System.out.println(comparator.compare(b1,0,b1.length,b2,0,b2.length));
}
}
三,Hadoop 序列化框架
盡管大多數(shù)MapReduce程序使用Writable類型的key,value,但是不是所有MapReduce API 強(qiáng)制使用。事實(shí)上,可以使用任何類型,只要能有一種機(jī)制將每個(gè)類型進(jìn)行類型與二進(jìn)制的來(lái)回轉(zhuǎn)換。為此Hadoop提供了一個(gè)序列化框架來(lái)支持,他們?cè)趏rg.apache.hadoop.io.serializer包中,WritableSerialization類是對(duì)Writable類型的Serialization實(shí)現(xiàn)。
WritableSerialization類
public class WritableSerialization extends Configured implements Serialization<Writable> {...}
Serializer接口
定義了open()接口,序列化接口,close接口
public interface Serializer<T> {
void open(OutputStream arg0) throws IOException;
void serialize(T arg0) throws IOException;
void close() throws IOException;
}
Deserializer接口
定義了open()接口,反序列化接口,close接口
public interface Deserializer<T> {
void open(InputStream arg0) throws IOException;
T deserialize(T arg0) throws IOException;
void close() throws IOException;
}
Serialization接口
定義了一組接口,判斷是否支持輸入的類,根據(jù)輸入的類給出序列化接口和反序列化接口
public interface Serialization<T> {
boolean accept(Class<?> arg0);
Serializer<T> getSerializer(Class<T> arg0);
Deserializer<T> getDeserializer(Class<T> arg0);
}
還有幾個(gè)接口,可以查看API文檔
盡管這可以方便我們?cè)贛apReduce使用Java類型,比如String,Integer。但是這還是不如Writable高效。
Hadoop中不使用java Object Serialization的原因
1,Java Object Serialization不夠精簡(jiǎn),就如本文前面提到的Java Object Serialization序列化后的字節(jié)流會(huì)包含許多信息,比如類名與對(duì)象等。
2,對(duì)象在序列化中只存引用,引用可以出現(xiàn)的位置很隨機(jī),既可以在序列化的對(duì)象前,也可以在其后面,這樣就對(duì)隨機(jī)訪問(wèn)和排序造成影響,一旦出錯(cuò),整個(gè)后面的序列化就會(huì)全部錯(cuò)誤,Writable支持隨機(jī)訪問(wèn)和排序。因?yàn)榱髦械挠涗浭潜舜霜?dú)立的。
3,.Java序列化每次反序列化都要重新創(chuàng)建對(duì)象,內(nèi)存消耗大,而Writable是可以重用的。
以上所述是小編給大家介紹的Java Object Serialization與 Hadoop 序列化,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- java結(jié)合HADOOP集群文件上傳下載
- Java訪問(wèn)Hadoop分布式文件系統(tǒng)HDFS的配置說(shuō)明
- java使用hadoop實(shí)現(xiàn)關(guān)聯(lián)商品統(tǒng)計(jì)
- Java執(zhí)行hadoop的基本操作實(shí)例代碼
- hadoop中實(shí)現(xiàn)java網(wǎng)絡(luò)爬蟲(示例講解)
- Java/Web調(diào)用Hadoop進(jìn)行MapReduce示例代碼
- Hadoop運(yùn)行時(shí)遇到j(luò)ava.io.FileNotFoundException錯(cuò)誤的解決方法
- hadoop運(yùn)行java程序(jar包)并運(yùn)行時(shí)動(dòng)態(tài)指定參數(shù)
- java實(shí)現(xiàn)對(duì)Hadoop的操作
- 利用Java連接Hadoop進(jìn)行編程
相關(guān)文章
Java23種設(shè)計(jì)模式中的單例模式你了解嗎
這篇文章主要為大家詳細(xì)介紹了Java23種設(shè)計(jì)模式中的單例模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-02-02
詳解Spring Security的formLogin登錄認(rèn)證模式
對(duì)于一個(gè)完整的應(yīng)用系統(tǒng),與登錄驗(yàn)證相關(guān)的頁(yè)面都是高度定制化的,非常美觀而且提供多種登錄方式。這就需要Spring Security支持我們自己定制登錄頁(yè)面,也就是本文給大家介紹的formLogin模式登錄認(rèn)證模式,感興趣的朋友跟隨小編一起看看吧2019-11-11
java實(shí)現(xiàn)聯(lián)機(jī)五子棋
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)聯(lián)機(jī)五子棋,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
spring-cloud-gateway動(dòng)態(tài)路由的實(shí)現(xiàn)方法
這篇文章主要介紹了spring-cloud-gateway動(dòng)態(tài)路由的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Java實(shí)現(xiàn)FIFO任務(wù)調(diào)度隊(duì)列策略
在工作中,很多高并發(fā)的場(chǎng)景中,我們會(huì)用到隊(duì)列來(lái)實(shí)現(xiàn)大量的任務(wù)請(qǐng)求。當(dāng)任務(wù)需要某些特殊資源的時(shí)候,我們還需要合理的分配資源,讓隊(duì)列中的任務(wù)高效且有序完成任務(wù)。本文將為大家介紹通過(guò)java實(shí)現(xiàn)FIFO任務(wù)調(diào)度,需要的可以參考一下2021-12-12
springboot調(diào)用python腳本的實(shí)現(xiàn)示例
本文介紹了在SpringBoot應(yīng)用中調(diào)用Python腳本,包括ProcessBuilder類和ApacheCommonsExec庫(kù)兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-12-12
修改jar包package目錄結(jié)構(gòu)操作方法
這篇文章主要介紹了修改jar包package目錄結(jié)構(gòu)操作方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-07-07

