Java序列化-Serializable和ProtocolBuffers

文章目录

  • Java序列化-Serializable和ProtocolBuffers
        • 1.什么是Java序列化以及Java序列化的作用
        • 2.什么时候需要Java序列化
        • 3.Java序列化的基本使用
            • Player.java
            • SerUtils .java
            • 运行结果:
        • 4.Protocol Buffers
          • 4.1 什么是Protocol Buffers?
          • 4.2 为什么使用Protocol Buffers?
          • 4.3 Protocol Buffers的基本使用
            • player.proto
            • build.bat
            • pom.xml
            • PBUtils.java
            • 运行结果:
        • 5.自定义序列化协议
            • Test1.java
            • 运行结果:
            • Test2.java
            • 运行结果:
            • pom.xml
            • Test3.java
            • 运行结果:
            • BufferFactory.java
            • AbstractSerializable.java
            • Resource.java
            • Player.java
            • Test4.java
            • 运行结果:

Java序列化-Serializable和ProtocolBuffers

1.什么是Java序列化以及Java序列化的作用

 Java平台允许我们在内存中创建可复用的Java对象,一般情况下只有当JVM处于运行时,这些对象才可能存在,所以这些对象的生命周期比 JVM的生命周期更短暂。但现实应用中可能要求JVM在停止运行之后能够保存(持久化)指定的对象,并在之后需要时可重新读取被持久化的对象。Java对象序列化就能够帮助我们实现该要求。但是需要注意,对象序列化static(代表状态)以及transient(代表临时数据)不能够被序列化。
 所谓序列化其实就是将程序中的数据通过特定方式保存到本地中。然后把Java对象转换为字节序列的过程称为对象的序列化。核心就是将数据分解成字节流以便存储在文件中或者在网络中传输。就好比我们现在拥有一件体积很大的物品,然后把他尽可能地通过某种方法拆卸变成更小的零件寄出去给被人,别人再通过这种方法把它组装起来变回原本的样子,这一步也就是我们所说的反序列化,Java序列化的过程大概就是这样。

2.什么时候需要Java序列化

  1. 将内存中的数据保存至数据库或者磁盘文件中。
  2. 使用套接字进行网络传输数据。
  3. 对象序列化可以实现分布式对象。使用RMI远程方法调用传输数据。

3.Java序列化的基本使用

 使用的比较都的就是对象实现Serializable序列化接口,这个接口的主要作用就是标识该对象可序列化,在传输对象时JRE会进行相应的封装,这里给出一个基本使用的例子。

需要序列化的对象需要先实现Serializable接口

Player.java
package com.serializable;

import java.io.Serializable;
import java.util.List;

/**
 * java序列化对象
 * @author hzk
 * @date 2018/9/20
 */
public class Player implements Serializable{

    private static final long serialVersionUID = -1602899989398586421L;

    private Integer playerId;
    private String name;
    private Integer age;
    private List<Integer> skills;

    public Player(Integer playerId, String name, Integer age,List<Integer> skills) {
        this.playerId = playerId;
        this.name = name;
        this.age = age;
        this.skills = skills;
    }

    public Integer getPlayerId() {
        return playerId;
    }

    public void setPlayerId(Integer playerId) {
        this.playerId = playerId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public List<Integer> getSkills() {
        return skills;
    }

    public void setSkills(List<Integer> skills) {
        this.skills = skills;
    }
}

 由于对象已经实现了序列化接口,这里我们对他进行序列化和反序列化操作验证

SerUtils .java
package com.serializable;

import com.google.protobuf.InvalidProtocolBufferException;
import com.proto.PlayerModule;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * java序列化转换
 * @author hzk
 * @date 2018/9/20
 */
public class SerUtils {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        byte[] bytes = toBytes();
        toPlayer(bytes);
    }

    /**
     * 序列化
     * @return
     * @throws IOException
     */
    private static byte[] toBytes() throws IOException {
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(10);
        Player player = new Player(10, "Peter", 18,integers);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        //写入对象
        objectOutputStream.writeObject(player);

        //获取字节数据
        byte[] bytes = byteArrayOutputStream.toByteArray();
        System.out.println(Arrays.toString(bytes));
        return bytes;
    }

