Java:对一个对象序列化和反序列化的简单实现

本文介绍了Java对象序列化和反序列化的概念,包括将Java对象转化为字节流以保存对象状态和在网络中传输。文章详细阐述了基本数据类型到字节的转换方法,并提供了一个简单的序列化和反序列化实现。测试代码验证了方法的有效性。
摘要由CSDN通过智能技术生成

名词解释

序列化:将Java对象转化成字节的过程

反序列化:将字节转化成Java对象的过程

字节:1字节(byte)= 8bit,bit就是计算机认识的二进制

序列化的作用

Java对象是在Java虚拟机中使用的,一旦Java进程结束,对象就会消失,要将只有虚拟机才认识的对象,保存在磁盘中,必须将对象转化成字节。

  1. 在RPC中的用处:序列化将对象转换为字节流,然后通过网络传输进行发送
  2. 保存对象的状态:当Java进程需要重启时,可以将对象序列化后保存在文件中,对象的状态不会因为进程的关闭而丢失

如何进行序列化

基本数据类型转为字节的思路

对于有多个字节的数据,用移位运算符,将每8位进行移位,用一个字节保存

  • Int类型:一个int有4个字节,可以创建一个长度为4的字节数组进行保存(short,long类似)
  • char类型:一个char有2个字节,用相应长度的字节数组保存后,反序列化时再强制转化为char
  • String类型:String的值主要是一个char数组,创建一个大小为char数组两倍的字节数组进行保存,反序列化时再转化为String
  • Double和Float类型:过程比较复杂(没学会),建议直接调用工具类

一个字节和其他类型的转换工具类

 View Code

如何序列化对象

其实序列化就是为了反序列化,保存对象之后必然要读取对象,所以站在反序列化的角度去研究序列化

得到一串字节流之后,要如何转换成对象

  • 要通过反射创建对象,首先要知道对象的类名称,然后调用类的默认构造函数
    • 要识别类名称,得知道类名称是由哪些字节转换的
    • 要有一个值存放类名称的字节长度,长度前最好放一个标记
  • 给对象的字段赋值,各种类型都不一样,需要识别
    • 需要数据类型的字节长度,根据长度去识别字节数组中的一部分
    • 将字节转换成指定的数据类型
    • 通过反射给对象的字段赋值

序列化的代码实现如下

  • 这里没有将对象的类型写入字节数组,因为反序列化的方法会传入一个class参数,可以直接构造对象
 1 public static byte[] serialize(Object object) throws IllegalAccessException, ClassNotFoundException {
 2         
 3         // 对象序列化后的字节数组
 4         byte[] objectByte = new byte[1024];
 5         // 通过移动的下标变量不断往数组添加数据
 6         int index = 0;
 7         // 标记后面的字节可以转化成对象
 8         objectByte[index] = SerializationNum.SERIALIZATION_OBJECT_NUM;
 9         
10         Class clazz = object.getClass();
11         // 遍历所有字段,将字段的值转化为字节
12         Field[] fields = clazz.getDeclaredFields();
13         // 一开始的标记占了一个位置
14         int len = 1;
15         for (Field field : fields) {
16 
17             // 将private属性设置为可访问
18             field.setAccessible(true);
19 
20             // 每次移动下标,给后面的数据腾地方
21             index += len;
22             // 不同的类型,不同的转化方式
23             Class fieldClass = field.getType();
24             // 每种类型对应一个标记
25             byte magicNum = SerializationNum.getNum(fieldClass);
26 
27             byte[] valueByte = new byte[0];
28             switch (magicNum){
29                 case SerializationNum.INTEGER_NUM:
30                     // 反射获取值
31                     Integer iValue = (Integer) field.get(object);
32                     // int类型是4个字节
33                     len = 4;
34                     // 转化成一个长度为4的字节数组
35                     valueByte = ByteUtils.int2bytes(iValue);
36                     break;
37 
38                 case SerializationNum.LONG_NUM:
39                     long longValue = field.getLong(object);
40                     len = 8;
41                    valueByte = ByteUtils.long2bytes(longValue);
42                     break;
43 
44                 case SerializationNum.STRING_NUM:
45                     String sValue = (String) field.get(object);
46                     valueByte = ByteUtils.String2bytes(sValue);
47                     len = valueByte.length;
48                     break;
49 
50                 default:
51                     break;
52             }
53             // 将类型和长度都添加字节数组中
54             objectByte[index++] = magicNum;
55             objectByte[index++] = (byte) len;
56             // 转化后的字节复制到对象字节数组
57             System.arraycopy(valueByte,0,objectByte,index,len);
58         }
59 
60         index += len;
61         // 对象已经整个被转化完毕的标记
62         objectByte[index] = SerializationNum.FINISH_NUM;
63 
64         return objectByte;
65     }

