Java学习笔记 -- 序列化与反序列化(Protocol buff 与 Java序列化协议的比较)

1. 什么是序列化与反序列化?
  • 序列化就是将代码中的对象的某一个状态转化成字节数组的过程,也就是转化成二进制文件的过程。反序列化与之相反。
2. 为什么要进行序列化?
  • 在将对象存储在文件中或者通过网络进行传输的时候,对象是不能直接存储和传输的,所以要将它序列化为对应的二进制代码。
3. 实现序列化的常用方式有哪些?
  • 使用Java的序列化协议(实现Serializable接口)
  • 使用Google的序列化协议(Protocol buff)
4.两种序列化方法的区别有哪些?
  • 首先来看Java序列化协议的序列化过程
/**
 * @Description 对象实体类实现Serializable接口
 */
public class User implements Serializable {

    private int id;

    private String name;

    private String password;

    constructor and getters、setters...
}

 /**
 * 对象反序列化过程
 * @param bytes
 * @throws IOException
 * @throws ClassNotFoundException
 */
public static void toUser(byte[] bytes) throws IOException, ClassNotFoundException {

    ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
    User user = (User) inputStream.readObject();
    System.out.println("id: " + user.getId());
    System.out.println("name: " + user.getName());
    System.out.println("password: " + user.getPassword());
}

/**
* 对象序列化过程
* @return
* @throws IOException
*/
public static byte[] toBytes() throws IOException {

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    ObjectOutputStream outputStream = new ObjectOutputStream(stream);
    User user = new User(1,"admin", "123");
    outputStream.writeObject(user);
    byte[] bytes = stream.toByteArray();
    System.out.println(Arrays.toString(bytes));
    return bytes;
}
### 结果
[-84, -19, 0, 5, 115, 114, 0, 13, 99, 111, 109, 46, 106, 97, 118, 97, 46, 85, 115, 101, 114, 98, 0, -44, -50, -126, 35, -58, 126, 2, 0, 3, 73, 0, 2, 105, 100, 76, 0, 4, 110, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 76, 0, 8, 112, 97, 115, 115, 119, 111, 114, 100, 113, 0, 126, 0, 1, 120, 112, 0, 0, 0, 1, 116, 0, 5, 97, 100, 109, 105, 110, 116, 0, 3, 49, 50, 51]
id: 1
name: admin
password: 123
  • Protocol buff序列化对象的过程(文件配置省略了,读者自行百度)
//user.proto 文件
option java_package = "com.proto";
option java_outer_classname = "UserModule";

message User {
	required int32 id = 1;
	required string name = 2;
	required string password = 3;
}
/**
 * 反序列化
 * @param bytes
 * @throws InvalidProtocolBufferException
 */
public static void toUser(byte[] bytes) throws InvalidProtocolBufferException {

    //反序列化为对象
    UserModule.User user = UserModule.User.parseFrom(bytes);
    System.out.println("id: " + user.getId());
    System.out.println("username: " + user.getName());
    System.out.println("password: " + user.getPassword());

}

/**
 * 序列化
 * @return
*/
public static byte[] toBytes() {

    //获取一个PBUser构造器
    UserModule.User.Builder builder = UserModule.User.newBuilder();
    //设置数据
    builder.setId(1).setName("admin").setPassword("123");
    //构造对象
    UserModule.User user = builder.build();
    //序列化
    byte[] bytes = user.toByteArray();

    System.out.println(Arrays.toString(bytes));

    return bytes;
}
### 结果
[8, 1, 18, 5, 97, 100, 109, 105, 110, 26, 3, 49, 50, 51]
id: 1
username: admin
password: 123

所以从结果来看,java序列化要比protobuf序列化数组长很多,也就意味着进行网络传输时protobuf所需带宽更少,传输效率更高。

5. 产生这种区别的原因是什么?
  • Java的序列化包含了类名,字段名,字段属性,字段值等信息,而Protocolbuf只包含了字段值,其余信息是通过配置文件表示的。
  • Protocolbuf存取字节数组的方式采用的可伸缩性的方法。
    例如对int字段的存取,Java序列化是固定长度的,比如int就是四个字节
/**
* Java存储方式
* @param i
* @return
*/
public static byte[] int2bytes(int i){
	byte[] bytes = new byte[4];
	bytes[0] = (byte)(i >> 3*8);
	bytes[1] = (byte)(i >> 2*8);
	bytes[2] = (byte)(i >> 1*8);
	bytes[3] = (byte)(i >> 0*8);
	return bytes;
}

而Procobuf采用伸缩方式,对于int存取1-5个字节

/**
* Encode and write a varint.  {@code value} is treated as
* unsigned, so it won't be sign-extended if negative.
*/
public void writeRawVarint32(int value) throws IOException {
    while (true) {
    /*0x7f   0111 1111
      ~0x7f  1000 0000
      value&~0x7f 就是除去value(32位)的后7位后判断是否为0
      即判断value是否小于 0x7f*/
    if ((value & ~0x7F) == 0) {
      writeRawByte(value);
      return;
    } else { //如果不是,把value的后7位加上第八位1写入,即写入1xxx xxxx,然后value右移7位
      writeRawByte((value & 0x7F) | 0x80);
      value >>>= 7;
    }
}

Protocolbuff将每一个字节的最高位定义为这个字节是否有数据的标记位,没有数据的字节不存,所以int总共有32-4=28位可用,所以如果大于28位的int值将会多用一个字节进行存,所以int占用1-5个字节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chook_lxk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值