    /**
     * 反序列化
     * @param bs
     * @throws InvalidProtocolBufferException
     */
    private static void toPlayer(byte[] bs) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bs));
        Player player = (Player) objectInputStream.readObject();
        System.out.println("PlayInfo:"+player.getPlayerId()+":"+player.getName()+":"+player.getAge()+":"+player.getSkills());
    }
}

运行结果:
[-84, -19, 0, 5, 115, 114, 0, 23, 99, 111, 109, 46, 115, 101, 114, 105, 97, 108, 105, 122, 97, 98, 108, 101, 46, 80, 108, 97, 121, 101, 114, -23, -63, 90, -13, -63, 107, 123, -53, 2, 0, 4, 76, 0, 3, 97, 103, 101, 116, 0, 19, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 73, 110, 116, 101, 103, 101, 114, 59, 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, 108, 97, 121, 101, 114, 73, 100, 113, 0, 126, 0, 1, 76, 0, 6, 115, 107, 105, 108, 108, 115, 116, 0, 16, 76, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 76, 105, 115, 116, 59, 120, 112, 115, 114, 0, 17, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 73, 110, 116, 101, 103, 101, 114, 18, -30, -96, -92, -9, -127, -121, 56, 2, 0, 1, 73, 0, 5, 118, 97, 108, 117, 101, 120, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 78, 117, 109, 98, 101, 114, -122, -84, -107, 29, 11, -108, -32, -117, 2, 0, 0, 120, 112, 0, 0, 0, 18, 116, 0, 5, 80, 101, 116, 101, 114, 115, 113, 0, 126, 0, 5, 0, 0, 0, 10, 115, 114, 0, 19, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46, 65, 114, 114, 97, 121, 76, 105, 115, 116, 120, -127, -46, 29, -103, -57, 97, -99, 3, 0, 1, 73, 0, 4, 115, 105, 122, 101, 120, 112, 0, 0, 0, 1, 119, 4, 0, 0, 0, 1, 113, 0, 126, 0, 9, 120]
PlayInfo:10:Peter:18:[10]

4.Protocol Buffers

