什麼是protobuf
它是一個對象序列化/反序列化的工具,什麼是對象的序列化/反序列化?就是把一個Java堆中存活的對象轉換成一串二進制編碼,然後該編碼可以用于本地存儲和網絡傳輸。反序列化就是根據一串二進制編碼還原出原來的那個對象,protobuf能夠将一個對象以特定的格式轉換為一個二進制串(序列化),然後将二進制串還原成對象(反序列化)。這裡涉及到兩個名額:
對同一個目标對象:
1)序列化和反序列化的時間開銷,
2)序列化之後串的長度
protobuf在這兩個方面都有非常出色的表現(網傳)
在Windows下使用protobuf的步驟如下:
第一步:
下載下傳protoc-2.5.0-win32.zip,得到其中的protoc.exe.然後将該protoc.exe的存放路徑加入Path環境變量,便于通路。比如,我的protoc.exe存放于D:/protobuf,環境變量中添加如下配置:
D:/protobuf
第二步:
編寫.proto檔案,它是序列化一個對象的“模闆”,protobuf就是根據它來決定如何序列化和反序列化。
編寫的person-entity.proto配置檔案如下:
option java_outer_classname = "PersonEntity";//生成的資料通路類的類名
message Person {
required int32 id= 1;//同上
required string name = 2;//必須字段,在後面的使用中必須為該段設定值
optional string email = 3;//可選字段,在後面的使用中可以自由決定是否為該字段設定值
}
message字段代表了一個對象,是以,可以使用message實作對象的嵌套序列化
required表示是強制字段,在後面的使用中必須為該字段設定值;
optional表示是可選字段,在後面的使用中可選地為該字段設定值;
repeated表示集合類型,可以填充多個資料
後面的1,2,3是字段的編号,字段名是讓使用者使用的,字段編号則是讓系統識别的,從1開始。
protobuf自定義的資料類型和Java的資料類型的對應關系見這篇部落格https://blog.csdn.net/chuhui1765/article/details/100670318
可以指定字段類型為其他的Message字段類型:
message SearchResponse{
repeated Result result= 1;
}
message Result{
requiredstring url = 1;
optionalstring title = 2;
repeatedstring snippets = 3;
}
可以在message内部嵌套另一個message:
message SearchResponse{
message Result{
requiredstring url = 1;
optionalstring title = 2;
repeatedstring snippets = 3;
}
repeated Result result= 1;
}
有關protobuf更詳細的用法,可以參考這篇優秀的博文https://worktile.com/tech/share/prototol-buffers
第三步:
用protoc.exe生成PersonEntity.class。打開DOS視窗,輸入如下的編譯指令:
protoc.exe -I=D:/protobuf --java_out=D:/protobuf D:/protobuf/person-entity.proto
編譯指令的格式如下:
protoc.exe -I=.protoc檔案的存放路徑 --java_out=.class檔案的輸出路徑 .protoc檔案的具體存放路徑
其中第三個路徑是必須的,而且要寫明.protoc檔案
最後會生成PersonEntity.java檔案,可以把它了解為一個工具類,幫助我們執行對象的序列化。
第四步:
建立maven項目,将PersonEntity.java複制到項目中,引入maven依賴
com.google.protobuf
protobuf-java
2.5.0
利用工具類序列化和反序列化對象。
import java.io.IOException;public classTest {public static voidmain(String[] args) throws IOException {//模拟将對象轉成byte[],友善傳輸
PersonEntity.Person.Builder builder =PersonEntity.Person.newBuilder();
builder.setId(1);
builder.setName("abc");
builder.setEmail("def");
PersonEntity.Person person=builder.build();
System.out.println("after :" +person.toString());
finalbyte[] bytes =person.toByteArray();
System.out.println("===========Person Byte==========");for(byteb : person.toByteArray()){
System.out.print(b);
}
System.out.println();//反序列化成Person類
byte[] byteArray =person.toByteArray();
PersonEntity.Person person2=PersonEntity.Person.parseFrom(byteArray);
System.out.println("after :" +person2.toString());
}
}
這裡還犯過一個錯:另外自定義一個Person類與PersonEntity.Person相對應。實際上在我們的項目中可以直接利用這個PersonEntity.Person來實作功能需求。
下面同樣以這個Person類序列化為例,來實際地比較通過各種不同的序列化方式得到的的位元組數組長度:
1.protobuf
序列化長度為:
System.out.println(person.getSerializedSize());
結果為:12
2.Java原生序列化
import java.io.*;public classTest2 {public static voidmain(String[] args) throws IOException {
Person p= new Person(1,"abc","def");
ObjectOutputStream objectOutputStream= newObjectOutputStream(new FileOutputStream("E:/object.txt"));
objectOutputStream.writeObject(p);
FileInputStream fileInputStream= new FileInputStream("E:/object.txt");
finalint length =fileInputStream.read();
System.out.println(length);
}
}
結果為:172
3.通過對象的toString()
序列化:重寫對象的toString()方法,按照特定的格式,将對象的各個字段封裝進一個String字元串,再将字元串按照特定的編碼方式,轉換成byte數組,實作本地存儲和網絡傳輸。
反序列化:對byte數組,重新轉換成字元串,再将字元串按照約定的格式,還原成對象的屬性,進而構造出原來的對象。
這裡重點探讨的是:String對象轉換成byte數組之後的長度
public static voidmain(String[] args) {
String s= "abc";
System.out.println(s.getBytes().length);
String s2= "嘤嘤嘤";
System.out.println(s2.getBytes().length);
System.out.println(Charset.defaultCharset());
}
輸出結果為:
3
9
UTF-8
可見,如果是英文字母每個字元占一個位元組,如果是漢字,每個字元占3個位元組。如果通過這種方式序列化,它的空間效率也是非常高的。
這裡有必要搞清楚Java預設的字元編碼集:
public byte[] getBytes() {return StringCoding.encode(value, 0, value.length);
}
Java采用平台的預設字元編碼集來進行字元與二進制byte序列的轉換。當然也可以在序列化的時候顯示指定字元編碼集。
Unicode 與 UTF-8
為什麼會出現unicode,這首先要從ASCII碼說起,它起源最早,用7位來表示英文字母、數字等字元。但是後來又出現了很多其他語言的字元,比如漢字,ASCII碼最多隻能表示256個字元,無法支援這些”異域“字元,是以出現了unicode碼,将各種語言的字元用一種統一的編碼方式轉換成01序列。但是新的問題又出現了,unicode編碼效率不高,原來一個字元a用ASCII表示隻需要一個字元,現在用unicode可能用3個字元,存儲效率和網絡傳輸效率大打折扣。是以出現了中間轉換編碼集,utf-8就是其中之一。”UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼,又稱萬國碼“,注意,它的編碼長度是可變的。是以,unicode是utf-8的基礎,utf-8是對unicode的一種解釋。