序列化與反序列化

序列化的意義java

  在考慮系統性能的時候,會考慮序列化。遠程通訊的時候,就要考慮序列化。序列化和反序列化是天天都會碰到的問題。就我而言,序列化這個概念基本上一直在聽到,可是不多有了解。Java對象的傳輸、分佈式架構、大數據量的工程。
  Java 平臺容許咱們在內存中建立可複用的Java 對象,但通常狀況下,只有當JVM處於運行時,這些對象纔可能存在,因此這些對象的生命週期不會比JVM 的生命週期更長。但在現實應用中,就可能要求在JVM中止運行以後可以保存(持久化)指定的對象,並在未來從新讀取被保存的對象。Java 對象序列化就可以幫助咱們實現該功能。
  序列化是把對象的狀態信息轉化爲可存儲或傳輸的形式過程,也就是把對象轉化爲字節序列的過程稱爲對象的序列化。
  反序列化是序列化的逆向過程,把字節數組反序列化爲對象,把字節序列恢復爲對象的過程成爲對象的反序列化。
  那麼如何去評價一個序列化工具呢?
  評價一個序列化算法優劣的兩個重要指標是:序列化之後的數據大小;序列化操做自己的速度及系統資源開銷(CPU、內存)。


常見的序列化工具git

Java自己自帶的序列化github

  Java自己就有一個序列化工具(實現Serializable接口),可是缺點也是存在的:(1)序列化後數據比較大。(2)其餘語言沒法識別
  Java自己序列化方法:
  @Override public <T> byte[] serializer(T obj) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); } catch (IOException e) { e.printStackTrace(); } finally { if(objectOutputStream != null) { try { objectOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return byteArrayIutputStream.toByteArray(); } @Override public <T> T deSerializer(byte[] bytes, Class<T> clazz) { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); ObjectInput objectInput = null; try { objectInput = new ObjectInputStream(byteArrayInputStream); return (T)objectInput.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; }

  對於一個但願經過序列化的類,可能會報InvalidClassException,這是由於序列化的類最好加上serialVersionUID,用來讓系統判斷序列化的可靠性。算法

serialVersionUID 的做用
  Java 的序列化機制是經過判斷類的serialVersionUID 來驗證版本一致性的。在進行反序列化時,JVM 會把傳來的字節流中的serialVersionUID與本地相應實體類的serialVersionUID 進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常,便是InvalidCastException若是沒有爲指定的class 配置serialVersionUID,那麼java 編譯器會自動給這個class 進行一個摘要算法,相似於指紋算法,只要這個文件有任何改動,獲得的UID 就會大相徑庭的,能夠保證在這麼多類中,這個編號是惟一的。json

  serialVersionUID 有兩種顯示的生成方式:
  一是默認的1L,好比:private static final long serialVersionUID = 1L;
  二是根據類名、接口名、成員方法及屬性等來生成一個64 位的哈希字段。
  當實現java.io.Serializable 接口的類沒有顯式地定義一個serialVersionUID 變量時候,Java 序列化機制會根據編譯的Class 自動生成一個serialVersionUID 做序列化版本比較用,這種狀況下,若是Class 文件(類名,方法明等)沒有發生變化(增長空格,換行,增長註釋等等),就算再編譯屢次,serialVersionUID 也不會變化的。api

  Java對象序列化過程當中,若是序列化獲得的對象增長或者減小一個變量,並不會報錯,僅僅是某個字段讀取不到。對於一個靜態變量的序列化,靜態變量不會參與序列化。由於序列化保存的是一個對象的狀態,而靜態變量屬於一個類的狀態。數組

transient架構

  transient修飾的字段表示不會在序列化過程被保存,他的值在反序列化以後還是類定義的值。也能夠手動寫到流裏面,來繞過序列化框架

序列化與繼承分佈式

  若是說一個子類實現了序列化,父類沒有實現序列化,在子類被反序列化以後,是沒法得到父類的值,即子類繼承父類已經被定義的那個值是空的。

  若是一個父類實現序列化,那麼子類自動實現序列化,不用繼承Serializable接口。

  對於同一個對象寫入流兩次,流裏的數據不會加倍,而是增長五個字節(增長新增引用和一些控制信息的空間),由於當流裏存在同一個對象的時候,只是會增長一個引用。這算是個優勢,節省了存儲空間。

序列化實現克隆

  Java對於每一個接口類都具備克隆能力,但只是淺克隆。淺克隆只是新建對象,對原對象的一些變量只是複製它的引用。我實例2 克隆實例1,當實例2改變某個值後,實例1也會改變。深克隆實現Serializable接口,把對象序列化流中,再從流裏讀出來,這個對象就不是原來的對象了,全部的變量的引用都會新建一個引用。

xml序列化框架

  優勢是可讀性強,缺點是序列化以後的數據比較大。在技術要求比較高的時候,通常不會用到它。

  代碼實例:

public class XmlSerializer { XStream xStream = new XStream(new DomDriver()); public <T> String serializer(T obj) { return xStream.toXML(obj); } public <T> T deSerializer(String bytes, Class<T> clazz) { return (T)xStream.fromXML(bytes); } public static void main(String[] args) { XmlSerializer iSerializer = new XmlSerializer(); User user = new User(); String bytes = iSerializer.serializer(user); user.setName("jolivan"); System.out.println(new String(bytes)); User userNow = iSerializer.deSerializer(bytes,User.class); System.out.println(userNow.getName()); System.out.println(userNow.getAge()); } } ==============輸出==================
<serial.User>
  <name>Lushe</name>
  <age>23</age>
</serial.User> Lushe 23

  咱們能夠看到,根據數據咱們就知道他是個什麼類,裏面有啥。可讀性很是高。

JSON

  JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,相對於XML 來講,JSON 的字節流更小,並且可讀性也很是好。如今JSON數據格式在企業運用是最廣泛的
  JSON 序列化經常使用的開源工具備不少
  1. Jackson (https://github.com/FasterXML/jackson)
  2. 阿里開源的FastJson (https://github.com/alibaba/fastjon)
  3. Google 的GSON (https://github.com/google/gson)
  這幾種json序列化工具中,Jackson 與fastjson 要比GSON 的性能要好,可是Jackson、GSON 的穩定性要比Fastjson 好。而fastjson的優點在於提供的api 很是容易使用。

  用阿里的FastJson來示例一下:

public class FastjsonSerializeer implements ISerializer { @Override public <T> byte[] serializer(T obj) { return JSON.toJSONBytes(obj); } @Override public <T> T deSerializer(byte[] bytes, Class<T> clazz) { return (T)JSON.parseObject(bytes,clazz); } public static void main(String[] args) { ISerializer iSerializer = new FastjsonSerializeer(); User user = new User(); byte [] bytes = iSerializer.serializer(user); user.setName("jolivan"); System.out.println(new String(bytes)); User userNow = iSerializer.deSerializer(bytes,User.class); System.out.println(userNow.getName()); } }

===================輸出=====================

{"age":"23","name":"Lushe"}
Lushe

 

hessian

  dubbo裏使用的就是它,但對它作了優化,又稱爲hessian2。

Protobuf

  優點:(1)獨立語言(能夠基於不一樣的語言)、獨立平臺(跨平臺交互)(2)性能高,壓縮性好;(3)解析性好

  缺陷:實現起來很麻煩,學習成本大。

  它有獨立的編譯器

序列化的實際應用舉例

  好比說咱們在一個分佈式系統中,有一個訂單模塊和一個支付模塊,訂單模塊基於一個協議(dubbo)調用訂單系統,底層傳輸的事二進制數據,那麼咱們要作的事情就是把對象轉化爲一個二進制數據,這就是序列化的場景。

相關文章
相關標籤/搜索