4.1 什么是Protocol Buffers?

 什么是Google Protocol Buffers?网上比较官方的说法是这样的:
 Google Protocol Buffers( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。
 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

 其实官方的解释已经能够大体解释清楚Protocol buffers是什么,其实它就是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,与XML相比,Protocol buffers序列化后的码流更小、速度更快、操作更简单。我们只需要将要被序列化的数据结构定义一次(使用.proto文件定义),便可以使用特别生成的源代码(使用protobuf提供的生成工具)轻松的使用不同的数据流完成对这些结构数据的读写操作,即使你使用不同的语言(protobuf的跨语言支持特性)。你甚至可以更新你的数据结构的定义(就是更新.proto文件内容)而不会破坏依赖“老”格式编译出来的程序。

4.2 为什么使用Protocol Buffers?

 首先我们对比一下普通Java序列化和Protobuf序列化转换字节的区别

Java序列化 [-84, -19, 0, 5, 115, 114, 0, 15, 99, 111, 109, 46, 106, 97, 118, 97, 46, 80, 108, 97, 121, 101, 114, -73, 43, 28, 39, -119, -86, -125, -3, 2, 0, 4, 73, 0, 3, 97, 103, 101, 74, 0, 8, 112, 108, 97, 121, 101, 114, 73, 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, 6, 115, 107, 105, 108, 108, 115, 116, 0, 16, 76, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 76, 105, 115, 116, 59, 120, 112, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 101, 116, 0, 5, 112, 101, 116, 101, 114, 115, 114, 0, 19, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46, 65, 114, 114, 97, 121, 76, 105, 115, 116, 120, -127, -46, 29, -103, -57, 97, -99, 3, 0, 1, 73, 0, 4, 115, 105, 122, 101, 120, 112, 0, 0, 0, 1, 119, 4, 0, 0, 0, 10, 115, 114, 0, 17, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 73, 110, 116, 101, 103, 101, 114, 18, -30, -96, -92, -9, -127, -121, 56, 2, 0, 1, 73, 0, 5, 118, 97, 108, 117, 101, 120, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 78, 117, 109, 98, 101, 114, -122, -84, -107, 29, 11, -108, -32, -117, 2, 0, 0, 120, 112, 0, 0, 3, -23, 120]
ProtoBuf [8, 101, 16, 20, 26, 5, 112, 101, 116, 101, 114, 32, -23, 7]

 从转换字节的对比上就能很明显发现ProtoBuf的优点之一:数据转换占用空间小
 其实ProtoBuf所做的事情在之前更多的是由XML完成,但是ProtoBuf的优点远不止于此,下面几点也是ProtoBuf的会逐渐成为更多开发者序列化数据选择的关键

  1. 平台无关,语言无关,可扩展;
  2. 提供了友好的动态库,使用简单;
  3. 解析速度快,比对应的XML快约20-100倍;
  4. 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10;
  5. 自动生成数据访问类方便应用程序的使用;
4.3 Protocol Buffers的基本使用

 在使用ProtoBuf之前首先我们需要知道我们要先编写一个.proto文件去指定我们所需要序列化的数据结构。下面在介绍如何使用时会举出这里我们用到的例子,关于ProtoBuf这种数据描述语言这里推荐两篇博客介绍一些规范,可以给大家可以参考一下
Protobuf3语言指南
google protobuf数据类型

player.proto
option java_package = "com.proto";
option java_outer_classname = "PlayerModule";

message PBPlayer{
    required int64 playerId = 100;
    required int32 age = 10;
    required string name = 111;
    required int32 skills = 8;
}

message PBResource{
    required int64 gold = 8;
    required int32 energy = 100;
}

 编写好了.proto文件后,我们要生成对应的代码要先从网上将protoc.exe下载下来,这里我一并放在一个目录下方便执行,为了方便生成编写一个.bat脚本
ProtoBuf源码
ProtoBuf程序

build.bat
protoc ./*.proto --java_out=./
pause

 使用之前还需要导入对应版本的Jar包

pom.xml
 
 <dependency>
     <groupId>com.google.protobufgroupId>
     <artifactId>protobuf-javaartifactId>
     <version>2.4.1version>
 dependency>

 执行脚本之后会发现当前目录下自动生成了一个层级目录,里面生成了一个java文件 PlayerModule.java
 这里一大串代码都是ProtoBuf根据我们自己编写的.proto文件自动生成的,这里我们来简单地使用达成和Java序列化一样的效果

PBUtils.java
package com.proto;

import com.google.protobuf.InvalidProtocolBufferException;

import java.util.Arrays;

/**
 * protocol buffers 转换
 * @author hzk
 * @date 2018/9/20
 */
public class PBUtils {

    public static void main(String[] args) throws InvalidProtocolBufferException {
        byte[] bytes = toBytes();
        toPlayer(bytes);
    }

    /**
     * 序列化
     * @return
     */
    private static byte[] toBytes(){
        //获取PBPlayer构造器
        PlayerModule.PBPlayer.Builder builder = PlayerModule.PBPlayer.newBuilder();
        //设置数据
        builder.setName("hhh").setAge(10).setSkills(10).setPlayerId(1);
        //构造对象
        PlayerModule.PBPlayer player = builder.build();
        //序列化成字节数组
        //This is supposed to be overridden by subclasses. 需要maven引用和生成java文件的版本相同
        byte[] bytes = player.toByteArray();
        System.out.println(Arrays.toString(bytes));
        return bytes;
    }

    /**
     * 反序列化
     * @param bs
     * @throws InvalidProtocolBufferException
     */
    private static void toPlayer(byte[] bs) throws InvalidProtocolBufferException {
        PlayerModule.PBPlayer player = PlayerModule.PBPlayer.parseFrom(bs);
        System.out.println("PlayInfo:"+player.getPlayerId()+":"+player.getName()+":"+player.getAge()+":"+player.getSkills());
    }
}
运行结果:
[64, 10, 80, 10, -96, 6, 1, -6, 6, 3, 104, 104, 104]
PlayInfo:1:hhh:10:10

 简单几步就能利用protobuf序列化我们需要的数据对象,并且可以发现序列化和反序列化的效率都十分高

5.自定义序列化协议

 根据不同的业务需求和不同的应用场景,我们可能需要采取不同的序列化方式,开发人员也会根据具体需要自定义所需序列化规则,这里介绍几种大家可能会接触到的自定义协议规则。
 第一种用的是大端字节序列,先写高位再写低位大端 小端和网络字节序说明

Test1.java
package com.customSerializable.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * @author hzk
 * @date 2018/9/25
 */
public class Test1 {
    
    public static void main(String[] args) throws IOException {
        int id = 111; // 0110 1111
        int age = 23; //0001 0111

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] idBytes = int2byte(id);
        byte[] ageBytes = int2byte(age);
        System.out.println("Bytes id:"+ Arrays.toString(idBytes));
        System.out.println("Bytes age:"+ Arrays.toString(ageBytes));
        byteArrayOutputStream.write(idBytes);
        byteArrayOutputStream.write(ageBytes);
        byte[] outBytes = byteArrayOutputStream.toByteArray();

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outBytes);
        byte[] idBytesIn = new byte[4];
        byteArrayInputStream.read(idBytesIn);
        System.out.println("Read Bytes id:"+Arrays.toString(idBytesIn));
        System.out.println("Read Bytes Trans id:"+byte2int(idBytesIn));

        byte[] ageBytesIn = new byte[4];
        byteArrayInputStream.read(ageBytesIn);
        System.out.println("Read Bytes age:"+Arrays.toString(ageBytesIn));
        System.out.println("Read Bytes Trans age:"+byte2int(ageBytesIn));

    }

    /**
     * 大端字节序列(先写高位,再写低位)
     * @param i
     * @return
     */
    private static byte[] int2byte(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;
    }

    /**
     * 大端字节反序列
     * @param bytes
     * @return
     */
    private static int byte2int(byte[] bytes){
        return (bytes[0] << 3*8)|(bytes[1] << 2*8)|(bytes[2] << 1*8)|(bytes[3] << 0*8);
    }

}
运行结果:
Bytes id:[0, 0, 0, 111]
Bytes age:[0, 0, 0, 23]
Read Bytes id:[0, 0, 0, 111]
Read Bytes Trans id:111
Read Bytes age:[0, 0, 0, 23]
Read Bytes Trans age:23

 第二种用到了NIO中提供的ByteBuffer,相比上面的方法更加方便就能分解成字节数据,但是缺点是需要指定要转换数据的字节大小

