序列化 订阅
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。确切地说,代码执行序列化需要特殊的权限:即指定了 SerializationFormatter 标志的 SecurityPermission。在默认策略下,通过 Internet 下载的代码或 Internet 代码不会授予该权限;只有本地计算机上的代码才被授予该权限。通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存不可序列化的重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。 展开全文
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。确切地说,代码执行序列化需要特殊的权限:即指定了 SerializationFormatter 标志的 SecurityPermission。在默认策略下,通过 Internet 下载的代码或 Internet 代码不会授予该权限;只有本地计算机上的代码才被授予该权限。通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存不可序列化的重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。
信息
类    型
概念
类    别
通信
中文名
序列化
外文名
Serialization
序列化目的
1、以某种存储形式使自定义对象持久化;2、将对象从一个地方传递到另一个地方。3、使程序更具维护性。
收起全文
精华内容
下载资源
问答
  • 序列化
    千次阅读
    2022-01-12 03:22:50

    1、什么是序列化:

            两个服务之间要传输一个数据对象,就需要将对象转换成二进制流,通过网络传输到对方服务,再转换成对象,供服务方法调用。这个编码和解码的过程称之为序列化和反序列化。所以序列化就是把 Java 对象变成二进制形式,本质上就是一个byte[]数组。将对象序列化之后,就可以写入磁盘进行保存或者通过网络中输出给远程服务了。反之,反序列化可以从网络或者磁盘中读取的字节数组,反序列化成对象,在程序中使用。

    2、序列化优点:

    ① 永久性保存对象:将对象转为字节流存储到硬盘上,即使 JVM 停机,字节流还会在硬盘上等待,等待下一次 JVM 启动时,反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间

    ② 方便网络传输:序列化成字节流形式的对象可以方便网络传输(二进制形式),节约网络带宽

    ③ 通过序列化可以在进程间传递对象

    3、序列化的几种方式:

    参考文章:https://www.jianshu.com/p/7298f0c559dc

    3.1、Java 原生序列化:

            Java 默认通过 Serializable 接口实现序列化,只要实现了该接口,该类就会自动实现序列化与反序列化,该接口没有任何方法,只起标识作用。Java序列化保留了对象类的元数据(如类、成员变量、继承类信息等),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。

            实现 Serializable 接口的类在每次运行时,编译器会根据类的内部实现,包括类名、接口名、方法和属性等自动生成一个 serialVersionUID,serialVersionUID 主要用于验证对象在反序列化过程中,序列化对象是否加载了与序列化兼容的类,如果是具有相同类名的不同版本号的类,在反序列化中是无法获取对象的,显式地定义 serialVersionUID 有两种用途:

    • 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的 serialVersionUID;
    • 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的 serialVersionUID;

    如果源码改变,那么重新编译后的 serialVersionUID 可能会发生变化,因此建议一定要显示定义 serialVersionUID 的属性值。

    3.2、Hessian 序列化:

            Hessian 序列化是一种支持动态类型、跨语言、基于对象传输的网络协议。Java 对象序列化的二进制流可以被其他语言反序列化。 Hessian 协议具有如下特性:

    • 自描述序列化类型。不依赖外部描述文件或接口定义, 用一个字节表示常用
    • 基础类型,极大缩短二进制流
    • 语言无关,支持脚本语言
    • 协议简单,比 Java 原生序列化高效

            Hessian 2.0 中序列化二进制流大小是 Java 序列化的 50%,序列化耗时是 Java 序列化的 30%,反序列化耗时是 Java 反序列化的20% 。

            Hessian 会把复杂对象所有属性存储在一个 Map 中进行序列化。所以在父类、子类存在同名成员变量的情况下, Hessian 序列化时,先序列化子类 ,然后序列化父类,因此反序列化结果会导致子类同名成员变量被父类的值覆盖。

    3.3、Json 序列化:

            JSON 是一种轻量级的数据交换格式。JSON 序列化就是将数据对象转换为 JSON 字符串,在序列化过程中抛弃了类型信息,所以反序列化时需要提供类型信息才能准确地反序列化,相比前两种方式,JSON 可读性比较好,方便调试。

    4、为什么不建议使用Java序列化

    该部分主要参考文章:为什么我不建议你使用Java序列化 - 掘金

            目前主流框架很少使用到 Java 序列化,比如 SpringCloud 使用的 Json 序列化,Dubbo 虽然兼容 Java 序列化,但默认使用的是 Hessian 序列化。这是为什么呢?主要是因为 JDK 默认的序列化方式存在以下一些缺陷:无法跨语言、易被攻击、序列化的流太大、序列化性能太差等

    4.1、无法跨语言:

            Java 序列化只支持 Java 语言实现的框架,其它语言大部分都没有使用 Java 的序列化框架,也没有实现 Java 序列化这套协议,因此,两个不同语言编写的应用程序之间通信,无法使用 Java 序列化实现应用服务间传输对象的序列化和反序列化。

    4.2、易被攻击:

            对象是通过在 ObjectInputStream 上调用 readObject() 方法进行反序列化的,它可以将类路径上几乎所有实现了 Serializable 接口的对象都实例化。这意味着,在反序列化字节流的过程中,该方法可以执行任意类型的代码,这是非常危险的。

            对于需要长时间进行反序列化的对象,不需要执行任何代码,也可以发起一次攻击。攻击者可以创建循环对象链,然后将序列化后的对象传输到程序中反序列化,这种情况会导致 hashCode 方法被调用次数呈次方爆发式增长, 从而引发栈溢出异常。

            序列化通常会通过网络传输对象,而对象中往往有敏感数据,所以序列化常常成为黑客的攻击点,攻击者巧妙地利用反序列化过程构造恶意代码,使得程序在反序列化的过程中执行任意代码。 Java 工程中广泛使用的 Apache Commons Collections、Jackson、fastjson 等都出现过反序列化漏洞。如何防范这种黑客攻击呢?有些对象的敏感属性不需要进行序列化传输,可以加 transient 关键字,避免把此属性信息转化为序列化的二进制流,除此之外,声明为 static 类型的成员变量也不能要序列化。如果一定要传递对象的敏感属性,可以使用对称与非对称加密方式独立传输,再使用某个方法把属性还原到对象中。

    4.3、序列化后的流太大

            序列化后的二进制流大小能体现序列化的性能。序列化后的二进制数组越大,占用的存储空间就越多,存储硬件的成本就越高。如果我们是进行网络传输,则占用的带宽就更多,这时就会影响到系统的吞吐量。

            Java 序列化中使用了 ObjectOutputStream 来实现对象转二进制编码,那么这种序列化机制实现的二进制编码完成的二进制数组大小,相比于 NIO 中的 ByteBuffer 实现的二进制编码完成的数组大小,有没有区别呢?

    我们可以通过一个简单的例子来验证下:

    User user = new User();
    user.setUserName("test");
    user.setPassword("test");
          
    ByteArrayOutputStream os =new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(os);
    out.writeObject(user);
    byte[] testByte = os.toByteArray();
    System.out.print("ObjectOutputStream 字节编码长度:" + testByte.length + "\n");
    ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
    
    byte[] userName = user.getUserName().getBytes();
    byte[] password = user.getPassword().getBytes();
    byteBuffer.putInt(userName.length);
    byteBuffer.put(userName);
    byteBuffer.putInt(password.length);
    byteBuffer.put(password);        
    byteBuffer.flip();
    byte[] bytes = new byte[byteBuffer.remaining()];
    System.out.print("ByteBuffer 字节编码长度:" + bytes.length+ "\n");

    运行结果:

    ObjectOutputStream 字节编码长度:99
    ByteBuffer 字节编码长度:16

     4.4、序列化性能太差:

            序列化的速度也是体现序列化性能的重要指标,如果序列化的速度慢,网络通信效率就低,从而增加系统的响应时间。我们再来通过上面这个例子,来对比下 Java 序列化与 NIO 中的 ByteBuffer 编码的性能:

        User user = new User();
        user.setUserName("test");
        user.setPassword("test");
          
        long startTime = System.currentTimeMillis();
          
         for(int i=0; i<1000; i++) {
            ByteArrayOutputStream os =new ByteArrayOutputStream();
              ObjectOutputStream out = new ObjectOutputStream(os);
              out.writeObject(user);
              out.flush();
              out.close();
              byte[] testByte = os.toByteArray();
              os.close();
         }
          
        long endTime = System.currentTimeMillis();
        System.out.print("ObjectOutputStream 序列化时间:" + (endTime - startTime) + "\n");
    long startTime1 = System.currentTimeMillis();
    for(int i=0; i<1000; i++) {
       ByteBuffer byteBuffer = ByteBuffer.allocate( 2048);
    
            byte[] userName = user.getUserName().getBytes();
            byte[] password = user.getPassword().getBytes();
            byteBuffer.putInt(userName.length);
            byteBuffer.put(userName);
            byteBuffer.putInt(password.length);
            byteBuffer.put(password);
                
            byteBuffer.flip();
            byte[] bytes = new byte[byteBuffer.remaining()];
    }
    long endTime1 = System.currentTimeMillis();
    System.out.print("ByteBuffer 序列化时间:" + (endTime1 - startTime1)+ "\n");

    运行结果:

    ObjectOutputStream 序列化时间:29
    ByteBuffer 序列化时间:6

    更多相关内容
  • 序列化和反序列化

    万次阅读 多人点赞 2021-12-11 13:07:52
    我以前确实对序列化,乃至现在也是不是很熟悉,有时候查找资料,但依旧懵懵懂懂,不过还好遇到一个博主,确定写的挺好的,链接会放再底部 废话不多说,先看官网定义: 序列化 (Serialization)是将对象的状态信息转换...

    我以前确实对序列化,乃至现在也是不是很熟悉,有时候查找资料,但依旧懵懵懂懂,不过还好遇到一个博主,确定写的挺好的,链接会放再底部

    废话不多说,先看官网定义:

    序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

    序列化主要有两个用途

    • 把对象的字节序列永久保存到硬盘上,通常存放在一个文件中(序列化对象)
    • 在网络上传送对象的字节序列(网络传输对象)

    实际上就是将数据持久化,防止一直存储在内存当中,消耗内存资源。而且序列化后也能更好的便于网络运输何传播

    • 序列化:将java对象转换为字节序列
    • 反序列化:把字节序列回复为原先的java对象

    对象如何序列化?

    java目前没有一个关键字是直接定义一个所谓的“可持久化”对象

    对象的持久化和反持久化需要靠程序员在代码里手动显式地进行序列化和反序列化还原的动作。

    举个例子,假如我们要对Student类对象序列化到一个名为student.txt的文本文件中,然后再通过文本文件反序列化成Student类对象:

    按我的理解就是序列化:将一个对象转化为一种格式,能够更好的传输和电脑理解。

    反序列化是转换过来,便于人们观看的。

    先写一个实体类

    import java.io.Serializable;
    public class Student implements Serializable {
        private String name;
        private Integer age;
        private Integer score;
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    	//getter、setter省略
    }
    
    

    然后填写一个测试类

    public class test01 {
        public static void serialize(  ) throws IOException {
    
            Student student = new Student();
            student.setName("linko");
            student.setAge( 18 );
            student.setScore( 1000 );
    
            ObjectOutputStream objectOutputStream =
                    new ObjectOutputStream( new FileOutputStream( new File("student.txt") ) );
            objectOutputStream.writeObject( student );
            objectOutputStream.close();
    
            System.out.println("序列化成功!已经生成student.txt文件");
            System.out.println("==============================================");
        }
    
        public static void deserialize(  ) throws IOException, ClassNotFoundException {
            ObjectInputStream objectInputStream =
                    new ObjectInputStream( new FileInputStream( new File("student.txt") ) );
            Student student = (Student) objectInputStream.readObject();
            objectInputStream.close();
    
            System.out.println("反序列化结果为:");
            System.out.println( student );
        }
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            serialize();
            deserialize();
        }
    }
    

    运行结果:

    image-20211211111125205

    Serializable接口的作用

    这时候我们来看下Serializable接口,这时候我们点进去,会发现这个接口是个空接口,并没有包含任何方法

    image-20211211111237128

    我们会感觉这个接口没什么用,那这时我们不继承Serializable接口,运行一下试试

    image-20211211111501118

    这时候我们会看到这个错误,java.io.NotSerializableException报出了没有序列化异常

    然后我们按照错误提示,由源码一直跟到ObjectOutputStreamwriteObject0()方法底层(虽然我看不懂

    image-20211211112104380

    如果一个对象既不是字符串数组枚举,而且也没有实现Serializable接口的话,在序列化时就会抛出NotSerializableException异常!

    说的太好了哭,强烈推荐底部链接的那个博主

    他一说我就大概知道懂什么意思了。

    先说说instanceof,它是用来测试一个对象是否为一个类的实例

    // remaining cases
    //判断这个obj对象是否是String类
    if (obj instanceof String) {
        writeString((String) obj, unshared);
        //判断这个obj对象是否是数组
    } else if (cl.isArray()) {
        writeArray(obj, desc, unshared);
        //判断这个obj对象是否是枚举
    } else if (obj instanceof Enum) {
        writeEnum((Enum<?>) obj, desc, unshared);
        //判断这个obj对象是否是实现序列化
    } else if (obj instanceof Serializable) {
        writeOrdinaryObject(obj, desc, unshared);
        //否则就报错
    } else {
        //报错是否由有扩展信息,详细信息可以观看源码
        if (extendedDebugInfo) {
            throw new NotSerializableException(
                cl.getName() + "\n" + debugInfoStack.toString());
        } else {
            throw new NotSerializableException(cl.getName());
        }
    }
    

    所以Serializable接口只是一个做标记用的

    它告诉代码只要是实现了Serializable接口的类都是可以被序列化的!然而真正的序列化动作不需要靠它完成。

    serialVersionUID号有何用?

    在我们写项目的时候,我们会经常看到这么这么一个语句。

    定义一个名为serialVersionUID的字段

    private static final long serialVersionUID = -4392658638228508589L;
    

    为什么有这句话呢,为什么要搞一个名为**serialVersionUID**的序列号

    继续做一个简单实验,依旧是上面的Student类,然后我们先不写序列号。然后在Student中添加一个字段,并添加序列号

    private static final long serialVersionUID = -4392658638228508589L;
    private String name;
    private Integer age;
    private Integer score;
    private Long studentId;
    

    然后再次序列化和反序列化一下

    再然后我们拿刚才已经序列化好的student.txt文件,然后试着反序列化一下。

    test01测试类中,我们将主函数的序列化调用的函数给删掉,再把序列号给删掉

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //serialize();
        deserialize();
    }
    

    我们会发现报错了。太长了,没能截屏出来

    它说序列号不一致

    Exception in thread "main" java.io.InvalidClassException: demo01.Student; local class incompatible: stream classdesc serialVersionUID = -4392658638228508589, local class serialVersionUID = -2825960062149872719
    	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
    	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
    	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
    	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
    	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
    	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
    	at demo01.test01.deserialize(test01.java:32)
    	at demo01.test01.main(test01.java:41)
    

    从这几个地方我们可以看出几个重要的信息

    • serialVersionUID是序列化前后的唯一标识符
    • 默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!

    第1个问题: serialVersionUID序列化ID,可以看成是序列化和反序列化过程中的“暗号”,在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。

    所以,为了serialVersionUID的确定性,写代码时还是建议,凡是implements Serializable的类,都最好人为显式地为它声明一个serialVersionUID明确值!

    当然,如果不想手动赋值,你也可以借助IDE的自动添加功能,比如我使用的IntelliJ IDEA,按alt + enter就可以为类自动生成和添加serialVersionUID字段,十分方便;

    两种特殊情况

    • 凡是被static修饰的字段是不会被序列化的
    • 凡是被transient修饰符修饰的字段也是不会被序列化的 更正:经过@Programming_artist同学的修正和参考这位作者Java中关键字transient解析,这里不能一概而论。只有实现Serializable接口,被transient修饰符修饰的字段也是不会被序列化的 。后面详细解释一下

    对于第一个,因为序列化保存的是对象的状态而非类的状态,所以会忽略static静态域。

    对于第二个,就需要了解transient修饰符的作用了(在实现Serializable接口的情况下

    如果在序列化某个类的对象时,就是不希望某个字段被序列化(比如这个字段存放的是隐私值,如:密码等),那这时就可以用transient修饰符来修饰该字段。

    比如在之前定义的Student类中,加入一个密码字段,但是不希望序列化到txt文本,则可以:

    image-20211211130118972
    jvm是通过Serializable这个标识来实现序列化的,那么我们是否可以自己自定义是否决定序列化呢?答案是可以的,java给我们提供了Externalizable接口,让我们自己实现。
    image-20220425105816374
    从实现的接口上看,它是继承了Serializable接口,但使用这个接口,需要实现writeExternal以及readExternal这两个方法,来自己实现序列化和反序列化。
    实现的过程中,需要自己指定需要序列化的成员变量,此时,static和transient关键词都是不生效的,因为你重写了序列化中的方法。感觉这个static验证也可以再多写点(有时间再更呜
    在这里插入图片描述在这里插入图片描述

    约束性加持(后面两个有时间再更)

    从上面的过程可以看出,序列化和反序列化是有漏洞的,因为序列化和反序列化是有中间过程的,如果别人拿到中间字节流,然后加以伪造或篡改,那反序列化出来的对象就有一定风险了。

    单例模式增强

    分享链接

    展开全文
  • 【RPC】序列化与反序列化

    万次阅读 2022-06-26 22:04:53
    1. 基本概念? 2. 文本格式的序列化方案 2.1 XML格式 2.2 JSON格式 3. 二进制格式的序列化方法 4. 序列化框架选型

    1. 基本概念?

    序列化和反序列化是一种数据转化的技术,从数据的用途来看,序列化就是为了将数据按照规定的格式就行重组,在保持原有的数据语义不变的情况下,达到存储或者传输的目的。

    反序列化则是为了将序列化后的数据重新还原成具有语义的数据,以达到重新使用或者复用原有数据的目的。

    序列化框架则是提供这种转化功能的解决方案和工具库。

    序列化协议就是一种结构化的数据格式,它一般指的是数据格式化的规则。

    2. 文本格式的序列化方案

    2.1 XML格式

    XML数据的基本单位是元素,元素由其实标签、元素内容和结束标签组成。用户把要描述的数据对象放在其实标签和结束标签之间。

    在Java领域中,目前常见的XML格式的序列化框架有三种:

    • 第一种是JDK自带的XML格式的序列化API,分别是java.beans.XMLEncoder和java.beans.XMLDecoder,这两个类提供了XML格式数据的序列化能力。

    • 第二种是Digester,它是从Struts发展而来的,它只能从XML文件解析成Java Bean对象,功能比较单一。

    • 第三种是XStream,是一个功能比较丰富的框架,它不仅可以将Java Bean对象和XML数据互传,还支持JSON格式的转化啊。并且它的API使用也比较简单。现在大多数Java Bean和XML数据互转都用该框架来实现。

      • 在Maven依赖中加入以下依赖:
      <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.15</version>
      </dependency>
      

      API也比较简单:

      // 序列化
      String xml = xstream.toXML(对象);
      // 反序列化,需要强制转化
      类 对象 = (类)xtream.fromXML(xml);
      

    2.2 JSON格式

    JSON是JavaScript Object Notation的缩写,可以理解为是JavaScript对象的表示方法。

    JSON格式整体看起来每个数据都是key-value形式。JSON数据本身也有自己的一套规范。比如数据就是中括号“[]”包裹起来的内容,数组中的每个元素都用“,”隔开,每个对象都用“{}”包裹起来。

    JSON和XML相比,序列化后的数据包会更小,反序列化JSON格式的数据要比反序列化XML格式的数据更容易,速度也会更快。

    JSON作为文本格式的序列化方案,并没有对数据进行压缩,原封不动地呈现了所有数据,并且还增加了一些标示符来帮助反序列化,数据包依旧非常大,在序列化和反序列化地性能上也还有很大进步空间。

    目前集中常见地支持JSON格式的序列化矿建如下:

    • Fastjson:该框架是阿里巴巴开源的JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以反序列化为Java Bean。它的优点是速度快、社区较为活跃、API十分简单,不需要第三方库的依赖。
    • Jackson:简单易用并且性能相对较高一些,流式解析JSON格式,在解析数据量大的JSON格式文本时比较有优势。将复杂类型的JSON转换为Bean会出现问题,比如一些集合Map、List的转换;将复杂类型的Bean转换为JSON,转换的JSON格式不是标准的JSON格式
    • Gson:由Google开源的JSON解析库,功能最全,它没有依赖第三方类库,但是在性能上比JSON-lib和Fastjson稍微差一点。
    • JSON-lib:最开始的JSON解析工具,它在进行Java Bean和JSON字符串互相转化的时候,依赖了太多的第三方库,并且在转化的时候还有缺陷,比如数值溢出时不抛出错误。该工具最近一次更新也是在七年前了,它已经满足不了现在的开发要求。

    下面详细介绍一下Fastjson。

    Fastjson从名字上就可以很好理解:快和JSON。专注一件事:就是将Java对象与JSON字符串互转。

    **从序列化的角度来看,**在进行序列化时要将一个对象转化为JSON格式的字符串,必定经历两个步骤,

    • 第一步就是获取Java对象的属性值。JSON格式中的每个数据都是key-value的形式。所以在转化为JSON格式之气那,必须获取对象的属性值。
    • 第二步就是将属性名及属性值按照JSON的规范组合成JSON字符串。Fastjson在第一步获取属性值时采用了ASM库,并没有选择传统的Java反射来获取属性值,因为反射的性能很差,远不如直接使用ASM库来获取Java对象的属性值。并且Fastjson并没有将整个ASM库引入Fastjson框架,二十引入了ASM库的部分内容。在第二步中由于用字符串直接拼接的性能太差,而使用java.lang.StringBuilder会相对好得多,所以Fastjson提供了类似StringBuilder的工具类SerializeWriter,进一步提升了字符串拼接的性能。

    除了通过以上两个策略来提升性能,Fastjson还做了以下三点比较重要的优化:

    • 第一点就是用ThreadLocal来缓存buf。SerializeWriter中有一个char buf[]属性,每序列化一次,都要做一次分配,它的作用时缓存每次序列化后字符串的内存地址,Fastjson使用ThreadLocal来缓存buf。这个办法能够减少对象分配和GC,从而提升性能。

      protected char buf[];
      private final static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>();
      

      在SerializeWriter的构造方法,当调用SerializeWriter的构造方法时,先从bufLocal中获取,如果在bufLocal中有缓存存在,则把值清空。这样就不需要重新分配对象,也减少了GC。如果没有缓存,才执行new char[2048]的操作。

    • 第二点就是使用IdentityHashMap存储Class和ObjectSerializer的映射关系:Fastjson有一个重要的类叫做SerializeConfig,其中缓存了各种配置,包括Calss和Serizlier的映射关系。该属性定义如下:

      private final IdentityHashMap<Type, ObjectSerializer> serializers;
      

      其中ObjectSerializer时Fastjson的一个接口,它定义了序列化的方法,每个类型都对应一个ObjectSerializer,比如数组类型的序列化就用到了ArraySerializer等。每个类型都需要和ObjectSerializer做映射,并且缓存,这样能够方便序列化处理类的快速查找、避免ObjectSerializer的反复创建。如果使用HashMap,则存在并发问题;如果使用ConcurentHashMap,则会有自选等待的情况,导致性能变低。Fastjson实现了一个IdentityHashMap,它参考了JDK自带的java.util.IdentityHashMap,通过避免equals操作来提高性能,Fastjson重写了值匹配的逻辑,在对比是否为同一个key值时只认为key==entry.key才算是同一个。除了去掉equals操作,Fastjson还去掉了transfer操作,保证并发处理时正常工作,但是不会导致死循环。

    • 第三点优化就是排序输出,为反序列化做准备。Fastjson默认启用排序:JSON格式的数据都是一种key-value结构,正常的HashMap是无序的,Fastjson默认是按照key的顺序进行排序输出的,这样做为了反序列化读取的时候只需要做key的匹配,而不需要把key从输入读取出来。

    Fastjson在反序列化的优化也四点:

    • 第一点是使用IdentityHashMap存储Class和ObjectDeserializer的映射关系:每个类型都应该和对应的序列化器ObjectDeserializer一一映射,并且缓存,减少反复创建序列化器的开销。反序列化时也和序列化时一样,用IdentityHashMap进行存储,并且该缓存存放在ParserConfig组件中。
    • 第二点是读取token值时基于预测:Fastjson在反序列化一个JSON字符串时,下一个字符一般情况下是可以预估的。比如字符”}“之后最有可能出现的是”,“”]””}”等。预测的逻辑被封装在JSONLexerBase中。
    • 第三点是与序列化相呼应,快速匹配。与上面的序列化有序输出一一对应,反序列化的时候key-value的内容是有序的,读取的时候只需要做key的匹配,而不需要把key读取出来再匹配。
    • 第四点就是基于SymbolTable算法缓存关键字。使用SymbolTable算法缓存关键字,可以避免创建新的字符串对象。假设在一个JSON字符串中,有成千上万个同样的JSON对象的数组,在数据转换过程中,如果不对这些JSON对象中的key做缓存,那么将存在成千上万个同样的字符串对象(值相同)。

    3. 二进制格式的序列化方法

    二进制格式的序列化方案指数据按照某种编排规则通过二进制格式呈现。将对象序列化为二进制格式的数据能够大大减小数据包大小,从而提高传输速度和解析速度。二进制格式的序列化方案相对于文本格式的序列化方案而言,性能较高,对数据容易做加密处理,安全性较高。

    下面举两个二进制格式的序列化方案:

    • 第一个方案就是JDK原生序列化方法:Java序列化是在JDK1.1中引入的,Java序列化的API可以帮助我们将Java对象和二进制流互相转化,API的使用也很简单,只要在序列化的类定义上实现java.io.Serializable接口,然后通过OBjectInputStream和ObjectOuputStream即可实现两者的转化。该序列化方式将字段类型信息用字符串格式写到了二进制中,这样反序列化方就可以根据字段信息实现反序列化。该序列化方式生成的字节流太大,并且性能也不高,所以目前基本不会用该序列化方法。

    • 第二个方案就是Kryo:Kryo是一款优秀的Java序列化框架,它是一款快速序列化/反序列化的工具,相比于JDK的序列化方案,Kryo使用了字节码生成机制(底层依赖了ASM库),因此在序列化和反序列化的性能上提升了不少,Kryo还做了数据压缩的优化,见笑了序列化后的数据包大小。它除了在性能提升方面和简化数据包方面做了优化,还提供了非常便利的功能。Kryo序列化出来的结果是Kryo独有的一种格式,它会将这种格式的数据转化为二进制格式的数据。(Kryo目前只有Java版本的实现。)

      Kryo之所以在序列化和反序列化的性能上有良好的优势,是因为Kryo在时间消耗和空间消耗上都做了一定程度的优化,在时间消耗方面的优化,主要是预先缓存了元数据信息。Kryo先加载类元数据信息,将加载后的二进制数据存入缓存,保证之后的序列化及反序列化都不需要重新加载该类元数据信息。虽然在第一次加载时耗费性能,但是方便了后续的加载。在空间消耗上,为了降低序列化后的数据大小,做出了一下两点优化:

      • 变长的设计降低空间消耗:Kryo对long、int这两种数据类型采用边长字节存储来代替Java中使用固定的长度存储的模式。以int数据类型为例,在Java中一般需要4字节去存储,而对Kryo来说,可以通过1~5个变长字节去存储,从而避免较小的数值的高位都是0,0存储非常浪费空间。
      • 压缩元数据信息:Kryo提供了单独序列化对象信息,以及将类的元数据信息与对象信息一起序列化这两种徐方式。由于第二种方式携带的元数据信息太大,导致序列化后的数据特别大,所以Kryo由提供了一种提前注册类的方式,这种方式是在Kryo中将Java类用唯一ID表示,当Kryo对Class进行序列化时只需要将对应的ID数值进行序列化即可,无须序列化类的全部元信息,而在反序列化时也只需要根据ID来找到对应的类,然后通过类加载进行加载,这样大大减少了序列化收的数据大小。

    Kryo和JDK的序列化方案有一个共同的特点,那就是都只能在Java语言中使用。二进制的序列化方案有一个比较严重的问题——它对异构性语言并不友好。目前有三个方案可以解决异构语言的问题:

    • 第一个方案:根据相同的机制重新实现对应语言的序列化框架
    • 第二个方案:通过与编程语言无关的IDL来解决异构语言的问题。目前通过这种方案实现对多语言的支持,并且较为常见的序列化方案有:
      • Thrift:是Facebook开源的一个高性能、轻量级RPC服务框架。在Thrift框架内部提供了对thrift协议的序列化工具,并且见笑了序列化后的数据包大小,以及提升了解析的性能,但是Thrift没有暴露序列化和反序列化的API,thrift协议的序列化能力与Thrift框架强耦合,所以它支持其他协议的序列化和反序列化比较困难。由于Thrift有IDL的设计,支持多语言之间进行远程调用,所以它同样支持多语言之间的序列化。
      • ProtoBuf:全称为Google Protocal Buffer,是Google公司内部的混合语言数据标准,它是一种轻便高效的结构化数据存储格式,可用于序列化。它解析速度快,序列化和反序列化API非常简单,它的文档也非常丰富,并且可以跟各种传输协议结合使用。它同样有IDL的设计,并且提供了IDL的编译器,它支持的语言也非常多,比如C++、C#、Dart、Golang、Java、Python、Rust等。ProtoBuf在性能上也具有非常大的又是,主要体现在:
        • 用标识符来识别字段。(序列化后的数据大大减小了)
        • 自定义可变数据类型Varint用来存储整数。
        • 记录字符串长度,解析时直接截取。

    4. 序列化框架选型

    在序列化框架选型阶段,需要从多个角度来考量各个序列化框架。在做选型之前,首先需要明确项目的需求,比如需要将序列化框架运用在数据的传输中,但是要明确整个传输过程中更侧重性能,还是侧重可读性,仅明确这一点就可以在选型初期排除一大批序列化框架。

    通过初期筛选的序列化框架可以从以下几个角度综合考量:

    • 通用性。
    • 性能。
    • 可扩展性。
    • 安全性。
    • 支持的数据类型的丰富程度。
    • 可读性。
    • 开源社区成熟度和活跃度。
    展开全文
  • 什么是反序列化?反序列化的过程,原理

    千次阅读 多人点赞 2021-12-16 13:55:12
    本篇主要分析java序列化、反序列化的过程,原理, 并且通过简化版URLDNS做案例分析利用链原理。

    介绍

    本篇主要分析java序列化、反序列化的过程,原理,
    并且通过简化版URLDNS做案例分析利用链原理。

    本篇很重要,打好基础是关键。

    什么是序列化

    我的理解很简单,就是将对象转化为可以传输、储存的数据。
    什么意思?
    正常一个对象,只能存在于程序运行时,当程序运行结束了,对象就消失了,对象只在程序运行时存在

    如有这样一个需求,我想把user对象(包含了名字,年龄,身高,简历信息)传给数据持久化的服务(保存数据的服务)。
    需要怎么做?
    创建一个json对象,然后把user对象信息填写到json中,
    {“name”:“zhangsan”,“age”:12,“height”,12,“resume”:{“school”:“tsinghua”,“level”:1}}
    然后把这个json传给持久化服务,
    这样就要求每次传输数据时,都要将对象先转为json等格式数据,再进行传输,而且这只是简单的数据。
    如果是更复杂的,对象A里有对象B、C,对象B又有D,则需要手动,将这些数据转成json,发送到远程服务器,再根据这些数据,还原对象。

    而使用序列化,则是将user对象序列化为数据,传输到持久化服务器上时,再反序列化,就得到了user对象,让对象可以传输。
    所以序列化,就是将对象转化为数据,相当与给对象拍了个快照,传输或者储存,使用时再反序列化,又变为了对象。

    看下php序列化和反序列化过程:
    image

    执行结果:
    image

    再反序列化看下:
    image

    image

    php的序列化将对象转化为了字符串,包含了对象的所有数据信息,
    反序列化时再根据这些信息还原对象。

    【一>所有资源获取<一】
    1、200份很多已经买不到的绝版电子书
    2、30G安全大厂内部的视频资料
    3、100份src文档
    4、常见安全面试题
    5、ctf大赛经典题目解析
    6、全套工具包
    7、应急响应笔记
    8、网络安全学习路线

    JAVA序列化

    一个java序列化对象例子:

    class Person implements Serializable{
        public String name;
        public int age;
        Person(String name,int age){
            this.name = name;
            this.age = age;
        }
    }
    
    public class Main {
        public static void main(String []args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
            FileOutputStream out =new FileOutputStream("person.txt");
            ObjectOutputStream obj_out = new ObjectOutputStream(out);
            obj_out.writeObject(new Person("z3",12));
        }
    }
    
    

    FileOutputStream和ObjectOutputStream是java的流操作,可以把OutputStream当做一个单向流出的水管,FileOutputStream打开了文件,就相当于给文件接了一个File类型水管,然后把FileOutputStream类型对象传给了ObjectOutputStream,相当于把File类型水管接到了Object类型水管。

    由于Object类是所有类的父类,所以Object类型水管可以投放任何对象,

    这里创建了Person对象并传给writeObject方法,
    相当于把Person对象扔进了Object类型水管,即 Person对象->Object类型水管->File类型水管->文件
    这样就把Person对象写入了文件,

    java输入输出流的方式处理数据真聪明
    如果我想把序列化对象写入byte数组,那就创建个byteArrayOutputStream类型水管,然后,把它接到Object类型水管上,后面步骤不变,则:Person对象->Object类型水管->byte类型水管->byte数组

    回到正题,
    打开文件查看是乱码。
    image

    因为每个语言都有自己的序列化规则,java的序列化方式不适用于用文本方式查看。
    可以使用SerializationDumper工具查看。
    image

    红框内对应的是类名,类属性名,成员变量值。
    如果在类里添加方法,会被序列化吗?
    答案是不会,调试发现,writeObject时只会将FieldValues即 成员变量序列化,
    所以,序列化的并不是整个对象,只是对象的属性值。

    以上就是java序列化的方法了,
    但仔细看代码,会发现Person类继承了Serializable接口,
    但是没有实现接口中的方法,,那继承这接口有什么用?去掉试试。
    报错了
    image

    这是什么原因?
    看一下Serializable接口,接口是空的

    public interface Serializable {
    }
    
    

    调试看一下,为什么非要继承个空接口才能序列化?这空接口起到了什么作用?
    如图
    image

    原因在这里,如果类不继承Serializable,那对象不属于字符串、数组、枚举类型,那就会进入else,抛出NotSerializableException异常,
    所以,Serializable,只是用来标志,这个对象可以被序列化的。

    在查看Serializable接口时,在注释中看到,建议在类里声明serialVersionUID变量,不然自动生成的serialVersionUID很容易出问题。
    image

    因为默认的serialVersionUID是jdk生成的,不同版本jdk可能生成的值不同,
    看名字应该能猜到serialVersionUID是序列化版本的标志,如果序列化数据的serialVersionUID 和对应类的serialVersionUID 不一致,就会导致反序列化失败。

    java反序列化

    首先看下反序列化代码

    class Person implements Serializable{
        public String name;
        public int age;
        Person(String name,int age){
            this.name = name;
            this.age = age;
        }
    }
    
    public class Main {
        public static void main(String []args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
            FileInputStream in =new FileInputStream("person.txt");
            ObjectInput obj_in = new ObjectInputStream(in);
            Person p = (Person) obj_in.readObject();
            System.out.println(p.name);
        }
    }
    
    

    成功将上面序列化的对象读出,

    和上面的代码区别只是把Output换为了Input,把writeObject换为了readObject。
    这也很好理解,把单向流出的水管换为单向流入的(Output换为Input),然后把写入数据的writeObject换为readObject,即:序列化数据person.txt->File类型水管->Object类型水管->Object对象。
    (Person)这个用法是强制类型转换,将Object转Person类型,

    再调试下,看java如何从一个二进制的序列化数据,恢复为java对象的,

    首先读取了二进制的第一个字节,如果不是0x73,即10进制的115,就抛异常。
    image

    根据变量名TC_OBJECT可以猜出,0x73标志着数据是Object类型。
    继续调试
    image

    发现然后又读了一个字节x72,对应变量TC_CLASSDESC,是类的描述:
    image

    然后读出类名和suid(对应serialVersionUID)。

    后面有继续读出了classDescFlags、Fields等信息,不一一调试了。
    对应数据如图:
    image

    这一步读出了类的所有信息,储存到ObjectStreamClass类型对象desc中,

    接下来desc又newInstance(),创建了一个对象obj。
    image

    这个newInstance()是不是很熟悉?
    在学反射时知道,Class类、Constructor类都有newInstance()方法。
    看一下ObjectStreamClass类的newInstance()方法,
    发现,newInstance调用的是类成员变量cons的newInstance方法。

    private Constructor<?> cons;
    
    

    所以在这一步,通过调用Object类的无参构造函数,创建了obj对象,这个对象有Person类的所有属性

    继续调试,调用了方法。

    readSerialData(obj, desc);
    
    

    看起来是将读出的序列化数据赋值给对象,
    然后继续调试,跟了很久。
    image

    putObject又是个native方法,将键值赋值给obj对象,
    这也就能解释的通,为什么在反序列化时,java不会发出警告。
    因为如果通过反射修改private成员函数值,java会发出警告,
    正常反射修改private变量会发出警告,如图:
    image

    可以利用下吗?
    执行下面代码试下:

    Unsafe unsafe = Unsafe.getUnsafe();
    Person p = new Person("z3",12);
    unsafe.putObject(p, 16, "z5"); // 至于第二个参数为什么是16,不知道,调试时发现java就这么调的,可能16是"name"的一个标志?
    System.out.println(p.getName());
    
    

    报错
    image

    好吧,先不管它了,估计是用不了。

    忘了说,调试时发现readSerialData方法,它会递归的反序列化当前对象的所有成员变量。

    所以,总结,java在反序列化时,是递归的,将对象反序列化,对象的成员变量反序列化,对象的成员变量的成员变量反序列化。
    一层层递归调用,直到基本变量,然后一层层再出来,完成最外层对象的反序列化,整个过程是由内向外的。
    例如对象A里包含对象B,对象B包含对象C,C包含一个数字1,
    将A序列化后,再反序列化的过程是,先打开A,发现A里有B,则再打开B,发现B包含C,再打开C,发现C里有1,则读取出1,完成C的反序列化,再把这个C给B,完成B的反序列化,再把B给A,完成A的反序列化。
    这个过程特别像把对象A一层层扒开,直到遇到基本变量,然后开始由内向外反序列化,

    现在知道反序列化过程了,读出序列化的数据,创建一个空的对象,再调用native方法,为对象赋值。

    java反序列化利用(URLDNS)

    现在了解了序列化与反序列过程。
    怎么利用呢?

    序列化是将对象里的成员变量序列化,进行传输,另一端收到序列化数据后,反序列化得到对象,

    在这个过程中,反序列化的数据有可能会被拦截,修改,

    所以就是说,反序列化数据可控,也就是对象的的成员变量可控。
    在之前的文章中提到过,对象的方法执行过程是有可能成员变量影响的,而成员变量可控,那就有希望通过构造成员变量,控制方法的执行。

    怎么通过控制对象的成员变量影响方法的执行呢?
    首先,我们可控的只有变量的值,所以,想执行代码或命令,只能靠java自带的类方法,则需要寻找java可以被利用的类。

    看下面这个代码
    image

    先调用genURLDNS函数,生成序列化对象,保存到文件out.bin中,

    之后,每调用一次getURLDNS函数,都会从文件读取序列化数据,然后反序列化,然后导致本机发出一条dns请求。
    image

    这个out.bin就是构造好的恶意序列化数据,也可以叫做payload,作用是可以控制机器向指定地址发出一条dns请求,

    那就调试下反序列化的过程,看看什么原因造成的。
    readObject之前已经调试过了,但是忽略了一个点,
    在readSerialData方法中调用了invokeReadObject,而在invokeReadObject函数中,判断了当前类是否有readObject方法,如果有,则通过反射的方式调用该类的readObject方法(例如反序列化A类时,如果A类有readObject方法,那就按A类自带的readObject方法反序列化,如果没有,那就用默认的方式反序列化)。

    而问题的产生,就在HashMap的readObject方法中。
    这里调用了hash(key)
    image

    调用了key的hashCode方法
    image

    而key是URL对象,
    看下URL对象的hashCode方法,首先判断hashCode,不为-1,则调用handler.hashCode。
    image

    继续向下跟
    image

    而getHostAddress过程中发现这一步骤
    image

    正是这句代码,导致了dns查询,

    所以总结下,反序列化导致dns查询的过程,
    ObjectInputStream的readObject方法 =》 HashMap的readObject方法 => 创建HashMap对象,并putVal(hash(key)) key是URL对象 =》 URL的hashCode方法 =》 URLStreamHandler的hashCode方法 =》URLStreamHandler的getHostAddress方法 =》 InetAddress.getByName方法。

    这个就是造成dns查询的利用链,也叫做Gadget,

    再回头看下代码,开始图片中标注的“这里先不看”。
    image

    看注释说明,是为了防止造成两次dns查询,
    现在明白了URLDNS的利用链,分析下原因。
    因为hashMap.put一个url对象时会调用,
    putVal(hash(key))代码,而上面总结过hash(URL对象)会造成dns查询。

    f是hashCode方法的对象,把url对象的hashCode的值改为非-1,然后put(url),再把hashCode的值改回-1
    回想一下“URL的hashCode方法”中,hashCode不为-1时,直接return,就不会有后面的dns查询了,利用链到“URL的hashCode方法”就断掉了。
    image

    image

    而hashCode默认值就是-1,
    所以通过这种方式,就可以避免在构造恶意序列化对象时,触发dns请求。

    总结

    造成反序列化漏洞的根本原因分析

    序列化的内容只有类名成员变量,所以可控点是,类名和成员变量的值。
    通过控制类名、可以指定反序列化的类,通过控制变量的值,就可以影响代码执行流程。然后按照我们的期望,将这些“影响”连接在一起,就可以控制代码的执行。
    例如本例中,
    影响1:URL对象赋值了一个http://开头的url地址,导致它只要调用hashCode就会查询dns(如果不赋值一个http://开头的url地址,就不会查询)。
    影响2:为HashMap赋值了一个URL对象,导致它在反序列化时,会计算URL对象的hash(如果不赋值,就不会计算hash)。
    上面dns查询,计算hash,这两个动作,都是因为两个变量的值导致的,所以:控制变量的值,会影响代码的执行流程。

    当一个影响产生了“动作”,就可以触发下一个影响,再触发下一个影响,最后达到预期效果:命令执行,而这些能搭配使用且能达到我们预期效果的“影响”们,就是利用链。
    而这个“动作”来源,只能是类的readObject方法,因为默认的反序列化过程,只是简单的赋值,,变量的值不会对代码的执行造成任何影响,也就不会有利用链。
    类自定义的readObject方法,初衷是为了反序列化出完整的对象(有些类很复杂,简单的赋值不能复原对象)。
    但是这个readObject方法会受变量值得影响,产生“动作”,触发下一个“影响”。

    这就像,每个类都是一个形状不确定的零件,当这个类被实例化后,对象的变量值确定了,这个零件的形状就固定了,不同的变量值,会导致零件形状不同。
    而我们要做的,就是从java自带的这些不确定的零件中找出,固定形状后可以像多米诺骨牌一样,后一个可以推倒前一个的这种零件。
    然后将这些零件按多米诺骨牌一样排列,最后一个零件倒下,就会按动致命令执行按钮,
    只要第一个零件被推倒,就会引发后面一系列连锁反应,导致命令执行。
    而推倒第一个零件的,就是readObject方法,因为它是唯一一个,在反序列化时,受变量值影响代码执行过程的函数,所以推倒第一个零件的动作,由它发出。

    展开全文
  • C++ 序列化和反序列化

    千次阅读 2020-07-07 15:04:16
    序列化序列化1、背景2、定义3、序列化评价指标4、序列化实例参考 序列化 1、背景 1、在TCP的连接上,它传输数据的基本形式就是二进制流,也就是一段一段的1和0。 2、在一般编程语言或者网络框架提供的API中,...
  • Java序列化和反序列化(详解)

    千次阅读 2022-07-20 10:13:34
    java序列化及可序列化讲解,代码清晰易懂。
  • 遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化 b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化 c,serialVersionUID 这个的值到底是在怎么设置的...
  • 1. 什么是序列化与反序列化,什么情况需要序列化1.1 序列化序列化是什么序列化的目的什么情况需要序列化1.2 反序列反序列化是什么反序列化的目的2. Java中的序列化与反序列化2.1 如何实现序列化Java序列化的规定序列...
  • .Net序列化与反序列化

    千次阅读 2020-03-30 17:38:46
    序列化,又称为串行化,是.NET运行时环境用来支持用户定义类型的流行的机制。序列化就是把一个对象保存到一个文件或数据库字段中去,反序列化就是在适当的时候把这个文件再转化成原来的对象使用。其目的是以某种存储...
  • SpringBoot的序列化和反序列化

    万次阅读 多人点赞 2020-11-11 15:06:16
    序列化与反序列化 1、认识序列化与反序列化 Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。 2、为什么要实现对象的序列化和反序列化? (1)我们创建的Java...
  • Java 序列化与反序列化

    千次阅读 2020-09-05 13:04:20
    介绍 Java 的序列化与反序列化
  • ProtoBuf的序列化和反序列化(基础库)

    千次阅读 2021-05-12 22:47:49
    ProtoBuf的序列化和反序列化 ProtoBuf的优势 前后端使用ProtoBuf交互 ProtoBuf简介: Google 的 ProtoBuf ==> 将文件转成二进制文件 protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法...
  • 序列化与反序列化漏洞详解

    千次阅读 2022-03-13 18:02:32
    文章目录小知识漏洞原理序列化序列化函数解析序列化:serialize()函数反序列化 :unserialize()函数魔术方法pikachu靶场练习1靶场练习2总结 小知识 weblogic反序列化漏洞 该漏洞挖掘较难。 与变量覆盖一样,并不...
  • 介绍二进制序列化器、XML序列化器、Json序列化器,其中Json序列化器分别介绍了.Net 标准库 `System.Text.Json`、`System.Runtime.Serialization.Json` 和 Newtonsoft 库`Newtonsoft.Json`
  • C#中Json序列化和反序列化总结

    千次阅读 2021-09-16 16:52:42
    Json序列化和反序列化总结 JSON介绍1.什么是JSON?2.JSON基于两种结构:3.几种表现形式:4.序列化和反序列化的4中方式:JavaScriptSerializer类1.定义2.示例 JSON介绍 1.什么是JSON? JSON(JavaScript Object Notation...
  • Json序列化技术

    千次阅读 2022-01-26 10:50:02
    从比特理解Json序列化方式 关于Json 可以说是web 开发不管是前端还是后端都是非常的熟悉,第一次接触Json,是在我大一的寒假,因为人生中第一个servlet原生项目中要使用所以进行了较为粗浅的研究: 当时乃至之后的...
  • Python--序列化和反序列化

    千次阅读 2021-11-19 22:08:00
    设计一套协议,按照某种规则,把内存中的数据转换为字节序列,保存到文件,这就是序列化,反之,从文件的字节序列恢复到内存中,就是反序列化序列化: 将对象转换为字节序列。反序列化:将字节序列转换为对象。...
  • 1.序列化与反序列化的概念 2.子类实现Serializable接口,父类没有实现,子类可以序列化吗? 3.类中存在引用对象,这个类对象在什么情况下可以实现序列化? 4.同一个对象多次序列化之间有属性更新,前后的序列化有...
  • 序列化和反序列化的详解

    千次阅读 2021-04-01 00:28:40
    1、序列化和反序列化的定义: (1)Java序列化就是指把Java对象转换为字节序列的过程 Java反序列化就是指把字节序列恢复为Java对象的过程。 (2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性...
  • 序列化和反序列化,Properties工具类

    千次阅读 2022-02-11 23:26:24
    一、序列化和反序列化 对象实现序列化后,可以进行网络传输或存储磁盘的功能。本质是,序列化就是将对象变成byte[]形式;反序列化 就是将byte[] 变成对象。 对象要实现序列化,类一定要实现Serializable接口 ...
  • Java 序列化详解

    千次阅读 2022-04-20 17:38:57
    序列化和反序列化相关概念 什么是序列化?什么是反序列化? 如果我们需要持久化Java对象比如将Java对象保存在文件中,或者在网络传输Java对象,这些场景都需要用到序列化。 简单来说: 序列化: 将数据结构或对象转换...
  • 序列化和反序列化的底层实现原理是什么?

    万次阅读 多人点赞 2018-04-07 13:53:41
    序列化和反序列化作为Java里一个较为基础的知识点,大家心里也有那么几句要说的,但我相信很多小伙伴掌握的也就是那么几句而已,如果再深究问一下Java如何实现序列化和反序列化的,就可能不知所措了!遥记当年也被问...
  • java序列化与反序列化全讲解

    千次阅读 多人点赞 2020-07-27 21:30:05
    序列化与反序列化 Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。 序列化分为两大部分:序列化和反序列化序列化是这个过程的第一部分,将数据分解成字节流...
  • go语言序列化及反序列化

    千次阅读 2020-06-19 22:56:45
    参考文章: ...特别好的文章:理解 Go 中的 JSON ...一、序列化 GO提供了 Marshal 方法:Go Struct转换为JSON对象,函数签名: func Marshal(v interface{}) ([]byte, error) 举例: type Person struct { Name .
  • 序列化和反序列化的区别

    千次阅读 2022-03-15 10:19:52
    首先我们来了解为什么需要序列化 卖个关子,大家玩游戏的时候,相信都知道【存档】的功能吧,每次我们重新进入游戏的时候,直接载入存档就可以了,不用重新在重头开始,这样我们的游戏也不会丢失 从面向对象的思维...
  • 序列化

    千次阅读 2019-06-09 20:16:27
    序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。 序列化分为两大部分:序列化和反序列化序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列...
  • 遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化 b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化 c,serialVersionUID 这个的值到底是在怎么设置的...
  • 序列化和反序列化,看这一篇就够了!

    千次阅读 多人点赞 2020-08-16 03:38:00
    Java 序列化的一些简单总结 Java序列化只是针对对象的状态进行保存,至于对象中的方法,序列化不关心 当一个父类实现了序列化,那么子类会自动实现序列化,不需要显示实现序列化接口 当一个对象的实例变量引用了...
  • 序列化&反序列化

    万次阅读 2021-03-22 22:02:12
    对象序列化 1、对象序列化概述 序列化是一种对象的传输手段,Java有自己的序列化管理机制。对象序列化,就是把一个对象变为二进制的数据流的一种方法,通过对象的序列化可以方便地实现对象的传输或存储,如图 1-1所...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,378,896
精华内容 551,558
关键字:

序列化