1,什么是序列化(Serialization)
序列化是指將結構化對象轉化為字節(jié)流以便在網絡上傳輸或者寫到磁盤永久存儲的過程。反序列化指將字節(jié)流轉回結構化對象的逆過程。簡單的理解就是對象轉換為字節(jié)流用來傳輸和保存,字節(jié)流轉換為對象將對象恢復成原來的狀態(tài)。
2,序列化(Serialization)的作用
(1)一種持久化機制,把的內存中的對象狀態(tài)保存到一個文件中或者數(shù)據(jù)庫。
(2)一種通信機制,用套接字在網絡上傳送對象。
(3)Java遠程方法調用(RMI)需要調用對象時,
3,實現(xiàn)了Serializable接口的對象的序列化
在java.io包中,接口Serialization用來作為實現(xiàn)對象串行化的工具 ,只有實現(xiàn)了Serialization的類的對象才可以被序列化。 Serializable接口中沒有任何的方法,當一個類聲明要實現(xiàn)Serializable接口時,只是表明該類參加序列化協(xié)議,而不需要實現(xiàn)任何特殊的方法。
要序列化一個對象,首先要創(chuàng)建OutputStream對象,然后將其封裝在一個ObjectOutputStream對象內。此時,調用writeObject()方法將對象序列化并發(fā)送給OutputStream。在反序列化時,需要將一個InputStream封裝在ObjectInputStream內,然后調用readObject(),得到的結果是一個Object對象,需要進行轉型得到最后所需的對象。需要注意的是,在對一個Serializable對象進行反序列化的過程中,沒有調用任何構造器,包括缺省的構造器,整個對象都是通過從InputStream中取得數(shù)據(jù)恢復過來的。對象序列化是面向字節(jié)的,因此采用InputStream和OutputStream層次結構。
對Student序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package cn.test.serializable; /** * 序列化對象實現(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; } } |
序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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對象 ObjectOutputStream ob= new ObjectOutputStream(out); //封裝在一個ObjectOutputStream對象內 ob.writeObject(stu); //調用writeObject()方法將對象序列化并發(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(); } } } |
反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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內 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(); } } } |
序列化機制寫到流中的數(shù)據(jù)有
1,聲明和標記,比如聲明使用了序列化協(xié)議、序列化協(xié)議版本、聲明這是一個新的對象、聲明這里開始一個新Class、結束標記等。
2,對象所屬的類 ,類的長度,
3,SerialVersionUID, 序列化ID,如果沒有指定,則會由算法隨機生成一個8byte的ID。
4,所有的非transient和非static的屬性的個數(shù)以及名稱。名稱長度,屬性的值。
這只是簡單對象序列化后寫到流中的數(shù)據(jù)。如果是復雜對象序列化就會包含更多的信息。比如引用其他對象作為成員變量,這是會將引用的對象也進行序列化。還有序列化時,只對對象的狀態(tài)進行保存,而不管對象的方法;當一個父類實現(xiàn)序列化,子類自動實現(xiàn)序列化,不需要顯式實現(xiàn)Serializable接口;當一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進行序列化。
二,Hadoop 序列化
hadoop在節(jié)點間的內部通訊使用的是RPC,RPC協(xié)議把消息翻譯成二進制字節(jié)流發(fā)送到遠程節(jié)點,遠程節(jié)點再通過反序列化把二進制流轉成原始的信息。
Hadoop使用自己的序列化格式Writable,它絕對緊湊、高速,但不太容易用Java以外的語言進行拓展和使用。
1,Writable接口
Writable接口定義了兩個方法:一個將其狀態(tài)寫到DataOutput二進制流,另一個從DataInput二進制流讀取其狀態(tài):
實現(xiàn)代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
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序列化機制中還包含另外幾個重要的接口:WritableComparable、RawComparator 和 WritableComparator
WritableComparable提供類型比較的能力,繼承自Writable接口和Comparable接口,其中Comparable進行 類型比較。ByteWritable、IntWritable、DoubleWritable等java基本類型對應的Writable類型,都繼承自 WritableComparable。
WritableComparable接口
1
2
3
4
5
6
7
8
|
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> { } |
對于MapReduce來說,類型的比較非常重要,因為中間有個基于鍵的排序階段。Hadoop提供了一個具有高效比較能力的RawComparator接口。
RawComparator接口
1
2
3
4
5
6
7
8
9
|
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); } |
該接口允許其實現(xiàn)直接比較數(shù)據(jù)流中的記錄,無須先把數(shù)據(jù)流反序列化為對象,這樣避免了新建對象而外的開銷。
WritableComparator 是對繼承自WritableComparable類的RawComparator類的一個通用實現(xiàn)。提供兩個主要功能。1,提供對原始的compare()方法的一個默認實現(xiàn),該方法能夠反序列化將在流中進行比較的對象,并調用對象的compare()方法。2,它充當?shù)氖荝awComparator實例的工廠,例如為了獲得IntWritable的comparator,可以直接調用
1
|
RawComparator<IntWritable>comparator=WritableComparator.get(IntWritable. class ); |
下面是《Hadoop權威指南》的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
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 強制使用。事實上,可以使用任何類型,只要能有一種機制將每個類型進行類型與二進制的來回轉換。為此Hadoop提供了一個序列化框架來支持,他們在org.apache.hadoop.io.serializer包中,WritableSerialization類是對Writable類型的Serialization實現(xiàn)。
WritableSerialization類
1
|
public class WritableSerialization extends Configured implements Serialization<Writable> {...} |
Serializer接口
定義了open()接口,序列化接口,close接口
1
2
3
4
5
|
public interface Serializer<T> { void open(OutputStream arg0) throws IOException; void serialize(T arg0) throws IOException; void close() throws IOException; } |
Deserializer接口
定義了open()接口,反序列化接口,close接口
1
2
3
4
5
|
public interface Deserializer<T> { void open(InputStream arg0) throws IOException; T deserialize(T arg0) throws IOException; void close() throws IOException; } |
Serialization接口
定義了一組接口,判斷是否支持輸入的類,根據(jù)輸入的類給出序列化接口和反序列化接口
1
2
3
4
5
|
public interface Serialization<T> { boolean accept(Class<?> arg0); Serializer<T> getSerializer(Class<T> arg0); Deserializer<T> getDeserializer(Class<T> arg0); } |
還有幾個接口,可以查看API文檔
盡管這可以方便我們在MapReduce使用Java類型,比如String,Integer。但是這還是不如Writable高效。
Hadoop中不使用java Object Serialization的原因
1,Java Object Serialization不夠精簡,就如本文前面提到的Java Object Serialization序列化后的字節(jié)流會包含許多信息,比如類名與對象等。
2,對象在序列化中只存引用,引用可以出現(xiàn)的位置很隨機,既可以在序列化的對象前,也可以在其后面,這樣就對隨機訪問和排序造成影響,一旦出錯,整個后面的序列化就會全部錯誤,Writable支持隨機訪問和排序。因為流中的記錄是彼此獨立的。
3,.Java序列化每次反序列化都要重新創(chuàng)建對象,內存消耗大,而Writable是可以重用的。
以上所述是小編給大家介紹的Java Object Serialization與 Hadoop 序列化,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:http://blog.csdn.net/yangjjuan/article/details/73121428