Java:对一个对象序列化和反序列化的简单实现
2022-01-05 10:29
405 查看
名词解释
序列化:将Java对象转化成字节的过程
反序列化:将字节转化成Java对象的过程
字节:1字节(byte)= 8bit,bit就是计算机认识的二进制
序列化的作用
Java对象是在Java虚拟机中使用的,一旦Java进程结束,对象就会消失,要将只有虚拟机才认识的对象,保存在磁盘中,必须将对象转化成字节。
- 在RPC中的用处:序列化将对象转换为字节流,然后通过网络传输进行发送
- 保存对象的状态:当Java进程需要重启时,可以将对象序列化后保存在文件中,对象的状态不会因为进程的关闭而丢失
如何进行序列化
基本数据类型转为字节的思路
对于有多个字节的数据,用移位运算符,将每8位进行移位,用一个字节保存
- Int类型:一个int有4个字节,可以创建一个长度为4的字节数组进行保存(short,long类似)
- char类型:一个char有2个字节,用相应长度的字节数组保存后,反序列化时再强制转化为char
- String类型:String的值主要是一个char数组,创建一个大小为char数组两倍的字节数组进行保存,反序列化时再转化为String
- Double和Float类型:过程比较复杂(没学会),建议直接调用工具类
一个字节和其他类型的转换工具类
import java.nio.ByteBuffer; public class ByteUtils { public static byte[] short2bytes(short v) { byte[] b = new byte[4]; b[1] = (byte) v; b[0] = (byte) (v >>> 8); return b; } public static byte[] String2bytes(String str){ char[] chars = str.toCharArray(); byte[] charByte = new byte[chars.length*2]; for (int i = 0; i < chars.length; i++) { charByte[i*2] = (byte) (chars[i] >>> 8); charByte[i*2+1] = (byte) (chars[i]); } return charByte; } public static byte[] int2bytes(int v) { byte[] b = new byte[4]; b[3] = (byte) v; b[2] = (byte) (v >>> 8); b[1] = (byte) (v >>> 16); b[0] = (byte) (v >>> 24); return b; } public static byte[] long2bytes(long v) { byte[] b = new byte[8]; b[7] = (byte) v; b[6] = (byte) (v >>> 8); b[5] = (byte) (v >>> 16); b[4] = (byte) (v >>> 24); b[3] = (byte) (v >>> 32); b[2] = (byte) (v >>> 40); b[1] = (byte) (v >>> 48); b[0] = (byte) (v >>> 56); return b; } public static byte[] double2bytes(double d){ long lValue = Double.doubleToLongBits(d); byte[] bytes = long2bytes(lValue); return bytes; } public static int bytes2Int_BE(byte[] bytes) { if(bytes.length < 4){ return -1; } int iRst = (bytes[0] << 24) & 0xFF; iRst |= (bytes[1] << 16) & 0xFF; iRst |= (bytes[2] << 8) & 0xFF; iRst |= bytes[3] & 0xFF; return iRst; } public static long bytes2long(byte[] b) { ByteBuffer buffer = ByteBuffer.allocate(8); buffer.put(b, 0, b.length); buffer.flip();// need flip return buffer.getLong(); } public static String bytes2String(byte[] bytes){ char[] chars = new char[bytes.length/2]; for (int i = 0; i < chars.length; i++) { chars[i] = (char) ((bytes[i*2] << 8) | bytes[i*2+1]); } return new String(chars); } public static float byte2Float(byte[] bytes){ Integer iValue = bytes2Int_BE(bytes); float dValue = Float.intBitsToFloat(iValue); return dValue; } public static double byte2Double(byte[] bytes){ Long lValue = bytes2long(bytes); double dValue = Double.longBitsToDouble(lValue); return dValue; } }View Code
如何序列化对象
其实序列化就是为了反序列化,保存对象之后必然要读取对象,所以站在反序列化的角度去研究序列化
得到一串字节流之后,要如何转换成对象
- 要通过反射创建对象,首先要知道对象的类名称,然后调用类的默认构造函数 要识别类名称,得知道类名称是由哪些字节转换的
- 要有一个值存放类名称的字节长度,长度前最好放一个标记
-
需要数据类型的字节长度,根据长度去识别字节数组中的一部分
序列化的代码实现如下
- 这里没有将对象的类型写入字节数组,因为反序列化的方法会传入一个class参数,可以直接构造对象
public static byte[] serialize(Object object) throws IllegalAccessException, ClassNotFoundException { // 对象序列化后的字节数组 byte[] objectByte = new byte[1024]; // 通过移动的下标变量不断往数组添加数据 int index = 0; // 标记后面的字节可以转化成对象 objectByte[index] = SerializationNum.SERIALIZATION_OBJECT_NUM; Class clazz = object.getClass(); // 遍历所有字段,将字段的值转化为字节 Field[] fields = clazz.getDeclaredFields(); // 一开始的标记占了一个位置 int len = 1; for (Field field : fields) { // 将private属性设置为可访问 field.setAccessible(true); // 每次移动下标,给后面的数据腾地方 index += len; // 不同的类型,不同的转化方式 Class fieldClass = field.getType(); // 每种类型对应一个标记 byte magicNum = SerializationNum.getNum(fieldClass); byte[] valueByte = new byte[0]; switch (magicNum){ case SerializationNum.INTEGER_NUM: // 反射获取值 Integer iValue = (Integer) field.get(object); // int类型是4个字节 len = 4; // 转化成一个长度为4的字节数组 valueByte = ByteUtils.int2bytes(iValue); break; case SerializationNum.LONG_NUM: long longValue = field.getLong(object); len = 8; valueByte = ByteUtils.long2bytes(longValue); break; case SerializationNum.STRING_NUM: String sValue = (String) field.get(object); valueByte = ByteUtils.String2bytes(sValue); len = valueByte.length; break; default: break; } // 将类型和长度都添加字节数组中 objectByte[index++] = magicNum; objectByte[index++] = (byte) len; // 转化后的字节复制到对象字节数组 System.arraycopy(valueByte,0,objectByte,index,len); } index += len; // 对象已经整个被转化完毕的标记 objectByte[index] = SerializationNum.FINISH_NUM; return objectByte; }
反序列化过程实现
public static Object deserialize(byte[] bytes,Class clazz) throws InstantiationException, IllegalAccessException { int index = 0; // 识别首部,确保是能够反序列化成对象的字节 if (bytes[index] != SerializationNum.SERIALIZATION_OBJECT_NUM) { return null; } // 使用默认构造方法 Object obj = clazz.newInstance(); // 跳过最开始的魔数 byte len = 1; // 一个个给对象的字段赋值 for (Field declaredField : clazz.getDeclaredFields()) { declaredField.setAccessible(true); // 每次移动下标 index += len; // 不同的类型,不同的转化方式 Class fieldClass = declaredField.getType(); byte magicNum = SerializationNum.getNum(fieldClass); // 防止数组越界 if (index >= bytes.length || bytes[index] != magicNum){ continue; } Object value = null; // 先获取需要的字节长度 len = bytes[++index]; // 创建一个新数组去作为方法参数,如果不采用这种传参方式,也许能节约这个空间 byte[] srcBytes = new byte[len]; System.arraycopy(bytes,++index,srcBytes,0,len); switch (magicNum){ case SerializationNum.INTEGER_NUM: value = ByteUtils.bytes2Int_BE(srcBytes); break; case SerializationNum.LONG_NUM: value = ByteUtils.bytes2long(srcBytes); break; case SerializationNum.STRING_NUM: value = ByteUtils.bytes2String(srcBytes); break; case SerializationNum.DOUBLE_NUM: value = ByteUtils.byte2Double(srcBytes); break; default: break; } // 将值赋给对象 declaredField.set(obj,value); } return obj; }
对各种基本数据类型进行特殊标记的工具类
public class SerializationNum { public static final byte BOOLEAN_NUM = 0x01; public static final byte SHORT_NUM = 0x02; public static final byte INTEGER_NUM = 0x03; public static final byte LONG_NUM = 0x04; public static final byte CHAR_NUM = 0x05; public static final byte FLOAT_NUM = 0x06; public static final byte DOUBLE_NUM = 0x07; public static final byte STRING_NUM = 0x08; public static final byte OBJECT_NUM = 0x09; public static final byte SERIALIZATION_OBJECT_NUM = 0x10; public static final byte FINISH_NUM = 0x30; public static byte getNum(Class clazz){ if (clazz == String.class) { return STRING_NUM; } if (clazz == Integer.class) { return INTEGER_NUM; } if (clazz == Double.class) { return DOUBLE_NUM; } if (clazz == Boolean.class) { return BOOLEAN_NUM; } if (clazz == Float.class) { return FINISH_NUM; } if (clazz == Long.class) { return LONG_NUM; } return 0x77; } }View Code
测试代码
public static void main(String[] args) throws IOException { Entity entity = new Entity(); entity.setName("name"); entity.setNum(5); entity.setId(5L); entity.setMoney(100.55); try { System.out.println("可以将对象序列号后的字节重新反序列化成对象"); byte[] objByte = serialize(entity); Entity entity1 = (Entity) deserialize(objByte,Entity.class); System.out.println(entity1); System.out.println("保存在文件的字节,取出来后也可以反序列成对象"); FileOutputStream outputStream = new FileOutputStream("text.out"); FileInputStream inputStream = new FileInputStream("text.out"); byte[] fileBytes = new byte[1024]; outputStream.write(objByte); inputStream.read(fileBytes); Entity entity2 = (Entity) deserialize(fileBytes,Entity.class); System.out.println(entity2); } catch (IllegalAccessException | ClassNotFoundException | InstantiationException e) { System.out.println("类不能被构造"); e.printStackTrace(); } }
测试结果:
相关文章推荐
- java 序列化 反序列化 简单实现
- jackson实现简单对象的序列化,反序列化
- 今天无意中发现JavaBean类基本都要求实现了Serializable接口,以前只是知道序列化以后,可以通过io流的方式将对象序列化和反序列化,进行存取,但不知道为什么需要序列化,今天总结一下
- 一个简单的把对象序列化/反序列化为json格式的程序
- 一个Java对象实例序列化和反序列化的例子
- (初级小项目):用Java实现一个简单的学生信息管理系统,可实现增加、删除、修改和查看学生信息的功能(此处用到面向对象和数组的知识点)
- java序列化和反序列化对象到mysql 的实现
- Java 对象序列化和反序列化 (实现 Serializable 接口)
- 用JAVA实现一个简单地Socket服务器,可以实现发给指定对象
- 【java】面向对象实现一个简单的表白流程
- 如何序列化和反序列化一个java对象
- java使用serializable进行序列化与反序列化实现对象clone(克隆)
- java执行序列化和反序列化操作实现对象的clone
- java中序列化一个对象保存在文件中的简单过程
- java----序列化与反序列化中及java序列化本质就是存储一个对象,然后在其他地方在调用它
- Java对象的序列化和反序列化及其实现和使用
- java用数据库实现一个简单的ATM机的案例,由Connection连接对象产生预编译处理对象PreparedStatement,可以防止sql的注入
- XStream实现java对象的序列化成xml和反序列化成java对象
- Java自己实现的一个简单的tomcat通过XML配置读取并返回相应Servlet对象
- 用java实现一个简单的序列化的例子