ProtoBuf使用指南Java基础篇-Proto3

本文是Google Protocol Buffers(ProtoBuf)在Java中的基础教程,讲解了为何使用ProtoBuf,如何定义消息格式,编译ProtoBuf文件,以及如何使用生成的API进行消息的序列化和反序列化。内容涵盖枚举、嵌套类、消息的读写、扩展性和高级用法,是理解ProtoBuf在Java应用中的核心指南。
摘要由CSDN通过智能技术生成


Protocol Buffer Basics: Java | Protocol Buffers | Google Developers

  • .proto 文件中定义消息格式
  • 使用协议缓冲区编译器
  • 使用Java协议缓冲区API来写入和读取消息

为什么要使用协议缓冲区?

我们将要使用的示例是一个非常简单的“地址簿”应用程序,它可以从文件中读取和写入人们的联系人详细信息。地址簿中的每个人都有一个姓名、一个ID、一个电子邮件地址和一个联系电话号码。

如何序列化和检索这样的结构化数据?有几种方法可以解决这个问题:

  • 使用Java序列化。这是默认的方法,因为它是内置于语言中的,但是它有许多众所周知的问题 (参见 Effective Java, by Josh Bloch pp. 213),如果您需要与用C+或Python编写的应用程序共享数据,也不能很好地工作。
  • 您可以发明一种特殊的方法将数据项编码为单个字符串,例如将4个INT编码为“12:3:-23:67”。这是一种简单而灵活的方法,尽管它确实需要编写一次性编码和解析代码,并且解析带来了较小的运行时成本。这对编码非常简单的数据最有效。
  • 将数据序列化为XML。这种方法非常有吸引力,因为XML(某种程度上)是人类可读的,并且有许多语言的绑定库。如果您想要与其他应用程序/项目共享数据,这可能是一个很好的选择。然而,XML是众所周知的空间密集型,它的编码/解码会给应用程序带来巨大的性能损失。另外,在XML DOM树中导航比在类中导航简单字段要复杂得多。

协议缓冲区是解决这一问题的灵活、高效、自动化的解决方案。使用协议缓冲区,您可以编写一个.proto要存储的数据结构的说明。由此,协议缓冲区编译器创建一个类,该类以高效的二进制格式实现协议缓冲区数据的自动编码和解析。生成的类为组成协议缓冲区的字段提供getter和setter,并负责作为一个单元读取和写入协议缓冲区的详细信息。重要的是,协议缓冲区格式支持随着时间的推移扩展格式的思想,以便代码仍然可以读取旧格式编码的数据。

示例代码下载

示例代码包含在“示例”目录下的源代码包中。在这里下载。
https://developers.google.cn/protocol-buffers/docs/downloads

定义协议格式

要创建通讯簿应用程序,您需要从一个.proto文件开始。定义一个.proto文件很简单:对于每个要序列化的数据结构你可以添加一个message,然后为message中的每个字段指定名称和类型。这是.proto定义您的message的文件,addressbook.proto.

syntax = "proto2";

package = tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

如您所见,语法类似于C+或Java。让我们检查一下文件的每个部分,看看它能做什么。

这个.proto文件以包声明开始,这有助于防止不同项目之间的命名冲突。在Java中,除非您已经显式地指定了java_package就像我们在这里一样。即使你提供了java_package,您仍然应该定义一个正常的package。此外,为了避免协议缓冲区中的名称冲突,以及在非Java语言中,名称空间也是如此。

在包声明之后,您可以看到两个特定于Java的选项:java_packagejava_outer_classname. java_package在Java包名称中指定生成的类应该使用的名称。如果没有显式地指定此名称,则它与package声明,但这些名称通常不是适当的Java包名称(因为它们通常不以域名开头)。这个java_outer_classname选项定义类名,该类名应包含该文件中的所有类。如果你显式地提供java_outer_classname,它将通过将文件名转换为CAMEL(驼峰式)大小写来生成。例如,默认情况下,“my_pro.proto”将使用“MyProto”作为外部类名。

接下来,您将得到您的消息定义。消息只是包含一组类型化字段的聚合。许多标准的简单数据类型都可以作为字段类型使用,包括boolint32floatdoublestring。还可以将其他消息类型作为字段类型添加到消息中-在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息中的消息类型-正如您所看到的,PhoneNumber类型是在Person中定义的。如果您希望您的一个字段具有一个预定义的值列表,您也可以定义enum类型-在这里,您希望指定电话号码可以是MOBILEHOMEWORK

每个元素上的“=1”、“=2”标记标识字段在二进制编码中使用的唯一“tag”。标签号 1-15 比较高的数字需要少一个字节来编码,因此,作为优化,您可以决定将这些标记用于常用的或重复的元素,留下标记16或更高的标记用于较少使用的可选元素。重复字段中的每个元素都需要重新编码标记号,因此重复字段是这种优化的最佳选择。

必须用下列修饰符之一对每个字段进行注释:

  • required:必须提供字段的值,否则该消息将被视为“未初始化”。试图构建未初始化的消息将引发RuntimeException。解析未初始化的消息将引发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值