Test2.java
package com.customSerializable.test;

import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * ByteBuffer 需要指定大小
 * @author hzk
 * @date 2018/9/26
 */
public class Test2 {
    
    public static void main(String[] args){
        int id = 111;
        int age = 23;

        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.putInt(id);
        buffer.putInt(age);
        byte[] array = buffer.array();
        System.out.println(Arrays.toString(array));

        ByteBuffer wrap = ByteBuffer.wrap(array);
        System.out.println(wrap.getInt());
        System.out.println(wrap.getInt());
    }
}

运行结果:
[0, 0, 0, 111, 0, 0, 0, 23]
111
23

 第三种借助了Netty提供的ChannelBuffers,可以利用该对象动态生成缓冲字节数据的ChannelBuffer

pom.xml
<dependency>
    <groupId>io.nettygroupId>
    <artifactId>nettyartifactId>
    <version>3.10.5.Finalversion>
dependency>
Test3.java
package com.customSerializable.test;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;

import java.util.Arrays;

/**
 * ChannelBuffers Netty工具
 * @author hzk
 * @date 2018/9/26
 */
public class Test3 {
    
    public static void main(String[] args){
        ChannelBuffer channelBuffer = ChannelBuffers.dynamicBuffer();
        channelBuffer.writeInt(111);
        channelBuffer.writeDouble(23.5);

        byte[] bytes = new byte[channelBuffer.writerIndex()];
        channelBuffer.readBytes(bytes);
        System.out.println(Arrays.toString(bytes));

        ChannelBuffer channelBuffer1 = ChannelBuffers.wrappedBuffer(bytes);
        System.out.println(channelBuffer1.readInt());
        System.out.println(channelBuffer1.readDouble());
    }
}
运行结果:
[0, 0, 0, 111, 64, 55, -128, 0, 0, 0, 0, 0]
111
23.5

 第四种利用Netty提供的ChannelBuffers根据自定义的规则(例如序列化String字符串类型数据时,首先写入一个short类型数据表示该字符串转换为字节数组的长度,再写入具体的字节数据,集合亦是如此)封装一个自定义序列化抽象类以供需要序列化的数据继承使用

