Java 对象与字节数组相互转换

来源:互联网 发布:耐驰热分析软件下载 编辑:程序博客网 时间:2024/04/30 16:55

开发过程中,遇到需要通过Socket与C++通讯的情况,Java客户端需要向C++服务端发送请求包,同时接收返回的响应数据包。
Java利用反射实现的通用对象与字节数组相互转换方法,在此记录与分享一下。

这里写图片描述

原理

1、对象转字节数组: 通过反射,获取对象的所有字段以及字段的注解和字段的值,根据字段注解对应的字段类型,依次通过类型对应的处理方法,将字段的值,转化为字节或字节数组。
2、字节数组转对象: 通过反射,获取对象的所有字段以及字段的注解,根据字段注解对应的字段类型,依次从字节数组中获取指定长度的字节数组,并将其转换为对应的值,赋值给此字段。

测试结果

1、请求对象

这里写图片描述

2、转换的字节数组解析

100 0               #包类型号,1000 0 0 0 0 0 0 0     #seq,012 0                #userName长度122 104 97 110 103 115 97 110 102 101 110 103   #userName,zhangsanfeng6 0                 #pwd长度49 50 51 52 53 54   #pwd,1234569 0                 #trueName长度-27 -68 -96 -28 -72 -119 -28 -72 -80            #trueName,张三丰-57 97 109 67 4 0 0 0                           #telNo,183111111111                   #gender,124 0                #age,24-80 0               #height,1760 0 -125 66         #weight,65.53 0                 #list长度101 0               #二级包类型号,101     -50 7               #startYear,1998-45 7               #endYear,200312 0                #school长度-27 -82 -98 -23 -86 -116 -27 -80 -113 -27 -83 -90   #school,实验小学101 0               #二级包类型号,101-45 7               #startYear,2003-39 7               #endYear,200912 0                #school长度-27 -123 -85 -28 -72 -128 -28 -72 -83 -27 -83 -90   #school,八一中学101 0               #二级包类型号,101-39 7               #startYear,2009-35 7               #endYear,201312 0                #school长度-26 -72 -123 -27 -115 -114 -27 -92 -89 -27 -83 -90  #school,清华大学

3、通过数组转换获得的对象

这里写图片描述

源码

1、请求包类:

package com.daidai.pack.communication;import java.util.List;import com.daidai.pack.ColumnProperty;import com.daidai.pack.ColumnType;import com.daidai.pack.Pack;import com.daidai.pack.PackType;/** * 测试请求包 *  * @author daidai */@PackType(typeNo = 100)public class TestRequest extends Pack {    /**     * 在进行tcp通讯时,根据此序列来对应请求包和返回包     */    @ColumnProperty(type = ColumnType.LONG)    public long seq;    /**     * 用户名     */    @ColumnProperty(type = ColumnType.STRING)    public String userName;    /**     * 密码     */    @ColumnProperty(type = ColumnType.STRING)    public String pwd;    /**     * 真实姓名     */    @ColumnProperty(type = ColumnType.STRING)    public String trueName;    /**     * 手机号     */    @ColumnProperty(type = ColumnType.LONG)    public long telNo;    /**     * 性别,0为女性、1为男性     */    @ColumnProperty(type = ColumnType.BYTE)    public byte gender;    /**     * 年龄     */    @ColumnProperty(type = ColumnType.SHORT)    public short age;    /**     * 身高     */    @ColumnProperty(type = ColumnType.SHORT)    public short height;    /**     * 体重     */    @ColumnProperty(type = ColumnType.FLOAT)    public float weight;    /**     * 教育背景     */    @ColumnProperty(type = ColumnType.LIST_OBJECT)    public List<EducationalBackground> eduBackground;    @Override    public String toString() {        return "TestRequest [seq=" + seq + ", userName=" + userName + ", pwd="                + pwd + ", trueName=" + trueName + ", telNo=" + telNo                + ", gender=" + gender + ", age=" + age + ", height=" + height                + ", weight=" + weight + ", eduBackground=" + eduBackground                + "]";    }}

