美文网首页
java序列化

java序列化

作者: 7d972d5e05e8 | 来源:发表于2020-04-15 00:05 被阅读0次

    一、java序列化详解

    生成对象的二进制文件:

       Student s1 = new Student("张三", 18);
       ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/zhangsan/student.txt"));
       out.writeObject(s1);
       out.close();
    

    Student的定义:

    static class Student implements Serializable{
            private static final long serialVersionUID = 7606147849215550380L;
    
            private String name;
    
            private int age;
    
            public Student(String name, int age){
                this.name = name;
                this.age = age;
            }
        }
    

    生成后的二进制的文件:


    image.png

    二、解析序列化后的二进制文件

    既然java序列化生成上面的二进制文件,那么它就能完整解析处理。也只有java才知道它序列化时的协议,按照这个协议,我们自己也能实现反序列化工具。下面开始解析这个二进制文件。

    接下来按照顺序逐一解读

    1. 魔法数 \color{red}{aced},(可在ObjectStreamConstants接口中找到)
    2. 序列化格式的版本号 \color{red}{0005} (可在ObjectStreamConstants接口中找到)
    3. 接下来的 73 72 解读如下 (参见 协议文档)
      73 代表接下来读取到的将是一个对象 (final static byte TC_OBJECT = (byte)0x73;)
      72 代表该对象是一个对类的描述 (final static byte TC_CLASSDESC = (byte)0x72;)
    4. 接下来的 00 33, 指代该类描述信息的长度, 经过转换计算, 51字节数据,内容正好是类com.example.demo.serializable.TransientTest.Student的完整类名,长度正好匹配
    5. 然后是69 8e77 2b75 9823 ac,这八位是用来验证该类是否被修改过的验证码,也就是serialVersionUID 字段
    6. 接下来就是 02, 该一个字节长度的标志信息代表了 序列化中标识类版本 ; 该数值也是可以在ObjectStreamConstants接口中找到. (final static byte SC_SERIALIZABLE = 0x02;)
    7. 继续往下就是 00 02 , 这两个字节长度的标志信息指代的是 该类型中字段的个数. 如这里所见, 正好对应了Student 中的两个字段.
      接着往下就是对这三个字段的逐一解读了:
      7.1 紧接着就是49,查询ASCII码得到49表示I,即Integer类型。
      7.2 紧接着是00 03,表示该Integer类型字段名的长度有多少字节。表示有3个字节。
      7.3 取出上面所说的3个字节61 6765,继续查询ASCII码61=a,67=g,65=e。就是Student定义的age字段。
      7.4 继续解析字段名称。接下来1个字节 4c,查码表知道4c=L,L表示啥呢?java源码其实给了解释。如下:
    switch (signature.charAt(0)) {
                case 'Z': type = Boolean.TYPE; break;
                case 'B': type = Byte.TYPE; break;
                case 'C': type = Character.TYPE; break;
                case 'S': type = Short.TYPE; break;
                case 'I': type = Integer.TYPE; break;
                case 'J': type = Long.TYPE; break;
                case 'F': type = Float.TYPE; break;
                case 'D': type = Double.TYPE; break;
                case 'L':
                case '[': type = Object.class; break;
                default: throw new IllegalArgumentException("illegal signature");
            }
    

    L没有定义,其他都有定义。说明L表示用户自定义的类型。
    7.4 然后紧接着的2个字节00 04,表示该字段名长度为4字节。向后取4个字节出来6e 61 6d 65,继续查码表得到name。由于L类型需要获取类型信息,所以后面必须跟着类型信息。如下:

    for (int i = 0; i < fields.length; i++) {
                ObjectStreamField f = fields[i];
                out.writeByte(f.getTypeCode());
                out.writeUTF(f.getName());
                if (!f.isPrimitive()) {
                    out.writeTypeString(f.getTypeString());
                }
            }
    

    如果f不是基本类型,那么就必须writeTypeString。里面是已74为标志开始的。4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b表示:java.lang.String;然后已78 70(序列化约定的)标志结束。

    1. 然后开始内容解析,由于第一个字段是age,类型是int,表示4个字节。获取7870后面的4个字节:0000 0012,十进制=18。即age=18
    2. 然后获取74表示结束标志。后面紧跟的2个字节表示name的长度。00 06,表示长度为6个字节。
    3. 查询0006,后面正好6个字节,然后全部结束。e5 bc a0 e4 b8 89,可以e5开头,查询UTF-8,可知e5 bc a0表示:张。e4 b8 89表示:三。

    到此整个java序列化的二进制文件,全部分析完成。

    参考文章:https://blog.csdn.net/lqzkcx3/article/details/79463450

    相关文章

      网友评论

          本文标题:java序列化

          本文链接:https://www.haomeiwen.com/subject/cajfvhtx.html