BufferFactory.java
package com.customSerializable.core;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;

import java.nio.ByteOrder;

/**
 * ChannelBuffers工具类
 * @author hzk
 * @date 2018/9/26
 */
public class BufferFactory{

    public static ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;

    /**
     * 获取一个ChannelBuffer
     * @return
     */
    public static ChannelBuffer getBuffer(){
        ChannelBuffer channelBuffer = ChannelBuffers.dynamicBuffer();
        return channelBuffer;
    }

    /**
     * 获取一个ChannelBuffer 并写入数据
     * @param bytes
     * @return
     */
    public static ChannelBuffer getBuffer(byte[] bytes){
        ChannelBuffer channelBuffer = ChannelBuffers.copiedBuffer(bytes);
        return channelBuffer;
    }
}
AbstractSerializable.java
package com.customSerializable.core;

import org.jboss.netty.buffer.ChannelBuffer;

import java.nio.charset.Charset;
import java.util.*;

/**
 * 自定义序列化
 * @author hzk
 * @date 2018/9/26
 */
public abstract class AbstractSerializable {

    public static final Charset CHARSET = Charset.forName("UTF-8");

    protected ChannelBuffer writeBuffer;

    protected ChannelBuffer readBuffer;

    /**
     * 反序列化具体实现
     */
    protected abstract void read();

    /**
     * 序列化具体实现
     */
    protected abstract void write();

    /**
     * 从bytes数组读取数据
     * @param bytes
     * @return
     */
    public AbstractSerializable readFromBytes(byte[] bytes){
        readBuffer = BufferFactory.getBuffer(bytes);
        read();
        readBuffer.clear();
        return this;
    }

    /**
     * 从channelBuffer读取数据
     * @param channelBuffer
     */
    public void readFromBuffer(ChannelBuffer channelBuffer){
        this.readBuffer = channelBuffer;
        read();
    }

    /**
     * 写入到本地channelBuffer
     * @return
     */
    public ChannelBuffer writeToLocalBuffer(){
        this.writeBuffer = BufferFactory.getBuffer();
        write();
        return writeBuffer;
    }

    /**
     * 写入到目标channelBuffer
     * @param channelBuffer
     * @return
     */
    public ChannelBuffer writeToTargetBuffer(ChannelBuffer channelBuffer){
        this.writeBuffer = channelBuffer;
        write();
        return writeBuffer;
    }

    public AbstractSerializable writeByte(Byte value){
        writeBuffer.writeByte(value);
        return this;
    }

    public AbstractSerializable writeInt(int value){
        writeBuffer.writeInt(value);
        return this;
    }

    public AbstractSerializable writeShort(short value){
        writeBuffer.writeShort(value);
        return this;
    }

    public AbstractSerializable writeLong(long value){
        writeBuffer.writeLong(value);
        return this;
    }

    public AbstractSerializable writeFloat(float value){
        writeBuffer.writeFloat(value);
        return this;
    }

    public AbstractSerializable writeDouble(double value){
        writeBuffer.writeDouble(value);
        return this;
    }

    public AbstractSerializable writeString(String value){
        if(null == value || value.isEmpty()){
            writeShort((short)0);
            return this;
        }
        byte[] bytes = value.getBytes(CHARSET);
        short size = (short) bytes.length;
        writeBuffer.writeShort(size);
        writeBuffer.writeBytes(bytes);
        return this;
    }