2、二级请求包类:

package com.daidai.pack.communication;import com.daidai.pack.ColumnProperty;import com.daidai.pack.ColumnType;import com.daidai.pack.Pack;import com.daidai.pack.PackType;/** * 测试二级请求包 * @author daidai */@PackType(typeNo = 101)public class EducationalBackground extends Pack{    /**     * 开始年份     */    @ColumnProperty(type=ColumnType.SHORT)    public short startYear;    /**     * 结束年份     */    @ColumnProperty(type=ColumnType.SHORT)    public short endYear;    /**     * 学校名称     */    @ColumnProperty(type=ColumnType.STRING)    public String school;    @Override    public String toString() {        return "EducationalBackground [startYear=" + startYear + ", endYear="                + endYear + ", school=" + school + "]";    }}

3、包类型注解类,一个类型号唯一对应一个请求包

package com.daidai.pack;import java.lang.annotation.Retention;import java.lang.annotation.Target;/** * 包类型 * @author daidai * */@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)@Target(value={java.lang.annotation.ElementType.TYPE})public @interface PackType {    /**     * 包类型号     * @return     */    public short typeNo();}

4、字段类型注解类,用于注明字段类型

package com.daidai.pack;import java.lang.annotation.Retention;import java.lang.annotation.Target;/** * 字段属性注解 * @author daidai */@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)@Target(value={java.lang.annotation.ElementType.FIELD})public @interface ColumnProperty {    // 字段类型    public ColumnType type();    // 字段长度限制,0为不限制//  public int maxLen() default 0;}

5、字段类型枚举,用于处理字段和字节数组的转换

package com.daidai.pack;import java.nio.ByteBuffer;import java.util.ArrayList;import java.util.List;/** * 字段类型 * @author daidai */public enum ColumnType {    BYTE{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            Number n = (Number)obj;            bf.put(n.byteValue());        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return bf.get();        }    },    SHORT{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            Number n = (Number)obj;            bf.putShort(n.shortValue());        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return bf.getShort();        }    },    INT{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            Number n = (Number)obj;            bf.putInt(n.intValue());        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return bf.getInt();        }    },    LONG{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            Number n = (Number)obj;            bf.putLong(n.longValue());        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return bf.getLong();        }    },    FLOAT{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            Number n = (Number)obj;            bf.putFloat(n.floatValue());        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return bf.getFloat();        }    },    DOUBLE{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            Number n = (Number)obj;            bf.putDouble(n.doubleValue());        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return bf.getDouble();        }    },    STRING{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            byte[] bs = obj.toString().getBytes("UTF-8");            bf.putShort((short)bs.length);            bf.put(bs);        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            short len = bf.getShort();            if(len > 0){                byte[] bs = new byte[len];                bf.get(bs);                return new String(bs,"UTF-8");            }            return "";        }    },    OBJECT{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            byte[] bs =new byte[]{};            if (obj instanceof Pack) {                Pack newPack = (Pack) obj;                bs = newPack.serialize();            }            bf.put(bs);        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            return Pack.deserialize(bf);        }    },    BYTE_ARRAYS{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            byte[] bs = (byte[]) obj;            bf.put(bs);        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            short len = bf.getShort();            byte[] bs = new byte[len];            bf.get(bs);            return bs;        }    },    LIST_INT{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            @SuppressWarnings("unchecked")            List<Object> list = (List<Object>)obj;            bf.putShort((short) list.size());            for (Object item : list) {                INT.serialize(bf, item);            }        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            short len = bf.getShort();            List<Object> list = new ArrayList<Object>(len);            if(len > 0){                for(int i=0; i<len; i++){                    list.add(INT.deserialize(bf));                }            }            return list;        }    },    LIST_STRING{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            @SuppressWarnings("unchecked")            List<Object> list = (List<Object>)obj;            bf.putShort((short) list.size());            for (Object item : list) {                STRING.serialize(bf, item);            }        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            short len = bf.getShort();            List<Object> list = new ArrayList<Object>(len);            if(len > 0){                for(int i=0; i<len; i++){                    list.add(STRING.deserialize(bf));                }            }            return list;        }    },    LIST_OBJECT{        @Override        public void serialize(ByteBuffer bf, Object obj) throws Exception {            @SuppressWarnings("unchecked")            List<Object> list = (List<Object>)obj;            bf.putShort((short) list.size());            for (Object item : list) {                OBJECT.serialize(bf, item);            }        }        @Override        public Object deserialize(ByteBuffer bf) throws Exception {            short len = bf.getShort();            List<Object> list = new ArrayList<Object>(len);            if(len > 0){                for(int i=0; i<len; i++){                    list.add(OBJECT.deserialize(bf));                }            }            return list;        }    };    /**     * 序列化     * @param bf     * @param obj     * @throws Exception     */    public abstract void serialize(ByteBuffer bf, Object obj) throws Exception;    /**     * 返序列化     * @param bf     * @return     * @throws Exception     */    public abstract Object deserialize(ByteBuffer bf) throws Exception;}

