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); }}
阅读全文
0 0
- Java 对象与字节数组相互转换
- java中对象与字节数组相互转换
- java中对象与字节数组相互转换
- Java中对象与字节数组相互转换
- java中对象与字节数组相互转换
- java中对象与字节数组相互转换
- java中对象与字节数组相互转换
- java中对象与字节数组相互转换
- Android学习之Bitmap对象与字节数组相互转换
- Android学习之Bitmap对象与字节数组相互转换
- Java 字节数组与String的相互转换错误
- 字符串与字节数组相互转换
- C# 字符串与字节数组相互转换
- jedis实现redis的消息队列、发布对象消息、字节数组与字符串相互转换
- jedis实现redis的消息队列、发布对象消息、字节数组与字符串相互转换
- jedis实现redis的消息队列、发布对象消息、字节数组与字符串相互转换
- Bitmap对象和字节数组的相互转换
- JAVA IO流实现字节数组与任何基本类型和引用类型的相互转换
- 单播,多播,广播
- 左旋字符串的三种算法
- 将整数字符串转成整数值 Python版
- 计算边长为abc三角形的面积
- C#中 委托与事件
- Java 对象与字节数组相互转换
- 虚拟机 第六章 类文件结构
- codeforces 887B
- 【PAT天梯赛】长城
- java基础: protected default 修饰符 补充
- B
- 第十八天总结
- JMeter插件管理器JMeter Plugins Manager
- linux系统的任务计划crontab