    public AbstractSerializable writeObject(Object object){
        if(null == object){
            writeByte((byte)0);
        }else{
            if(object instanceof Integer){
                writeInt((int)object);
            }else if(object instanceof Short){
                writeShort((short)object);
            }else if(object instanceof Byte){
                writeByte((byte)object);
            }else if(object instanceof Long){
                writeLong((long)object);
            }else if(object instanceof Float){
                writeFloat((float)object);
            }else if(object instanceof Double){
                writeDouble((double)object);
            }else if(object instanceof String){
                writeString((String) object);
            }else if(object instanceof AbstractSerializable){
                writeByte((byte)1);
                AbstractSerializable serializable = (AbstractSerializable) object;
                serializable.writeToTargetBuffer(writeBuffer);
            }else{
                throw new RuntimeException("不可序列化类型:[%s]"+object.getClass());
            }
        }
        return this;
    }

    public <T> AbstractSerializable writeList(List<T> list){
        if(isEmpty(list)){
            writeBuffer.writeShort((short)0);
            return this;
        }
        writeBuffer.writeShort((short)list.size());
        for(T t:list){
            writeObject(t);
        }
        return this;
    }

    public <K,V> AbstractSerializable writeMap(Map<K,V> map){
        if(isEmpty(map)){
            writeBuffer.writeShort((short)0);
            return this;
        }
        writeBuffer.writeShort((short)map.size());
        for (Map.Entry<K,V> entry:map.entrySet()) {
            writeObject(entry.getKey());
            writeObject(entry.getValue());
        }
        return this;
    }


    /**
     * 返回byte数组
     * @return
     */
    public byte[] getBytes(){
        writeToLocalBuffer();
        byte[] bytes = null;
        if(writeBuffer.writerIndex() == 0){
            bytes = new byte[0];
        }else{
            bytes = new byte[writeBuffer.writerIndex()];
            writeBuffer.readBytes(bytes);
        }
        writeBuffer.clear();
        return bytes;
    }

    public byte readByte(){
        return readBuffer.readByte();
    }

    public short readShort(){
        return readBuffer.readShort();
    }

    public int readInt(){
        return readBuffer.readInt();
    }

    public long readLong(){
        return readBuffer.readLong();
    }

    public float readFloat(){
        return readBuffer.readFloat();
    }

    public double readDouble(){
        return readBuffer.readDouble();
    }

    public String readString(){
        short size = readBuffer.readShort();
        if(size <= 0){
            return "";
        }
        byte[] bytes = new byte[size];
        readBuffer.readBytes(bytes);
        return new String(bytes,CHARSET);
    }