6、包授权管理类,用于初始化类型号和包类的对应关系,方便之后通过类型号找到对应的类

package com.daidai.pack;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;/** * 包授权管理 * @author daidai */public class PackAuthorize {    /**     * 存放通讯包类型号和通讯包对应关系     */    private static final ConcurrentMap<Short, Class<?>> typeNoAndPackMap ;    static{        typeNoAndPackMap = new ConcurrentHashMap<Short, Class<?>>();        registerPack(                com.daidai.pack.communication.EducationalBackground.class,                com.daidai.pack.communication.TestRequest.class);    }    private PackAuthorize(){};    private static PackAuthorize instance;    public static PackAuthorize getInstance(){        if(instance == null){            synchronized(PackAuthorize.class){                if(instance == null){                    instance = new PackAuthorize();                }            }        }        return instance;    }    /**     * 注册通讯包和包类型号的对应关系管理     * @param pack     */    public static void registerPack(Class<?>... classes){        for(Class<?> clazz : classes){            PackType packType = (PackType) clazz.getAnnotation(PackType.class);            typeNoAndPackMap.put(packType.typeNo(), clazz);        }    }    public ConcurrentMap<Short, Class<?>> getTypenoandpackmap() {        return typeNoAndPackMap;    }}

7、通用包父类,用于定义通用处理方法

