作者對序列化的描述淺顯易懂!(https://www.douban.com/note/313096752/) ...
作者對序列化的描述淺顯易懂!(https://www.douban.com/note/313096752/)
1. 序列化從頭說
在面向對象程式設計中,類是個很重要的概念。所謂“類”,可以將它想像成建築圖紙,而對象就是根據圖紙蓋的大樓。類,規定了對象的一切。根據建築圖紙造房子,蓋出來的就是大樓,等同於將類進行實例化,得到的就是對象。
一開始,在源代碼里,類的定義是明確的,但對象的行為有些地方是明確的,有些地方是不明確的。對象里不明確地方,是因為對象在運行的時候,需要處理無法預測的事情,諸如用戶點了下屏幕,用戶點了下按鈕,輸入點東西,或者需要從網路發送接收數據之類的。後來,引入了泛型的概念之後,類也開始不明確了,如果使用了泛型,直到程式運行的時候,才知道究竟是哪種對象需要處理。
對象可以很複雜,也可以跟時序相關。一般來說,“活的”對象只生存在記憶體里,關機斷電就沒有了。一般來說,“活的”對象只能由本地的進程使用,不能被髮送到網路上的另外一臺電腦。
序列化,可以存儲“活的”對象,可以將“活的”對象發送到遠程電腦。
把“活的”對象序列化,就是把“活的”對象轉化成一串位元組,而“反序列化”,就是從一串位元組里解析出“活的”對象。於是,如果想把“活的”對象存儲到文件,存儲這串位元組即可,如果想把“活的”對象發送到遠程主機,發送這串位元組即可,需要對象的時候,做一下反序列化,就能將對象“複活”了。
將對象序列化存儲到文件,術語又叫“持久化”。將對象序列化發送到遠程電腦,術語又叫“數據通信”。
Java對序列化提供了非常方便的支持,在定義類的時候,如果想讓對象可以被序列化,只要在類的定義上加上了”implements Serializable”即可,比如說,可以這麼定義”public class Building implements Serializable”,其他什麼都不要做,Java會自動的處理相關一切。Java的序列化機制相當複雜,能處理各種對象關係。
Java的序列化機制的缺點就是計算量開銷大,且序列化的結果體積大太,有時能達到對象大小的數倍乃至十倍。它的引用機制也會導致大文件不能分割的問題。這些缺點使得Java的序列化機制對Hadoop來說是不合適的。於是Hadoop設計了自己的序列化機制。
為什麼序列化對Hadoop很重要?因為Hadoop在集群之間進行通訊或者RPC調用的時候,需要序列化,而且要求序列化要快,且體積要小,占用帶寬要小。所以必須理解Hadoop的序列化機制。
2. Hadoop的序列化介面
什麼是介面?簡答來說,介面就是規定,它規定類必須實現的方法。一個介面可以包含多幹個方法。如果一個類說自己實現了某個介面,那麼它必須實現這個介面里的所有方法。特殊情況下,介面也可以沒有任何方法。
Writable介面,也就是org.apache.hadoop.io.Writable介面。Hadoop的所有可序列化對象都必須實現這個介面。Writable介面里有兩個方法,一個是write方法,將對象寫入位元組流,另一個是readFields方法,從位元組流解析出對象。
Java的API提供了Comparable介面,也就是java.lang.Comparable介面。這個介面只有一個方法,就是compareTo,用於比較兩個對象。
WritableComparable介面同時繼承了Writable和Comparable這兩個介面。
Hadoop里的三個類IntWritable、DoubleWritable和ByteWritable,都繼承了WritableComparable介面。註意,IntWritable、DoubleWritable和ByteWritable,儘管尾碼是“Writable”,但它們不是介面,是類!!
Hadoop的序列化介面還有更多的類型,在這裡不一一列舉。
3. IntWritable如何序列化
3.1 目錄和文件結構
這個例子演示IntWritable如何序列化。首先,創建一個IntWritable,然後,將它序列化,輸出到一個位元組流中。然後,創建一個新的IntWritable,從位元組流中讀取值,這是反序列化。
創建目錄~/intser存放源代碼、編譯和打包結果。在intser目錄下,有兩個子目錄,分別是src目錄和classes目錄,src目錄存放Java源代碼,class存放編譯結果。在src目錄下,只有一個源代碼文件IntSer.java。
3.2 IntSer.java文件的源代碼
package com.brianchen.hadoop;
import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.util.StringUtils;
public class IntSer{
public byte[] serialize(Writable w)throws IOException{
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dataout = new DataOutputStream(out);
w.write(dataout);
dataout.close();
return out.toByteArray();
}
public byte[] deserialize(Writable w, byte[] bytes) throws IOException{
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
DataInputStream datain = new DataInputStream(in);
w.readFields(datain);
datain.close();
return bytes;
}
public static void main(String[] args) throws Exception{
IntWritable intw = new IntWritable(7);
byte[] bytes = serialize(intw);
String bytes_str = StringUtils.byteToHexString(bytes);
System.out.println(bytes_str);
IntWritable intw2 = new IntWritable(0);
deserialize(intw2, bytes);
System.out.println(intw2);
}
}
3.3 編譯
“cd ~/intser”
“javac -cp /home/brian/usr/hadoop/hadoop-1.2.1/hadoop-core-1.2.1.jar -d ./classes src/*.java”
3.4 打包
“jar -cvf intser.jar -C ./classes .”
3.5 運行
“cd ~/usr/hadoop/hadoop-1.2.1”
“./bin/hadoop jar /home/brian/intser/intser.jar com.brianchen.hadoop.IntSer”
首先確認Hadoop已經是運行的,然後切換到Hadoop的安裝目錄,然後運行,輸出結果是兩行,第一行是”00000007”,第二行是”7”。