    public <K> K readObject(Class<K> clz){
        Object k = null;
        if(clz == int.class || clz == Integer.class){
            k = readInt();
        }else if(clz == byte.class || clz == Byte.class){
            k = readByte();
        }else if(clz == short.class || clz == Short.class){
            k = readShort();
        }else if(clz == long.class || clz == Long.class){
            k = readLong();
        }else if(clz == float.class || clz == Float.class){
            k = readFloat();
        }else if(clz == double.class || clz == Double.class){
            k = readDouble();
        }else if(clz == String.class){
            k = readString();
        }else if(AbstractSerializable.class.isAssignableFrom(clz)){
            try {
                byte hasObject = readBuffer.readByte();
                if(hasObject == 1){
                    AbstractSerializable temp = (AbstractSerializable) clz.newInstance();
                    temp.readFromBuffer(readBuffer);
                    k = temp;
                }else{
                    k = null;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }else{
            throw new RuntimeException(String.format("不支持类型:[%s]",clz));
        }
        return (K)k;
    }

    public <T> List<T> readList(Class<T> clz){
        ArrayList<T> list = new ArrayList<>();
        short size = readBuffer.readShort();
        for(int i=0;i<size;i++){
            list.add(readObject(clz));
        }
        return list;
    }

    public <K,V> Map<K,V> readMap(Class<K> keyClz,Class<V> valueClz){
        HashMap<K, V> map = new HashMap<>();
        short size = readBuffer.readShort();
        for (int i =0;i<size;i++){
            K key = readObject(keyClz);
            V value = readObject(valueClz);
            map.put(key,value);
        }
        return map;
    }

    private <T> boolean isEmpty(Collection<T> c) {
        return c == null || c.isEmpty();
    }
    public <K,V> boolean isEmpty(Map<K,V> c) {
        return c == null || c.isEmpty();
    }
}

Resource.java
package com.customSerializable.entity;

import com.customSerializable.core.AbstractSerializable;

/**
 * @author hzk
 * @date 2018/9/27
 */
public class Resource extends AbstractSerializable {

    /**
     * 体力
     */
    private Integer energy;
    /**
     * 金币
     */
    private Double gold;

    public Integer getEnergy() {
        return energy;
    }

    public void setEnergy(Integer energy) {
        this.energy = energy;
    }

    public Double getGold() {
        return gold;
    }

    public void setGold(Double gold) {
        this.gold = gold;
    }

    @Override
    protected void read() {
        energy = readInt();
        gold = readDouble();
    }

    @Override
    protected void write() {
        writeInt(energy);
        writeDouble(gold);
    }

    @Override
    public String toString() {
        return "Resource{" +
                "energy=" + energy +
                ", gold=" + gold +
                '}';
    }
}

Player.java
package com.customSerializable.entity;

import com.customSerializable.core.AbstractSerializable;

import java.util.ArrayList;
import java.util.List;

/**
 * @author hzk
 * @date 2018/9/27
 */
public class Player extends AbstractSerializable {

    private Long playerId;
    private Integer age;
    private List<Integer> skills = new ArrayList<>();
    private Resource resource = new Resource();

    public Long getPlayerId() {
        return playerId;
    }

    public void setPlayerId(Long playerId) {
        this.playerId = playerId;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public List<Integer> getSkills() {
        return skills;
    }

    public void setSkills(List<Integer> skills) {
        this.skills = skills;
    }

    public Resource getResource() {
        return resource;
    }

    public void setResource(Resource resource) {
        this.resource = resource;
    }

    @Override
    protected void read() {
        playerId = readLong();
        age = readInt();
        skills = readList(Integer.class);
        resource = readObject(Resource.class);
    }

    @Override
    protected void write() {
        writeLong(playerId);
        writeInt(age);
        writeList(skills);
        writeObject(resource);
    }

    @Override
    public String toString() {
        return "Player{" +
                "playerId=" + playerId +
                ", age=" + age +
                ", skills=" + skills +
                ", resource=" + resource +
                '}';
    }
}

Test4.java
package com.customSerializable.test;

import com.customSerializable.entity.Player;

import java.util.Arrays;

/**
 * 自定义序列化
 * @author hzk
 * @date 2018/9/27
 */
public class Test4 {
    
    public static void main(String[] args){
        Player player = new Player();
        player.setPlayerId(1111L);
        player.setAge(23);
        player.getSkills().add(77);
        player.getSkills().add(88);
        player.getResource().setEnergy(100);
        player.getResource().setGold(15000.00D);

        byte[] bytes = player.getBytes();
        System.out.println(Arrays.toString(bytes));

        Player player2 = new Player();
        player2.readFromBytes(bytes);
        System.out.println(player2.toString());

    }

}

运行结果:
[0, 0, 0, 0, 0, 0, 4, 87, 0, 0, 0, 23, 0, 2, 0, 0, 0, 77, 0, 0, 0, 88, 1, 0, 0, 0, 100, 64, -51, 76, 0, 0, 0, 0, 0]
Player{playerId=1111, age=23, skills=[77, 88], resource=Resource{energy=100, gold=15000.0}}

 最后这种方式是基于前面几种方式技术和思维的一些结合,不管怎样去实现序列化都有我们需要注意的一件事就是写入和读取的顺序需要一致,否则数据无法匹配正确,关于序列化还有很多更深层次的知识,如果大家有需要可以自己去深入了解。

你可能感兴趣的:(Java,ProtoBuf,序列化,Netty)