package com.daidai.pack;import java.lang.reflect.Field;import java.nio.ByteBuffer;import java.nio.ByteOrder;/** * 通用包父类 * @author daidai */public abstract class Pack {    // 字节序    final ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN;    // 当前包的允许最大长度    final int packMaxSize = 1500;    /**     * 获取包类型号typeNo,包类型号不允许重复,一个包类型号对应一个包     * @return     */    public final short getTypeNo(){        @SuppressWarnings("rawtypes")        Class clazz = this.getClass();        @SuppressWarnings("unchecked")        PackType packType = (PackType) clazz.getAnnotation(PackType.class);        return packType.typeNo();    }    /**     * 序列化,将包对象转化为字节数组     * @return     * @throws Exception     */    public byte[] serialize() throws Exception{        ByteBuffer bf = ByteBuffer.allocate(packMaxSize);        bf.order(byteOrder);        bf.putShort(this.getTypeNo());        Field[] fields = this.getClass().getDeclaredFields();        for(Field field : fields){            ColumnProperty cp = field.getAnnotation(ColumnProperty.class);            Object obj = field.get(this);            try {                final ColumnType type = cp.type();                type.serialize(bf, obj);            } catch (Exception e) {                throw new Exception("#socket_serialize_error " + field + " " + obj + " " + cp + " " + e, e);            }        }        byte[] result = new byte[bf.position()];        System.arraycopy(bf.array(), 0, result, 0, result.length);        return result;    }    /**     * 返序列化,将字节数组转换为包对象     * @param datas     * @return     * @throws Exception     */    public static final Pack deserialize(byte[] datas) throws Exception{        ByteBuffer byteBuffer = ByteBuffer.wrap(datas);        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);        return deserialize(byteBuffer);    }    /**     * 返序列化,将字节缓冲区转换为包对象     * @param byteBuffer     * @return     * @throws Exception     */    public static final Pack deserialize(ByteBuffer byteBuffer) throws Exception{        short typeNo = byteBuffer.getShort();        Class<?> clazz = PackAuthorize.getInstance().getTypenoandpackmap().get(typeNo);        Pack pack = getPack(byteBuffer,clazz);        return pack;    }    private static final Pack getPack(ByteBuffer byteBuffer, Class<?> clazz) throws Exception{        Pack pack = (Pack) clazz.newInstance();        Field[] fields = clazz.getDeclaredFields();        for(Field field : fields){            ColumnProperty cp = field.getAnnotation(ColumnProperty.class);            field.setAccessible(true);            try {                final ColumnType type = cp.type();                field.set(pack, type.deserialize(byteBuffer));            } catch (Exception e) {                throw new Exception("#socket_serialize_error " + field + " " + cp + " " + e, e);            }        }        return pack;    }}

8、唯一序列生成类,序列用于将请求包和响应包进行匹配对应,防止“答非所问”

package com.daidai.pack.util;/** * 获取唯一序列 * @author daidai */public class SequenceIdUtil {    private final int beginValue = 0;    private final int maxValue = Integer.MAX_VALUE;    private static final int STEP = 1;    private int value;    private SequenceIdUtil(){}    private static SequenceIdUtil instance;    public static SequenceIdUtil getInstance() {        if (instance == null)            synchronized (SequenceIdUtil.class) {                if (instance == null)                    instance = new SequenceIdUtil();            }        return instance;    }    /**     * 获取一个序列值     * @return     */    public synchronized int nextVal() {        if (value < beginValue) {            value = beginValue;        }        if(value >= maxValue){            value = beginValue;        }        value += STEP;        return value;    }}

9、测试类

package com.daidai.test;import java.util.ArrayList;import java.util.List;import com.daidai.pack.Pack;import com.daidai.pack.communication.EducationalBackground;import com.daidai.pack.communication.TestRequest;public class Test {    public static void main(String[] args) throws Exception {        // 创建一个请求包对象,并给对象赋值         TestRequest request = new TestRequest();        request.seq = 0;        request.userName = "zhangsanfeng";        request.pwd = "123456";        request.trueName = "张三丰";        request.telNo = 18311111111L;        request.gender = 1;        request.age = 24;        request.height = 176;        request.weight = 65.5F;        EducationalBackground primarySchool = new EducationalBackground();        primarySchool.startYear = 1998;        primarySchool.endYear = 2003;        primarySchool.school = "实验小学";        EducationalBackground middleSchool = new EducationalBackground();        middleSchool.startYear = 2003;        middleSchool.endYear = 2009;        middleSchool.school = "八一中学";        EducationalBackground university = new EducationalBackground();        university.startYear = 2009;        university.endYear = 2013;        university.school = "清华大学";        List<EducationalBackground> list = new ArrayList<EducationalBackground>();        list.add(primarySchool);        list.add(middleSchool);        list.add(university);        request.eduBackground = list;        // 打印创建完成的请求对象        System.out.println(request.toString());        // 调用序列化方法将对象转换成字节数组        byte[] bs = request.serialize();        // 打印生成的字节数组        System.out.println(bs.toString());        for(byte b : bs){            System.out.print(b+" ");        }        // 调用返序列化方法,将字节数组转换为对象        TestRequest request2 = (TestRequest) Pack.deserialize(bs);        // 打印通过字节数组转换的对象        System.out.println(request2);    }}
原创粉丝点击