在Java中使⽤protobuf序列化对象
什么是protobuf
它是⼀个对象序列化/反序列化的⼯具,什么是对象的序列化/反序列化?就是把⼀个Java堆中存活的对象转换成⼀串⼆进制编码,然后该编码可以⽤于本地存储和⽹络传输。反序列化就是根据⼀串⼆进制编码还原出原来的那个对象,protobuf能够将⼀个对象以特定的格式转换为⼀个⼆进制串(序列化),然后将⼆进制串还原成对象(反序列化)。这⾥涉及到两个指标:
对同⼀个⽬标对象:
1)序列化和反序列化的时间开销,
2)序列化之后串的长度
protobuf在这两个⽅⾯都有⾮常出⾊的表现(⽹传)
在Windows下使⽤protobuf的步骤如下:
第⼀步:
下载protoc-2.5.0-win32.zip,得到其中的然后将该的存放路径加⼊Path环境变量,便于访问。⽐如,我的存放于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开始。
可以指定字段类型为其他的Message字段类型:
message SearchResponse{
repeated Result result = 1;
}
message Result{
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
可以在message内部嵌套另⼀个message:
message SearchResponse{
message Result{
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
repeated Result result = 1;
}
第三步:
⽤⽣成PersonEntity.class。打开DOS窗⼝,输⼊如下的编译命令:
< -I=D:/protobuf --java_out=D:/protobuf D:/protobuf/person-entity.proto
编译命令的格式如下:
< -I=.protoc⽂件的存放路径 --java_out=.class⽂件的输出路径 .protoc⽂件的具体存放路径
其中第三个路径是必须的,⽽且要写明.protoc⽂件
最后会⽣成PersonEntity.java⽂件,可以把它理解为⼀个⼯具类,帮助我们执⾏对象的序列化。
第四步:
新建maven项⽬,将PersonEntity.java复制到项⽬中,引⼊maven依赖
<dependency>
<groupId&le.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>
利⽤⼯具类序列化和反序列化对象。
/*
author:chxy
data:2020/4/1
description:
*/
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
//模拟将对象转成byte[],⽅便传输
PersonEntity.Person.Builder builder = wBuilder();
builder.setId(1);
builder.setName("abc");
builder.setEmail("def");
PersonEntity.Person person = builder.build();
System.out.println("after :" +String());
final byte[] bytes = ByteArray();
System.out.println("===========Person Byte==========");
for(byte b : ByteArray()){
System.out.print(b);
}
System.out.println();
//反序列化成Person类
byte[] byteArray =ByteArray();
PersonEntity.Person person2 = PersonEntity.Person.parseFrom(byteArray);
System.out.println("after :" +String());
}
}
这⾥还犯过⼀个错:另外⾃定义⼀个Person类与PersonEntity.Person相对应。实际上在我们的项⽬中可以直接利⽤这个PersonEntity.Person来实现功能需求。
下⾯同样以这个Person类序列化为例,来实际地⽐较通过各种不同的序列化⽅式得到的的字节数组长度:
1.protobuf
序列化长度为:
System.out.SerializedSize());
结果为:12
2.Java原⽣序列化
author:chxy
data:2020/4/2
java中字符串转数组description:
*/
import java.io.*;
public class Test2 {
public static void main(String[] args) throws IOException {
Person p = new Person(1,"abc","def");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
new FileOutputStream("E:/"));
objectOutputStream.writeObject(p);
FileInputStream fileInputStream = new FileInputStream("E:/");
final int length = ad();
System.out.println(length);
}
}
结果为:172
3.通过对象的toString()
序列化:重写对象的toString()⽅法,按照特定的格式,将对象的各个字段封装进⼀个String字符串,再将字符串按照特定的编码⽅式,转换成byte数组,实现本地存储和⽹络传输。
反序列化:对byte数组,重新转换成字符串,再将字符串按照约定的格式,还原成对象的属性,从⽽构造出原来的对象。
这⾥重点探讨的是:String对象转换成byte数组之后的长度
public static void main(String[] args) {
String s = "abc";
System.out.Bytes().length);
String s2 = "嘤嘤嘤";
System.out.Bytes().length);
System.out.println(Charset.defaultCharset());
}
输出结果为:
3
9
UTF-8
可见,如果是英⽂字母每个字符占⼀个字节,如果是汉字,每个字符占3个字节。如果通过这种⽅式序列化,它的空间效率也是⾮常⾼的。
这⾥有必要搞清楚Java默认的字符编码集:
/**
* Encodes this {@code String} into a sequence of bytes using the
* platform's default charset, storing the result into a new byte array.
*
* <p> The behavior of this method when this string cannot be encoded in
* the default charset is unspecified. The {@link
* java.nio.charset.CharsetEncoder} class should be used when more control
* over the encoding process is required.
*
* @return The resultant byte array
*
* @since JDK1.1
*/
public byte[] getBytes() {
de(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的⼀种解释。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论