原文位址為: Protocol Buffer技術詳解(Java執行個體)
該篇Blog和上一篇(C++執行個體)基本相同,隻是面向于我們團隊中的Java工程師,畢竟我們項目的前端部分是基于Android開發的,而且我們研發團隊中目前主要使用的開發語言就是C++、Java和Python,其中Python主要用于編寫各種工具程式。然而為了保證該篇Blog的完整性和獨立性,我仍然會将上一篇Blog中已經出現的内容再一次贅述,同時對于Java中特有的部分也會着重介紹。
一、生成目智語言代碼。
下面的指令幫助我們将MyMessage.proto檔案中定義的一組Protocol Buffer格式的消息編譯成目智語言(Java)的代碼。至于消息的内容,我們會在後面以分段的形式逐一列出,同時也會在附件中給出所有源代碼。
protoc -I=./message --java_out=./src ./MyMessage.proto
從上面的指令行參數中可以看出,待編譯的檔案為MyMessage.proto,他存放在目前目錄的message子目錄下。--java_out參數則訓示編譯工具我們需要生成目智語言是java,輸出目錄是目前目錄的src子目錄。這裡需要補充說明的是,因為在MyMessage.proto檔案中定義了option java_package = "com.lsk.lyphone"的檔案級選項,是以輸出的目前是src/com/lsk/lyphone,生成的目标代碼檔案名是MyMessage.java。
二、簡單message生成的Java代碼。
這裡先定義一個最簡單的message,其中隻是包含原始類型的字段。
option java_package = "com.lsk.lyphone";
option java_outer_classname = "LYPhoneMessage";
option optimize_for = LITE_RUNTIME;
message LogonReqMessage {
required int64 acctID = 1;
required string passwd = 2;
}
對于選項java_package和java_outer_classname的功能,我們已經在之前的一篇Blog(語言規範)中進行了清晰的闡述,這裡就不在另行介紹了。然而對于選項optimize_for,這裡将結合本例給出一些實用性描述。
目前.proto檔案中該選項的值為LITE_RUNTIME,是以由該.proto檔案生成的所有Java類的父類均為com.google.protobuf.GeneratedMessageLite,而非com.google.protobuf.GeneratedMessage,同時與之對應的Builder類則均繼承自com.google.protobuf.MessageLiteOrBuilder,而非com.google.protobuf.MessageOrBuilder。在之前的部落格中已經給出了一些簡要的說明,MessageLite接口是Message的父接口,在MessageLite中将缺少Protocol Buffer對反射的支援,而此功能均在Message接口中提供了接口規範,同時又在其實作類GeneratedMessage中給予了最小功能的實作。對于我們的項目而言,整個系統相對比較封閉,不會和更多的外部程式進行互動,與此同時,我們的用戶端部分又是運作在Android平台,有鑒于此,我們考慮使用LITE版本的Protocol Buffer。這樣不僅可以得到更高編碼效率,而且生成代碼編譯後所占用的資源也會更少,至于反射所能帶來的靈活性和極易擴充性,對于該項目而言完全可以忽略。下面我們來看一下由message LogonReqMessage生成的Java類的部分聲明,以及常用方法的說明性注釋。
在做各種case的對比性分析之前必須要事先聲明的是,Protocol Buffer針對Java語言所生成的代碼和C++相比存在一個非常重要的差别,即為每個消息均會生成一個Builder接口和一個與消息對應的實作類,該實作類又将同時實作生成的Builder接口和擴充Protocol Buffer内置的GeneratedMessageLite(或GeneratedMessage)類。這一點對于Protocol Buffer而言,是巧妙的使用了設計模式中的Builder模式。換言之,對于所有消息字段的修改操作均需要通過與其對應的Builder接口輔助完成。相信我們會通過對下面用例的學習可以得到更為清楚的認識。
1 //用于修改LogonReqMessage消息字段的輔助Builder接口。
2 //該接口會為消息中的每個字段均提供getter和setter方法。
3 public interface LogonReqMessageOrBuilder
4 extends com.google.protobuf.MessageLiteOrBuilder {
5
6 // required int64 acctID = 1;
7 boolean hasAcctID();
8 long getAcctID();
9
10 // required string passwd = 2;
11 boolean hasPasswd();
12 String getPasswd();
13 }
14 //該類為final類,即不可以在被子類化了。這一點在Protocol Buffer的官方文檔中給予了明确
15 //的說明,因為子類化将會破壞序列化和反序列化的過程。
16 public static final class LogonReqMessage extends
17 com.google.protobuf.GeneratedMessageLite
18 implements LogonReqMessageOrBuilder {
19
20 // Use LogonReqMessage.newBuilder() to construct.
21 // 由于所有構造函數均為私有方法,由此可見,我們不能直接new LogonReqMessage的對象
22 // 執行個體,而是隻能通過與其對應Builder來構造,或是直接通過反序列化的方式生成。
23 private LogonReqMessage(Builder builder) {
24 super(builder);
25 }
26 //該靜态方法為該類Builder接口的工廠方法。傳回的Builder實作類在完成各個字段的
27 //初始化後,通過build()方法傳回與其對應的消息實作類,即LogonReqMessage。
28 public static Builder newBuilder() { return Builder.create(); }
29 //通過該類的對象擷取與其對應的Builder類對象,一般用于通過Builder類完成消息字段的修改。
30 public Builder toBuilder() { return newBuilder(this); }
31
32 private LogonReqMessage(boolean noInit) {}
33 //判斷目前對象的所有字段是否都已經被初始化。
34 public final boolean isInitialized() {
35 ... ...
36 }
37 //擷取已經被初始化後的對象序列化時所占用的位元組空間。
38 public int getSerializedSize() {
39 ... ...
40 }
41 //從記憶體中飯序列化LogonReqMessage對象。
42 //Protocol Buffer中還提供其他一些接口方法,用于從不同的資料源反序列化對象。
43 public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(byte[] data)
44 throws com.google.protobuf.InvalidProtocolBufferException {
45 return newBuilder().mergeFrom(data).buildParsed();
46 }
47 //功能和上一個函數相同,隻是輸入源改為InputStream接口。
48 public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(java.io.InputStream input)
49 throws java.io.IOException {
50 return newBuilder().mergeFrom(input).buildParsed();
51 }
52
53 // required int64 acctID = 1;
54 // 下面的靜态變量對應于該字段在.proto中定義的标簽号。該變量的命名規則為:字段(全部大寫) + _FIELD_NUMBER。
55 public static final int ACCTID_FIELD_NUMBER = 1;
56 public boolean hasAcctID() {
57 return ((bitField0_ & 0x00000001) == 0x00000001);
58 }
59 public long getAcctID() {
60 return acctID_;
61 }
62
63 // required string passwd = 2;
64 public static final int PASSWD_FIELD_NUMBER = 2;
65 public boolean hasPasswd() {
66 return ((bitField0_ & 0x00000002) == 0x00000002);
67 }
68 public String getPasswd() {
69 ... ...
70 }
71 //每一個Message類都會包含一個靜态内部類,即與之對應的Builder類。上面代碼中所涉及的Builder類均為該内部類。
72 public static final class Builder extends
73 com.google.protobuf.GeneratedMessageLite.Builder<
74 com.lsk.lyphone.LYPhoneMessage.LogonReqMessage, Builder>
75 implements com.lsk.lyphone.LYPhoneMessage.LogonReqMessageOrBuilder {
76 //清空目前對象中的所有設定。調用該函數之後,本例中的hasAcctID和hasPasswd都會傳回false。
77 public Builder clear() {
78 super.clear();
79 acctID_ = 0L;
80 bitField0_ = (bitField0_ & ~0x00000001);
81 passwd_ = "";
82 bitField0_ = (bitField0_ & ~0x00000002);
83 return this;
84 }
85 //克隆出一個Builder對象。
86 public Builder clone() {
87 return create().mergeFrom(buildPartial());
88 }
89 public com.lsk.lyphone.LYPhoneMessage.LogonReqMessage build() {
90 com.lsk.lyphone.LYPhoneMessage.LogonReqMessage result = buildPartial();
91 if (!result.isInitialized()) {
92 throw newUninitializedMessageException(result);
93 }
94 return result;
95 }
96 // Builder類中修改外部消息類的方法。
97 // required int64 acctID = 1;
98 public boolean hasAcctID() {
99 return ((bitField0_ & 0x00000001) == 0x00000001);
100 }
101 public long getAcctID() {
102 return acctID_;
103 }
104 //設定AcctID字段,該函數調用後hasAcctID函數将傳回true。
105 //這裡之是以讓傳回值為Builder對象,就是可以讓調用者在一條代碼中友善的連續修改多個字段,
106 //如:myMessage.setAcctID(100).setPasswd("MyName");
107 public Builder setAcctID(long value) {
108 bitField0_ |= 0x00000001;
109 acctID_ = value;
110 return this;
111 }
112 //清空AcctID字段,該函數調用後hasAcctID函數傳回false。
113 //這裡之是以讓傳回值為Builder對象,就是可以讓調用者在一條代碼中友善的連續清空多個字段,
114 //如:myMessage.clearAcctID().clearPasswd();
115 public Builder clearAcctID() {
116 bitField0_ = (bitField0_ & ~0x00000001);
117 acctID_ = 0L;
118 return this;
119 }
120
121 // required string passwd = 2;
122 public boolean hasPasswd() {
123 return ((bitField0_ & 0x00000002) == 0x00000002);
124 }
125 public String getPasswd() {
126 ... ...
127 }
128 public Builder setPasswd(String value) {
129 ... ...
130 }
131 public Builder clearPasswd() {
132 bitField0_ = (bitField0_ & ~0x00000002);
133 passwd_ = getDefaultInstance().getPasswd();
134 return this;
135 }
136 void setPasswd(com.google.protobuf.ByteString value) {
137 bitField0_ |= 0x00000002;
138 passwd_ = value;
139 }
140 }
141 }
在上面生成的代碼中并沒有列出與序列化相關的函數,這部分代碼基本都是在父類中實作的,我們将在下面的例子中給出一些最基本的用法,有興趣的開發者可以直接看Protocol Buffer中的源碼,這部分代碼比較通俗易懂。
下面是讀寫LogonReqMessage對象的Java測試代碼和說明性注釋。
1 private static void testSimpleMessage() {
2 System.out.println("==================This is simple message.================");
3 //如前所述,不能直接構造該消息類對象,隻能通過他的内部Builder類構造并完成所有字段的初始化。
4 LogonReqMessage.Builder logonReqBuilder = LogonReqMessage.newBuilder();
5 logonReqBuilder.setAcctID(20);
6 logonReqBuilder.setPasswd("Hello World");
7 //builder對象初始化完畢後,再通過build方法生成與之對應的消息類對象。
8 LogonReqMessage logonReq = logonReqBuilder.build();
9 int length = logonReq.getSerializedSize();
10 System.out.println("The result length is " + length);
11 //直接序列化到記憶體中,之後可對該記憶體進行二次加工後再寫到本地檔案或發送到遠端,如加密。
12 byte[] buf = logonReq.toByteArray();
13
14 try {
15 LogonReqMessage logonReq2 = LogonReqMessage.parseFrom(buf);
16 System.out.println("acctID = " + logonReq2.getAcctID() + "\tpassword = " + logonReq2.getPasswd());
17 } catch (InvalidProtocolBufferException e) {
18 e.printStackTrace();
19 }
20 //需要說明的是,檔案中的内容是由之前C++執行個體代碼寫入的,這裡這樣寫主要是一種驗證。
21 System.out.println("Reading data from local file generated by C++");
22 try {
23 LogonReqMessage logonReq3 = LogonReqMessage.parseFrom(new FileInputStream("C:/Mine/LogonReq.dat"));
24 System.out.println("acctID = " + logonReq3.getAcctID() + "\tpassword = " + logonReq3.getPasswd());
25 } catch (FileNotFoundException e) {
26 e.printStackTrace();
27 } catch (IOException e) {
28 e.printStackTrace();
29 }
30 }
三、嵌套message生成的Java代碼。
enum UserStatus {
OFFLINE = 0;
ONLINE = 1;
}
enum LoginResult {
LOGON_RESULT_SUCCESS = 0;
LOGON_RESULT_NOTEXIST = 1;
LOGON_RESULT_ERROR_PASSWD = 2;
LOGON_RESULT_ALREADY_LOGON = 3;
LOGON_RESULT_SERVER_ERROR = 4;
}
message UserInfo {
required int64 acctID = 1;
required string name = 2;
required UserStatus status = 3;
}
message LogonRespMessage {
required LoginResult logonResult = 1;
required UserInfo userInfo = 2; //這裡嵌套了UserInfo消息。
}
對于上述消息生成的Java代碼,UserInfo因為隻是包含了原始類型字段,是以和上例中的LogonReqMessage沒有太多的差别,這裡也就不在重複列出了。由于LogonRespMessage消息中嵌套了UserInfo類型的字段,在這裡我們将僅僅給出該消息生成的Java代碼和關鍵性注釋。
1 public static final class LogonRespMessage extends
2 com.google.protobuf.GeneratedMessageLite
3 implements LogonRespMessageOrBuilder {
4
5 //Message類的通用性函數定義。
6 ... ...
7
8 // required .LoginResult logonResult = 1;
9 public static final int LOGONRESULT_FIELD_NUMBER = 1;
10 public boolean hasLogonResult() {
11 return ((bitField0_ & 0x00000001) == 0x00000001);
12 }
13 public com.lsk.lyphone.LYPhoneMessage.LoginResult getLogonResult() {
14 return logonResult_;
15 }
16
17 // required .UserInfo userInfo = 2;
18 public static final int USERINFO_FIELD_NUMBER = 2;
19 public boolean hasUserInfo() {
20 return ((bitField0_ & 0x00000002) == 0x00000002);
21 }
22 public com.lsk.lyphone.LYPhoneMessage.UserInfo getUserInfo() {
23 return userInfo_;
24 }
25 //Message類的通用性函數定義。可參照上一小節中的代碼和注釋。
26 ... ...
27
28 public static final class Builder extends
29 com.google.protobuf.GeneratedMessageLite.Builder<
30 com.lsk.lyphone.LYPhoneMessage.LogonRespMessage, Builder>
31 implements com.lsk.lyphone.LYPhoneMessage.LogonRespMessageOrBuilder {
32
33 //一些适用于絕大多數Builder對象的通用性方法。
34 ... ...
35
36 //目前示例中Builder生成的代碼和上一小節中生成的代碼非常類似,這裡就不一一贅述了。
37 //和前面的例子相比一個重要的差别是setUserInfo函數多提供了一種函數簽名,其參數為
38 //UserInfo類的Builder對象。這樣調用者在使用時可以直接将Builder對象作為參數傳入。
39 public Builder setUserInfo(com.lsk.lyphone.LYPhoneMessage.UserInfo.Builder builderForValue) {
40 userInfo_ = builderForValue.build();
41 bitField0_ |= 0x00000002;
42 return this;
43 }
44 }
45 }
下面是讀寫LogonRespMessage對象的Java測試代碼和說明性注釋。
1 private static void testNestedMessage() {
2 System.out.println("==================This is nested message.================");
3 LogonRespMessage.Builder logonRespBuilder = LogonRespMessage.newBuilder();
4 logonRespBuilder.setLogonResult(LoginResult.LOGON_RESULT_SUCCESS);
5 UserInfo.Builder userInfo = UserInfo.newBuilder();
6 userInfo.setAcctID(200);
7 userInfo.setName("Tester");
8 userInfo.setStatus(UserStatus.OFFLINE);
9 //這裡也可以直接傳遞userInfo對象作為參數。因為LogonRespBuilder類提供了setUserInfo的方法重載。
10 logonRespBuilder.setUserInfo(userInfo.build());
11 LogonRespMessage logonResp = logonRespBuilder.build();
12 int length = logonResp.getSerializedSize();
13 System.out.println("The result length is " + length);
14 byte[] buf = logonResp.toByteArray();
15
16 try {
17 LogonRespMessage logonResp2 = LogonRespMessage.parseFrom(buf);
18 UserInfo userInfo2 = logonResp2.getUserInfo();
19 System.out.println("LogonResult = " + logonResp2.getLogonResult().toString() + " acctID = "
20 + userInfo2.getAcctID() + " name = " + userInfo2.getName() + " status = " + userInfo2.getStatus().toString());
21 } catch (InvalidProtocolBufferException e) {
22 e.printStackTrace();
23 }
24 System.out.println("Reading data from local file generated by C++");
25 try {
26 LogonRespMessage logonResp3 = LogonRespMessage.parseFrom(new FileInputStream("C:/Mine/LogonResp.dat"));
27 UserInfo userInfo3 = logonResp3.getUserInfo();
28 System.out.println("LogonResult = " + logonResp3.getLogonResult().toString() + " acctID = "
29 + userInfo3.getAcctID() + " name = " + userInfo3.getName() + " status = " + userInfo3.getStatus().toString());
30 } catch (FileNotFoundException e) {
31 e.printStackTrace();
32 } catch (IOException e) {
33 e.printStackTrace();
34 }
35 }
四、repeated嵌套message生成的Java代碼。
message BuddyInfo {
required UserInfo userInfo = 1;
required int32 groupID = 2;
}
message RetrieveBuddiesResp {
required int32 buddiesCnt = 1;
repeated BuddyInfo buddiesInfo = 2;
}
對于上述消息生成的代碼,我們将隻是針對RetrieveBuddiesResp消息所對應的Java代碼進行詳細說明,其餘部分和前面小節的例子基本相同,可直接參照。而對于RetrieveBuddiesResp類中的代碼,我們也僅僅是對buddiesInfo字段生成的代碼進行更為詳細的解釋。
1 public static final class RetrieveBuddiesResp extends
2 com.google.protobuf.GeneratedMessageLite
3 implements RetrieveBuddiesRespOrBuilder {
4 //這裡均為Protocol Buffer生成的通用性代碼。
5 ... ...
6 // repeated .BuddyInfo buddiesInfo = 2;
7 public static final int BUDDIESINFO_FIELD_NUMBER = 2;
8 //對于repeated類型的字段,均傳回類型參數為字段類型的泛型容器對象。
9 public java.util.List<com.lsk.lyphone.LYPhoneMessage.BuddyInfo> getBuddiesInfoList() {
10 return buddiesInfo_;
11 }
12 public java.util.List<? extends com.lsk.lyphone.LYPhoneMessage.BuddyInfoOrBuilder> getBuddiesInfoOrBuilderList() {
13 return buddiesInfo_;
14 }
15 public int getBuddiesInfoCount() {
16 return buddiesInfo_.size();
17 }
18 public com.lsk.lyphone.LYPhoneMessage.BuddyInfo getBuddiesInfo(int index) {
19 return buddiesInfo_.get(index);
20 }
21 public com.lsk.lyphone.LYPhoneMessage.BuddyInfoOrBuilder getBuddiesInfoOrBuilder(int index) {
22 return buddiesInfo_.get(index);
23 }
24
25 //這裡仍有一些Protocol Buffer生成的通用性代碼。
26 ... ...
27
28 public static final class Builder extends
29 com.google.protobuf.GeneratedMessageLite.Builder<
30 com.lsk.lyphone.LYPhoneMessage.RetrieveBuddiesResp, Builder>
31 implements com.lsk.lyphone.LYPhoneMessage.RetrieveBuddiesRespOrBuilder {
32
33 //這裡僅列出和操作repeated字段相關的方法,其他的方法和前面的例子基本一緻。
34 // repeated .BuddyInfo buddiesInfo = 2;
35 //本來打算給出比較詳細的說明,但是看到Google為每個函數的命名之後就放棄這個想法,
36 //這樣一來不僅可以避免畫蛇添足,而且也節省了時間。:)
37 public java.util.List<com.lsk.lyphone.LYPhoneMessage.BuddyInfo> getBuddiesInfoList() {
38 return java.util.Collections.unmodifiableList(buddiesInfo_);
39 }
40 public int getBuddiesInfoCount() {
41 return buddiesInfo_.size();
42 }
43 public com.lsk.lyphone.LYPhoneMessage.BuddyInfo getBuddiesInfo(int index) {
44 return buddiesInfo_.get(index);
45 }
46 public Builder setBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) {
47 ... ...
48 }
49 public Builder setBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) {
50 ... ...
51 }
52 public Builder addBuddiesInfo(com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) {
53 ... ...
54 }
55 public Builder addBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) {
56 ... ...
57 }
58 public Builder addBuddiesInfo(com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) {
59 ... ...
60 }
61 public Builder addBuddiesInfo(
62 int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) {
63 ... ...
64 }
65 public Builder addAllBuddiesInfo(
66 java.lang.Iterable<? extends com.lsk.lyphone.LYPhoneMessage.BuddyInfo> values) {
67 ... ...
68 }
69 public Builder clearBuddiesInfo() {
70 ... ...
71 }
72 public Builder removeBuddiesInfo(int index) {
73 ... ...
74 }
75 }
76 }
下面是讀寫RetrieveBuddiesResp對象的Java測試代碼和說明性注釋。
1 private static void testRepeatedMessage() {
2 System.out.println("==================This is repeated message.================");
3 RetrieveBuddiesResp.Builder retrieveBuddiesBuilder = RetrieveBuddiesResp.newBuilder();
4 retrieveBuddiesBuilder.setBuddiesCnt(2);
5 BuddyInfo.Builder buddyInfoBuilder = BuddyInfo.newBuilder();
6 buddyInfoBuilder.setGroupID(20);
7 UserInfo.Builder userInfoBuilder = UserInfo.newBuilder();
8 userInfoBuilder.setAcctID(200);
9 userInfoBuilder.setName("user1");
10 userInfoBuilder.setStatus(UserStatus.OFFLINE);
11 buddyInfoBuilder.setUserInfo(userInfoBuilder.build());
12 retrieveBuddiesBuilder.addBuddiesInfo(buddyInfoBuilder.build());
13
14 buddyInfoBuilder = BuddyInfo.newBuilder();
15 buddyInfoBuilder.setGroupID(21);
16 userInfoBuilder = UserInfo.newBuilder();
17 userInfoBuilder.setAcctID(201);
18 userInfoBuilder.setName("user2");
19 userInfoBuilder.setStatus(UserStatus.ONLINE);
20 buddyInfoBuilder.setUserInfo(userInfoBuilder);
21 retrieveBuddiesBuilder.addBuddiesInfo(buddyInfoBuilder);
22 RetrieveBuddiesResp buddiesResp = retrieveBuddiesBuilder.build();
23
24 int length = buddiesResp.getSerializedSize();
25 System.out.println("The result length is " + length);
26 byte[] buf = buddiesResp.toByteArray();
27
28 try {
29 RetrieveBuddiesResp buddiesResp2 = RetrieveBuddiesResp.parseFrom(buf);
30 System.out.println("BuddiesCount = " + buddiesResp2.getBuddiesCnt());
31 System.out.println("Repeated Size = " + buddiesResp2.getBuddiesInfoCount());
32 for (int i = 0; i < buddiesResp2.getBuddiesInfoCount(); ++i) {
33 BuddyInfo buddyInfo = buddiesResp2.getBuddiesInfo(i);
34 UserInfo userInfo = buddyInfo.getUserInfo();
35 System.out.println("GroupID = " + buddyInfo.getGroupID() + " UserInfo.acctID = " + userInfo.getAcctID()
36 + " UserInfo.name = " + userInfo.getName() + " UserInfo.status = " + userInfo.getStatus());
37 }
38
39 } catch (InvalidProtocolBufferException e) {
40 e.printStackTrace();
41 }
42 System.out.println("Reading data from local file generated by C++");
43 try {
44 RetrieveBuddiesResp buddiesResp3 = RetrieveBuddiesResp.parseFrom(new FileInputStream("C:/Mine/RetrieveBuddiesResp.dat"));
45 System.out.println("BuddiesCount = " + buddiesResp3.getBuddiesCnt());
46 System.out.println("Repeated Size = " + buddiesResp3.getBuddiesInfoCount());
47 List<BuddyInfo> buddiesInfo = buddiesResp3.getBuddiesInfoList();
48 for (BuddyInfo buddyInfo : buddiesInfo) {
49 UserInfo userInfo = buddyInfo.getUserInfo();
50 System.out.println("GroupID = " + buddyInfo.getGroupID() + " UserInfo.acctID = " + userInfo.getAcctID()
51 + " UserInfo.name = " + userInfo.getName() + " UserInfo.status = " + userInfo.getStatus());
52 }
53 } catch (FileNotFoundException e) {
54 e.printStackTrace();
55 } catch (IOException e) {
56 e.printStackTrace();
57 }
58 }
對于Java而言,我們可以通過Maven工具生成兩個jar包,其中一個是protobuf-java-2.4.1.jar,主要用于optimize_for選項為非LITE_RUNTIME的情況,而另一個protobuf-java-2.4.1-lite.jar檔案則恰恰與之相反。另外,我通過Beyond Compare工具對這兩個jar包進行了二進制比較後發現,他們是完全相同的。這裡之是以仍以LITE版本為例,主要還是因為和之前一篇Blog(C++執行個體)想比對。
最後需要說明的是,Protocol Buffer仍然提供了很多其它非常有用的功能,特别是針對序列化的目的地,比如檔案流和網絡流等。與此同時,也提供了完整的官方文檔和規範的命名規則,在很多情況下,可以直接通過函數的名字便可獲悉函數所完成的工作。
該Blog中使用的示例代碼均已附件的方式上傳,歡迎大家下載下傳并指正。
轉載請注明本文位址: Protocol Buffer技術詳解(Java執行個體)