JAVA对象转字节数组

  • Post author:
  • Post category:java

日常使用中, 存在一些场景需要把java对象转为字节数组。 或者字节数组转java对象。 一般来说有以下几种场景。

我们来分别讨论。

1. JAVA之间相互通讯场景

这种场景常见于java应用之间的通讯, 比如A应用向B应用获取数据。 或者读取B应用预先存的数据。

此时一般来说实体类实现java自带的序列化接口, 然后使用以下方式即可完成序列化。

    private Object byteToObject( byte[] bytes) {
        java.lang.Object obj;
        try {
        //bytearray to object
        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
        ObjectInputStream oi = new ObjectInputStream(bi);

        obj = oi.readObject();

        bi.close();
        oi.close();
        }
        catch(Exception ae) {
         throw ae;
        }
        return obj;
    }
 

     public byte[] objectToByte(Object obj)
     {
        byte[] bytes;
        try {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(obj);

            bytes = bo.toByteArray();

            bo.close();
            oo.close();    
        }
        catch(Exception ae) {
           throw ae;
        }
        return(bytes);
    }

此种方式适合java语言之间的通讯或者数据交互。不适合跨语言。而且也不适合于IM场景。

2. 仅仅做数据储存方案

储存方案一般来说要区分是否跨语言, 数据储存体积, 读取效率等问题。 这种建议优先采用文本化协议比较好(比如json, xml等), 方便开发者校验和开发。 如果使用二进制储存, 虽然体积小了。但是不方便开发和核对。出了bug不方便排查。

3. 跨语言通讯场景

跨语言通讯场景一般是最多的, 比如物联网。 IM通讯, 视频等等。由于业务的特殊性,所以一般会采用二进制协议。而且是私有协议居多。在进行序列化时候。java自带的序列化机制并不能很好的满足我们的需求。这个时候就需要用户自己手动进行序列化过程。

主要使用到 java的 ByteBuffer.

    public class Student {
		byte height;
		int age;
		long phone;
	}


	public static void main(String[] args) {
		Student student = new Student();
		student.height = (byte) 185;
		student.age = 23;
		student.phone = 18380330237L;

		// 对象序列化到字节数组
		ByteBuffer byteBuffer = ByteBuffer.allocate(100).order(ByteOrder.BIG_ENDIAN);
		byteBuffer.put(student.height);
		byteBuffer.putInt(student.age);
		byteBuffer.putLong(student.phone);
		byte[] array = byteBuffer.array();


		// 字节数组序列化到对象
		byteBuffer = ByteBuffer.allocate(100).order(ByteOrder.BIG_ENDIAN);
		byteBuffer.put(array);
		byteBuffer.position(0);
		Student student2 = new Student();
		student2.height = byteBuffer.get();
		student2.age = byteBuffer.getInt();
		student2.phone = byteBuffer.getLong();
		
	}

上面的代码中, 列举出了我们平时序列化的方式。虽然使用字节数组可以高效率的完成对象的封装和转换。 但是需要注意以下问题:

  • 字节数组大小端双方一定要一致
  • 如果存在list, array 等属性, 要注意OOM
  • buffer使用时如果时directbuffer, 需要注意OOM
  • 注意序列化时空指针和默认值的处理
  • 序列化数组不全时,注意数据对齐进行填补
  • 处理 字符串时,注意字符编码集

其实跨语言通讯和私有协议这种场景,大都是大厂最先遇到并提出解决方案的。比如google 的 javastruct(2004年就出来了); 或者 magic-byte;

如果使用框架,以上的代码可以如下写了(注意类的定义和上面有所不同):


    @MagicClass(byteOrder = ByteOrder.BIG_ENDIAN)
    public class Student {
        @MagicField(order = 1)
        byte height;
        @MagicField(order = 2)
        int age;
        @MagicField(order = 3)
        long phone;

        // gettter setter
    }


    public static void main(String[] args) {
        Student student = new Student();
        student.height = (byte) 185;
        student.age = 23;
        student.phone = 18380330237L;

        // 对象序列化到字节数组
       byte[] array = MagicByte.unpackToByte(student);

        // 字节数组序列化到对象
        Student student2 = MagicByte.pack(array, Student.class);

    }

在定义类时, 即把序列化方式定义完成。然后只需要调用函数即可进行序列化和反序列化了。

怎么样, 这样是不是简单多了。 

附上开源项目的地址:

GitHub – MisterChangRay/magic-byte: faster convert byte to java object toolhttps://github.com/MisterChangRay/magic-byte

以上就是总结的一些java 对象转字节的方式。 大家按需索取吧。


版权声明:本文为jioulongzi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。