反序列化过程实现 

 1 public static Object deserialize(byte[] bytes,Class clazz) throws InstantiationException, IllegalAccessException {
 2 
 3         int index = 0;
 4         // 识别首部,确保是能够反序列化成对象的字节
 5         if (bytes[index] != SerializationNum.SERIALIZATION_OBJECT_NUM) {
 6             return null;
 7         }
 8 
 9         // 使用默认构造方法
10         Object obj = clazz.newInstance();
11         // 跳过最开始的魔数
12         byte len = 1;
13 
14         // 一个个给对象的字段赋值
15         for (Field declaredField : clazz.getDeclaredFields()) {
16             declaredField.setAccessible(true);
17             // 每次移动下标
18             index += len;
19 
20             // 不同的类型,不同的转化方式
21             Class fieldClass = declaredField.getType();
22 
23             byte magicNum = SerializationNum.getNum(fieldClass);
24 
25             // 防止数组越界
26             if (index >= bytes.length || bytes[index] != magicNum){
27                 continue;
28             }
29 
30             Object value = null;
31             // 先获取需要的字节长度
32              len = bytes[++index];
33             // 创建一个新数组去作为方法参数,如果不采用这种传参方式,也许能节约这个空间
34             byte[] srcBytes = new byte[len];
35             System.arraycopy(bytes,++index,srcBytes,0,len);
36 
37             switch (magicNum){
38                 case SerializationNum.INTEGER_NUM:
39 
40                     value = ByteUtils.bytes2Int_BE(srcBytes);
41                     break;
42 
43                 case SerializationNum.LONG_NUM:
44 
45                     value = ByteUtils.bytes2long(srcBytes);
46                     break;
47 
48                 case SerializationNum.STRING_NUM:
49 
50                    value = ByteUtils.bytes2String(srcBytes);
51                    break;
52 
53                 case SerializationNum.DOUBLE_NUM:
54                     value = ByteUtils.byte2Double(srcBytes);
55                     break;
56 
57                 default:
58                     break;
59             }
60             // 将值赋给对象
61             declaredField.set(obj,value);
62         }
63         return obj;
64     }

对各种基本数据类型进行特殊标记的工具类

 View Code

测试代码

 1 public static void main(String[] args) throws IOException {
 2         Entity entity = new Entity();
 3 
 4         entity.setName("name");
 5         entity.setNum(5);
 6         entity.setId(5L);
 7         entity.setMoney(100.55);
 8         
 9         try {
10             System.out.println("可以将对象序列号后的字节重新反序列化成对象");
11            byte[] objByte = serialize(entity);
12             Entity entity1 = (Entity) deserialize(objByte,Entity.class);
13             System.out.println(entity1);
14 
15 
16             System.out.println("保存在文件的字节,取出来后也可以反序列成对象");
17             FileOutputStream outputStream = new FileOutputStream("text.out");
18             FileInputStream inputStream = new FileInputStream("text.out");
19             byte[] fileBytes = new byte[1024];
20 
21             outputStream.write(objByte);
22             inputStream.read(fileBytes);
23 
24             Entity entity2 = (Entity) deserialize(fileBytes,Entity.class);
25             System.out.println(entity2);
26 
27         } catch (IllegalAccessException | ClassNotFoundException | InstantiationException e) {
28             System.out.println("类不能被构造");
29             e.printStackTrace();
30         }
31 
32     }

测试结果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值