程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 深入淺析Java Object Serialization與 Hadoop 序列化

深入淺析Java Object Serialization與 Hadoop 序列化

編輯:關於JAVA

一,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接口
 * @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");//創建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;
/**
 * 反序列化
 * @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();
    }
  }
}

序列化機制寫到流中的數據有

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 序列化與反序列化
 * @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接口

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接口

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);
}

該接口允許其實現直接比較數據流中的記錄,無須先把數據流反序列化為對象,這樣避免了新建對象而外的開銷。

WritableComparator 是對繼承自WritableComparable類的RawComparator類的一個通用實現。提供兩個主要功能。1,提供對原始的compare()方法的一個默認實現,該方法能夠反序列化將在流中進行比較的對象,並調用對象的compare()方法。2,它充當的是RawComparator實例的工廠,例如為了獲得IntWritable的comparator,可以直接調用

RawComparator<IntWritable>comparator=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 <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 序列化框架

盡管大多數MapReduce程序使用Writable類型的key,value,但是不是所有MapReduce API 強制使用。事實上,可以使用任何類型,只要能有一種機制將每個類型進行類型與二進制的來回轉換。為此Hadoop提供了一個序列化框架來支持,他們在org.apache.hadoop.io.serializer包中,WritableSerialization類是對Writable類型的Serialization實現。
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接口

定義了一組接口,判斷是否支持輸入的類,根據輸入的類給出序列化接口和反序列化接口

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序列化後的字節流會包含許多信息,比如類名與對象等。

2,對象在序列化中只存引用,引用可以出現的位置很隨機,既可以在序列化的對象前,也可以在其後面,這樣就對隨機訪問和排序造成影響,一旦出錯,整個後面的序列化就會全部錯誤,Writable支持隨機訪問和排序。因為流中的記錄是彼此獨立的。

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

以上所述是小編給大家介紹的Java Object Serialization與 Hadoop 序列化,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對網站的支持!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved