精华内容
下载资源
问答
  • java序列化
    千次阅读
    2020-09-05 13:04:20

    Java 序列化与反序列化


    1 序列化与反序列化的概念

    • Java 序列化是指:将对象转化成一个字节序列(二进制数据)的过程。

    • 将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化。

    • Java 反序列化是指:将一个对象的字节序列恢复成 Java 对象的过程。

    • 一个平台中序列化的对象,可以在另一个平台中进行反序列化,因为这个过程是在 JVM 中独立完成的,可以依赖于 Java 的可移植性。


    2 核心类与关键字总览

    • ObjectOutputStream:IO 类,包含序列化对象的方法,writeObject()

    • ObjectInputStream:IO 类,包含反序列化对象的方法,readObject()

    • 上面两个 IO 流类是高层次的数据库,需要借助文件流进行序列化与反序列化操作。

    • Serializable ,接口,是一个标志性接口,标识可以在 JVM 中进行序列化,JVM 会为该类自动生成一个序列化版本号。参与序列化与反序列化的类必须实现 Serializable 接口。

    • serialVersionUID,类属性,序列化版本号,用于给 JVM 区别同名类,没有提供版本号,JVM会默认提供序列化版本号。

    • transient,关键字,当序列化时,不希望某些属性参与,则可以使用这个关键字标注该属性。


    3 序列化与反序列化的过程

    • 内存中的数据信息被拆分成一小块一小块的部分,为每个小块设置编号,然后存放到硬盘文件中,也就是将 Java 对象对象的状态保存下来存储到文件中的过程就叫做序列化。

    • 将硬盘中保存了 Java 对象状态的字节序列按照编号组装成对象恢复到内存中,这个过程称为反序列化。


    3 应用示例

    参与序列化和反序列化的 Java 类

    public class Student implements Serializable {
        private String name;
        private int age;
        //  以下省略有参构造、无参构造、set、get、toString
    }
    
    • 参与序列化和反序列化的类必须实现 Serializable 接口。

    序列化操作

    public static void main(String[] args) throws Exception {
        //  创建 Java 对象
        Student student = new Student("张三",22);
        //  对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student"));
        // 使用 writeObject 序列化对象
        oos.writeObject(student);
        // 刷新
        oos.flush();
        //  关闭流
        oos.close();
    }
    
    • 序列化后的二进制文件会被保存到文件输出流指定的路径。

    反序列化操作

    public static void main(String[] args) throws Exception {
        //  对象输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"));
        //  使用 readObject() 反序列化  
        Object obj = ois.readObject();
        //  使用对象
        System.out.println(obj);
        //  关闭流
        ois.close();
    }
    
    • 反序列化需要借助文件输入流读取指定路径的二进制文件。

    4 序列化版本号的作用 serialVersionUID

    • JVM 首先会通过类名来区分 Java 类,类名不同,则不是同一个类。当类名相同时,JVM 就会通过序列化版本号来区分 Java 类,如果序列化版本号相同就为同一个类,序列化版本号不同就为不同的类。

    • 在序列化一个对象时,如果没有指定序列化版本号,后期对该类的源码进行修改并重新编译后,会导致修改前后的序列化版本号不一致,因为 JVM 会提供一个新的序列化版本号给该类对象。

    • 此时再用以往的反序列化代码去反序列化该类的对象,就会抛出异常 java.io.InvalidClassException ,所以序列化一个类时最好指定一个序列化版本号,或者永远不修改此类。

    public class Student implements Serializable {
        private static final Long serialVersionUID = 1L;
    }
    
    • 由 JVM 提供序列化版本号的好处是,同名却不同功能的类,会有两个不同的序列化版本号,JVM 可以通过序列化版本号加以区分,缺点是一旦修改源码,会重新提供序列化版本号,导致修改前后的序列化版本号不一致,进行反序列化时会出现运行出现异常。

    • 由 开发人员 手动提供序列化版本号的好处是,当修改了被序列化类的源码后,以往写的反序列化代码依然可以使用,如 JDK 中的 String 类。以便后期进行增强和维护不会影响使用。

    在这里插入图片描述


    5 transient 关键字

    • 这个关键字表示游离的,不参与序列化的。

    • 在序列化一个对象时,如果不希望某个属性参加序列化,可以使用 transient 修饰该属性。

    • 被该关键字修饰的属性不会参与到序列化中。

    public class Student implements Serializable {
    
        private static final Long serialVersionUID = 1L;
    
        private String name;
        private transient int age;
    }
    
    • 如上类,在序列化时就不会保存 age 属性,在反序列化时就不能会付出该属性,默认恢复成 null 或 0 ,由属性类型决定。

    6 序列化的好处及应用场景

    • 序列化会将内存中对象的状态转换成二进制文件保存到磁盘当中,当再次使用时会从磁盘中读取该二进制文件,将 Java 对象的状态恢复到内存中。

    • 当你想把内存中的对象保存到磁盘文件或数据库中时可以使用序列化。

    • 当你想在网络传输中传送 Java 对象时,可以使用序列化。

    • 当你想通过 RMI 传输对象时,可以使用序列化。


    7 序列化注意事项

    • 序列化只会保存对象的属性状态,不会保存对象中的方法。

    • 父类实现了 Serializable 接口,则其子类也自动实例化了该接口,也就是说子类不用显式实现 Serializable 接口也能参与序列化和反序列化。

    • 一个对象 A 的实例变量引用了其他对象 B,在 A 对象实例化的过程中 ,也会序列化 B ,前提是 A、B 两个类都实现了 Serializable 接口。

    • 当一个类实现 Serializable 接口时,最好手动指定一个序列化版本号(serialVersionUID),避免修改源代码后导致反序列化出现异常。

    • 当一个类对象会被多次重复使用,且一般不会对其属性做修改,就可以对其进行序列化。例如数据库操作中的实体类。


    参考博文:

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

    千次阅读 2021-07-21 08:53:59
    遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题 a,什么叫序列化和反序列化 b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化 c,serialVersionUID 这个的值到底是在怎么设置的...

    遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题
    a,什么叫序列化和反序列化
    b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化
    c,serialVersionUID 这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字,迷惑ing。
    我刚刚见到这个关键字 Serializable 的时候,就有如上的这么些问题。

    在处理这个问题之前,你要先知道一个问题,这个比较重要。
    这个Serializable接口,以及相关的东西,全部都在 Java io 里面的。
     

    1,序列化和反序列化的概念
    序列化:把对象转换为字节序列的过程称为对象的序列化。
    反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

    上面是专业的解释,现在来点通俗的解释。在代码运行的时候,我们可以看到很多的对象(debug过的都造吧),
    可以是一个,也可以是一类对象的集合,很多的对象数据,这些数据中,
    有些信息我们想让他持久的保存起来,那么这个就叫序列化。
    就是把内存里面的这些对象给变成一连串的字节(bytes)描述的过程。
    常见的就是变成文件
    我不序列化也可以保存文件啥的呀,有什么影响呢?我也是这么问的。

    2,什么情况下需要序列化 
    当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
    当你想用套接字在网络上传送对象的时候;
    当你想通过RMI传输对象的时候;

    (老实说,上面的几种,我可能就用过个存数据库的。)

    3,java如何实现序列化
    实现Serializable接口即可

    上面这些理论都比较简单,下面实际代码看看这个序列化到底能干啥,以及会产生的bug问题。

    先上对象代码,飞猪.java

    package com.lxk.model;
     
    import java.io.Serializable;
     
    /**
     * @author lxk on 2017/11/1
     */
    public class FlyPig implements Serializable {
        //private static final long serialVersionUID = 1L;
        private static String AGE = "269";
        private String name;
        private String color;
        transient private String car;
     
        //private String addTip;
     
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
     
        public String getColor() {
            return color;
        }
     
        public void setColor(String color) {
            this.color = color;
        }
     
        public String getCar() {
            return car;
        }
     
        public void setCar(String car) {
            this.car = car;
        }
     
        //public String getAddTip() {
        //    return addTip;
        //}
        //
        //public void setAddTip(String addTip) {
        //    this.addTip = addTip;
        //}
     
        @Override
        public String toString() {
            return "FlyPig{" +
                    "name='" + name + '\'' +
                    ", color='" + color + '\'' +
                    ", car='" + car + '\'' +
                    ", AGE='" + AGE + '\'' +
                    //", addTip='" + addTip + '\'' +
                    '}';
        }
    }


    注意下,注释的代码,是一会儿要各种情况下使用的。

    下面就是main方法啦

    package com.lxk.test;
     
    import com.lxk.model.FlyPig;
     
    import java.io.*;
     
    /**
     * 序列化测试
     *
     * @author lxk on 2017/11/1
     */
    public class SerializableTest {
        public static void main(String[] args) throws Exception {
            serializeFlyPig();
            FlyPig flyPig = deserializeFlyPig();
            System.out.println(flyPig.toString());
     
        }
     
        /**
         * 序列化
         */
        private static void serializeFlyPig() throws IOException {
            FlyPig flyPig = new FlyPig();
            flyPig.setColor("black");
            flyPig.setName("naruto");
            flyPig.setCar("0000");
            // ObjectOutputStream 对象输出流,将 flyPig 对象存储到E盘的 flyPig.txt 文件中,完成对 flyPig 对象的序列化操作
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/flyPig.txt")));
            oos.writeObject(flyPig);
            System.out.println("FlyPig 对象序列化成功!");
            oos.close();
        }
     
        /**
         * 反序列化
         */
        private static FlyPig deserializeFlyPig() throws Exception {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/flyPig.txt")));
            FlyPig person = (FlyPig) ois.readObject();
            System.out.println("FlyPig 对象反序列化成功!");
            return person;
        }
    }


    对上面的2个操作文件流的类的简单说明

    ObjectOutputStream代表对象输出流:

    它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

    ObjectInputStream代表对象输入流:

    它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

    具体怎么看运行情况。

    第一种:上来就这些代码,不动,直接run,看效果。

    实际运行结果,他会在 d:/flyPig.txt 生成个文件。


    从运行结果上看:

     

    1,他实现了对象的序列化和反序列化。

    2,transient 修饰的属性,是不会被序列化的。我设置的奥迪四个圈的车不见啦,成了null。my god。

    3,你先别着急说,这个静态变量AGE也被序列化啦。这个得另测。

    第二种:为了验证这个静态的属性能不能被序列化和反序列化,可如下操作。

    public static void main(String[] args) throws Exception {
            serializeFlyPig();
            //FlyPig flyPig = deserializeFlyPig();
            //System.out.println(flyPig.toString());
        }


    这个完了之后,意思也就是说,你先序列化个对象到文件了。这个对象是带静态变量的static。

    现在修改flyPig类里面的AGE的值,给改成26吧。

    然后,看下图里面的运行代码和执行结果。

     

    可以看到,刚刚序列化的269,没有读出来。而是刚刚修改的26,如果可以的话,应该是覆盖这个26,是269才对。

    所以,得出结论,这个静态static的属性,他不序列化。

    第三种:示范这个 serialVersionUID 的作用和用法

    最暴力的改法,直接把model的类实现的这个接口去掉。然后执行后面的序列化和反序列化的方法。直接报错。

    抛异常:NotSerializableException

     这个太暴力啦,不推荐这么干。

    然后就是,还和上面的操作差不多,先是单独执行序列化方法。生成文件。
    然后,打开属性 addTip ,这之后,再次执行反序列化方法,看现象。

     抛异常:InvalidClassException  详情如下。

    InvalidClassException: com.lxk.model.FlyPig; 
    local class incompatible: 
    stream classdesc serialVersionUID = -3983502914954951240, 
    local class serialVersionUID = 7565838717623951575

    解释一下:

    因为我再model里面是没有明确的给这个 serialVersionUID 赋值,但是,Java会自动的给我赋值的,

    这个值跟这个model的属性相关计算出来的。

    我保存的时候,也就是我序列化的时候,那时候还没有这个addTip属性呢,

    所以,自动生成的serialVersionUID 这个值,

    在我反序列化的时候Java自动生成的这个serialVersionUID值是不同的,他就抛异常啦。

    (你还可以反过来,带ID去序列化,然后,没ID去反序列化。也是同样的问题。)

    再来一次,就是先序列化,这个时候,把 private static final long serialVersionUID = 1L; 这行代码的注释打开。那个addTip属性先注释掉,序列化之后,再把这个属性打开,再反序列化。看看什么情况。

    这个时候,代码执行OK,一切正常。good。序列化的时候,是没的那个属性的,在发序列化的时候,对应的model多了个属性,但是,反序列化执行OK,没出异常。

    这个现象对我们有什么意义:

    老铁,这个意义比较大,首先,你要是不知道这个序列化是干啥的,万一他真的如开头所讲的那样存数据库(这个存db是否涉及到Java的序列化估计还的看什么数据库吧)啦,socket传输啦,rmi传输啦。虽然我也不知道这是干啥的。你就给model bean 实现了个这个接口,你没写这个 serialVersionUID 那么在后来扩展的时候,可能就会出现不认识旧数据的bug,那不就炸啦吗。回忆一下上面的这个出错情况。想想都可怕,这个锅谁来背? 

    所以,有这么个理论,就是在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值,就是这么个问题。

    这也就解释了,我们刚刚开始编码的时候,实现了这个接口之后,为啥eclipse编辑器要黄色警告,需要添加个这个ID的值。而且还是一长串你都不知道怎么来的数字。

    下面解释这个 serialVersionUID 的值到底怎么设置才OK。

    首先,你可以不用自己去赋值,Java会给你赋值,但是,这个就会出现上面的bug,很不安全,所以,还得自己手动的来。

    那么,我该怎么赋值,eclipse可能会自动给你赋值个一长串数字。这个是没必要的。

    可以简单的赋值个 1L,这就可以啦。。这样可以确保代码一致时反序列化成功。

    不同的serialVersionUID的值,会影响到反序列化,也就是数据的读取,你写1L,注意L大些。计算机是不区分大小写的,但是,作为观众的我们,是要区分1和L的l,所以说,这个值,闲的没事不要乱动,不然一个版本升级,旧数据就不兼容了,你还不知道问题在哪。。。

    下面是摘自 jdk api 文档里面关于接口 Serializable 的描述
    类通过实现 java.io.Serializable 接口以启用其序列化功能。
    未实现此接口的类将无法使其任何状态序列化或反序列化。
    可序列化类的所有子类型本身都是可序列化的。因为实现接口也是间接的等同于继承。
    序列化接口没有方法或字段,仅用于标识可序列化的语义。

    关于 serialVersionUID 的描述

    https://blog.csdn.net/qq_27093465?viewmode=contents

    (注意对比一下,这个截图的两段话,就是对应下面的2段中文。仔细看这2段话,静态属性不会被序列化,但是却又有一个特殊的静态属性,会被序列化,没办法,这个静态属性是亲生的。自带的。)

    序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:

    如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

    最后更新一下
    1,(问题:序列化的类的所有成员变量是不是都要是基本类型或实现Serializable接口的类型?)

    当属性是对象的时候,如果这个对象,没实现序列化接口,那么上面的方法在序列化的时候就在执行oos.writeObject(flyPig)时候,报错了“Exception in thread "main" java.io.NotSerializableException: com.lxk.model.Bird”。然后给刚刚的属性的对象加上实现序列化的接口之后,上面的测试就正常通过了。你这个问题问的好。

    结论:要实现序列化的对象,所有涉及的引用,都需要实现序列化接口才可以。

    2,(问题:这个serialVersionUID的值在存数据库的时候,存哪里了?)

    大师兄

     大师兄

     

    好问题,答不上来呀!!!我也是郁闷了,那大概猜一下,数据库没有使用Java这一套序列化,而是不同db各自实现了一套自己的序列化,so,就跟Java的这个静态属性没关系啦。静态属性是不存db的。然后,你就发现,你即使没实现这个Java的序列化的接口,也可以正常的存db,取db。太机智了,这个解释,满分,为啥呢,这个serialVersionUID是Java给你提供的一个序列化和反序列化用的,你要是不使Java这一套的话,那就不需要考虑这个问题啦呀。在上面的测试代码里面也是使用这些个API去序列化以及反序列化的。???

    3,(问题:既然要比较新旧serialVersionUID, 旧的serialVersionUID是不是也应该序列化到文件中, 但serialVersionUID是static类型的, 是不会被序列化到文件中的)

    这个serialVersionUID是jdk亲生的,你写或者不写,只要实现了接口,他就是存在的,在序列化的时候,他还真就序列化,存起来了。不然在反序列化的时候,就没的对比了嘛,因为他是亲生的,所以,咱手动添加的静态,和这个静态是不能比的。

    我写完文章,给自己点个赞,不过分吧,
    不过分,那我可就点啦啊。
    我先点为敬,你们随意。大家随意。不要客气。。。

    展开全文
  • Java 序列化详解

    千次阅读 2022-04-20 17:38:57
    如果我们需要持久化Java对象比如将Java对象保存在文件中,或者在网络传输Java对象,这些场景都需要用到序列化。 简单来说: 序列化: 将数据结构或对象转换成二进制字节流的过程 反序列化:将在序列化过程中所生成...

    序列化和反序列化相关概念

    什么是序列化?什么是反序列化?

    如果我们需要持久化Java对象比如将Java对象保存在文件中,或者在网络传输Java对象,这些场景都需要用到序列化。

    简单来说:

    • 序列化: 将数据结构或对象转换成二进制字节流的过程
    • 反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程

    对于Java这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而class 对应的是对象类型。

    维基百科是如是介绍序列化的:

    序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。

    综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

    [图片上传失败...(image-ab4d8f-1650447513456)]

    实际开发中有哪些用到序列化和反序列化的场景?

    1. 对象在进行网络传输(比如远程方法调用RPC的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
    2. 将对象存储到文件中的时候需要进行序列化,将对象从文件中读取出来需要进行反序列化。
    3. 将对象存储到缓存数据库(如 Redis)时需要用到序列化,将对象从缓存数据库中读取出来需要反序列化。

    序列化协议对应于TCP/IP 4层模型的哪一层?

    我们知道网络通信的双方必须要采用和遵守相同的协议。TCP/IP 四层模型是下面这样的,序列化协议属于哪一层呢?

    1. 应用层
    2. 传输层
    3. 网络层
    4. 网络接口层

    [图片上传失败...(image-2ad094-1650447513456)]

    如上图所示,OSI七层协议模型中,表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据。这不就对应的是序列化和反序列化么?

    因为,OSI七层协议模型中的应用层、表示层和会话层对应的都是TCP/IP 四层模型中的应用层,所以序列化协议属于TCP/IP协议应用层的一部分。

    常见序列化协议对比

    JDK自带的序列化方式一般不会用 ,因为序列化效率低并且部分版本有安全漏洞。比较常用的序列化协议有 hessian、kyro、protostuff。

    下面提到的都是基于二进制的序列化协议,像 JSON 和 XML这种属于文本类序列化方式。虽然 JSON 和 XML可读性比较好,但是性能较差,一般不会选择。

    JDK自带的序列化方式

    JDK 自带的序列化,只需实现 java.io.Serializable接口即可。

    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Builder
    @ToString
    public class RpcRequest implements Serializable {
        private static final long serialVersionUID = 1905122041950251207L;
        private String requestId;
        private String interfaceName;
        private String methodName;
        private Object[] parameters;
        private Class<?>[] paramTypes;
        private RpcMessageTypeEnum rpcMessageTypeEnum;
    }
    
    

    序列化号 serialVersionUID 属于版本控制的作用。序列化的时候serialVersionUID也会被写入二级制序列,当反序列化时会检查serialVersionUID是否和当前类的serialVersionUID一致。如果serialVersionUID不一致则会抛出 InvalidClassException 异常。强烈推荐每个序列化类都手动指定其 serialVersionUID,如果不手动指定,那么编译器会动态生成默认的序列化号

    我们很少或者说几乎不会直接使用这个序列化方式,主要原因有两个:

    1. 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
    2. 性能差 :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。

    Kryo

    Kryo是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积。

    另外,Kryo 已经是一种非常成熟的序列化实现了,已经在Twitter、Groupon、Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。

    guide-rpc-framework

    就是使用的 kyro 进行序列化,序列化和反序列化相关的代码如下:

    /**
     * Kryo serialization class, Kryo serialization efficiency is very high, but only compatible with Java language
     */
    @Slf4j
    public class KryoSerializer implements Serializer {
    
        /**
         * Because Kryo is not thread safe. So, use ThreadLocal to store Kryo objects
         */
        private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
            Kryo kryo = new Kryo();
            kryo.register(RpcResponse.class);
            kryo.register(RpcRequest.class);
            return kryo;
        });
    
        @Override
        public byte[] serialize(Object obj) {
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                 Output output = new Output(byteArrayOutputStream)) {
                Kryo kryo = kryoThreadLocal.get();
                // Object->byte:将对象序列化为byte数组
                kryo.writeObject(output, obj);
                kryoThreadLocal.remove();
                return output.toBytes();
            } catch (Exception e) {
                throw new SerializeException("Serialization failed");
            }
        }
    
        @Override
        public <T> T deserialize(byte[] bytes, Class<T> clazz) {
            try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
                 Input input = new Input(byteArrayInputStream)) {
                Kryo kryo = kryoThreadLocal.get();
                // byte->Object:从byte数组中反序列化出对对象
                Object o = kryo.readObject(input, clazz);
                kryoThreadLocal.remove();
                return clazz.cast(o);
            } catch (Exception e) {
                throw new SerializeException("Deserialization failed");
            }
        }
    
    }
    
    

    Protobuf

    Protobuf出自于Google,性能还比较优秀,也支持多种语言,同时还是跨平台的。就是在使用中过于繁琐,因为你需要自己定义 IDL 文件和生成对应的序列化代码。这样虽然不然灵活,但是,另一方面导致protobuf没有序列化漏洞的风险。

    Protobuf包含序列化格式的定义、各种语言的库以及一个IDL编译器。正常情况下你需要定义proto文件,然后使用IDL编译器编译成你需要的语言

    一个简单的 proto 文件如下:

    // protobuf的版本
    syntax = "proto3"; 
    // SearchRequest会被编译成不同的编程语言的相应对象,比如Java中的class、Go中的struct
    message Person {
      //string类型字段
      string name = 1;
      // int 类型字段
      int32 age = 2;
    }
    
    

    ProtoStuff

    由于Protobuf的易用性,它的哥哥 Protostuff 诞生了。

    protostuff 基于Google protobuf,但是提供了更多的功能和更简易的用法。虽然更加易用,但是不代表 ProtoStuff 性能更差。

    hession

    hessian 是一个轻量级的,自定义描述的二进制RPC协议。hessian是一个比较老的序列化实现了,并且同样也是跨语言的。

    [图片上传失败…(image-16256e-1650447513453)]

    dubbo RPC默认启用的序列化方式是 hession2 ,但是,Dubbo对hessian2进行了修改,不过大体结构还是差不多。

    总结

    Kryo 是专门针对Java语言序列化方式并且性能非常好,如果你的应用是专门针对Java语言的话可以考虑使用,并且 Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。

    [图片上传失败...(image-8ac114-1650447513453)]

    像Protobuf、 ProtoStuff、hession这类都是跨语言的序列化方式,如果有跨语言需求的话可以考虑使用。

    除了我上面介绍到的序列化方式的话,还有像 Thrift,Avro 这些。

    在此我向大家推荐一个架构学习交流圈。交流学习微信:539413949(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

    展开全文
  • java序列化和反序列化,面试必备

    千次阅读 多人点赞 2020-04-26 17:04:19
    意义:序列化机制允许将实现序列化Java对象转换为字节序列,并将字节序列保存在磁盘中,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使地对象可以脱离程序的运行而独立存在。 使用场景:所有在网络上...

    最近阅读Serializable接口和Externalizable接口的源码,并结合了一些资料,对面试过程中与序列化相关的内容做了一些总结。
    一、序列化、反序列化、使用场景、意义。
    序列化:将对象写入IO流中;
    反序列化:从IO流中恢复对象
    意义:序列化机制允许将实现序列化的Java对象转换为字节序列,并将字节序列保存在磁盘中,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使地对象可以脱离程序的运行而独立存在。
    使用场景所有在网络上传输的对象都必须是可序列化的。如:RMI (远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错。所有必须保存到磁盘的java对象都必须是可序列化的程序创建的JavaBean最好都实现Serializable接口
    二、实现序列化的方式
    实现序列化有两种方式:实现Serializable接口或Externalizable接口,通常情况下,实现Serializable接口即可。两种接口的对比如下:

    实现Serializable接口:
    1) 系统自动存储必要的信息;
    2) Java内建支持,易于实现,只需要实现接口接口,不需要任何代码支持;
    3) 性能略差;
    
    实现Externalizable接口:
    1) 自己决定要序列化哪些属性;
    2) 必须实现该接口内的两个方法:
    void writeExternal(ObjectOutput out) throws IOException;
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    3) 性能略好;
    

    三、使用Serializable接口实现序列化。
    Serializable接口是一个标记接口,不用实现任何方法,一旦某个类实现了该方法,则该类的对象是可序列化的。
    1、通过以下步骤实现序列化:
    1)创建一个ObjectOutputStream输出流;
    2)调用OjectOutputSteam对象的writeObject ()输出可序列化对象。

    public class Person implements Serializable {
    	private String name;
    	private String age;
    
    	public Person() {
    		System.out.println("调用Person的无参构造函数");
    	}
    
    	public Person(String name, String age) {
    		this.name = name;
    		this.age = age;
    		System.out.println("调用Person的有参构造函数");
    	}
    
    	@Override
    	public String toString() {
    		// TODO 自动生成的方法存根
    		return "Person{'name' :" + name + ",'age' :" + age + "}";
    	}
    }
    
    public class WriteObject {
    	public static void main(String[] args) {
    		try {
    			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Person.txt"));
    			Person p = new Person("baby", "12");
    			oos.writeObject(p);
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    	}
    }
    

    输出的序列化文件如下:

    aced 0005 7372 0017 7365 7269 616c 697a
    6162 6c65 5465 7374 2e50 6572 736f 6e4e
    aff9 165f 38dd f602 0002 4c00 0361 6765
    7400 124c 6a61 7661 2f6c 616e 672f 5374
    7269 6e67 3b4c 0004 6e61 6d65 7100 7e00
    0178 7074 0002 3132 7400 0462 6162 79
    

    2、通过以下步骤实现反序列化:
    1)创建一个ObjectInputStream输入流;
    2)调用ObjectInputStream对象的readObject ()得到序列化对象。

    public class WriteObject {
    	public static void main(String[] args) {
    		try {
    			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Person.txt"));
    			Person p = (Person) ois.readObject();
    			System.out.println(p.toString());
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    	}
    }
    

    输出结果如下:

    Person{'name' :baby,'age' :12}
    

    通过输出结果,我们知道反序列化没有调用类的构造方法,而是由JVM自己生成对象。
    3、当类的成员是引用数据类型时
    若一个类的成员不是基本数据类型,也不是String类型的时候,则该成员必须是可序列化的,否则会导致该类无法完成序列化。如下例子所示:

    // 去掉Person类实现的序列化接口
    public class Teacher implements Serializable {
    	private String name;
    	private Person person;
    
    	public Teacher(String name, Person person) {
    		this.name = name;
    		this.person = person;
    	}
    
    	public static void main(String[] args) throws Exception {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Teacher.txt"));
    		Person p = new Person("baby", "16");
    		Teacher t = new Teacher("mom", p);
    		oos.writeObject(t);
    	}
    }
    

    执行时会抛出下面的异常,异常指出,因为Person类不可序列化,导致Teacher类无法完成序列化操作。
    在这里插入图片描述
    4、序列化过程中存在的问题。
    1)同一对象,会被序列化多次吗?
    依次将p、t1、t2、t1序列化到文件SerializableMore中。

    public class WriteMore {
    	public static void main(String[] args) throws Exception {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerializableMore.txt"));
    		Person p = new Person("baby", "16");
    		Teacher t1 = new Teacher("mom", p);
    		Teacher t2 = new Teacher("dad", p);
    
    		oos.writeObject(p);
    		oos.writeObject(t1);
    		oos.writeObject(t2);
    		oos.writeObject(t1);
    	}
    }
    

    接下来将反序列化文件SerializableMore。

    public class ReadMore {
    	public static void main(String[] args) throws Exception {
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("SerializableMore.txt"));
    		
    		// 注意:反序列化的顺序和序列化时的顺序一致
    		Person p = (Person) ois.readObject();
    		Teacher t1 = (Teacher) ois.readObject();
    		Teacher t2 = (Teacher) ois.readObject();
    		Teacher t3 = (Teacher) ois.readObject();
    
    		System.out.println("t1 == t2 ---------------------------->" + (t1 == t2));
    		System.out.println("t1.getPerson() == p ----------------->" + (t1.getPerson() == p));
    		System.out.println("t2.getPerson() == p ----------------->" + (t2.getPerson() == p));
    		System.out.println("t2 == t3 ---------------------------->" + (t2 == t3));
    		System.out.println("t1.getPerson() == t2.getPerson() ---->" + (t1.getPerson() == t2.getPerson()));
    	}
    }
    

    输出结果如下所示:

    t1 == t2 ---------------------------->false
    t1.getPerson() == p ----------------->true
    t2.getPerson() == p ----------------->true
    t2 == t3 ---------------------------->false
    t1.getPerson() == t2.getPerson() ---->true
    

    可以看到:针对同一对象进行多次序列化,Java并不会序列化多次,而是沿用第一次序列化获得的序列化编码
    2)由于Java序列化算法不会重复序列化同一个对象,只会记录已序列化对象的序列化编号。而当一个可变的对象中的内容发生改变时,此时进行序列化,却不会重新将此对象转换为字节序列,而是保存序列化编号。如下所示。

    public class WirteOnChange {
    	public static void main(String[] args) throws Exception {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("WriteOnchange.txt"));
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("WriteOnchange.txt"));
    
    		Person person = new Person("索隆", "20");
    		System.out.println("修改前:" + person.toString());
    		oos.writeObject(person);
    
    		person.setName("香吉士");
    		System.out.println("修改后:" + person.toString());
    		oos.writeObject(person);
    		
    		Person p1 = (Person) ois.readObject();
    		Person p2 = (Person) ois.readObject();
    		
    		System.out.println(p1 == p2);
    		System.out.println(p1.getName().equals(p2.getName()));
    	}
    }
    

    输出结果如下:

    修改前:Person{'name' :索隆,'age' :20}
    修改后:Person{'name' :香吉士,'age' :20}
    true
    true
    

    5、Java序列化算法

    1)所有保存到磁盘的对象都有一个序列化编号;
    2)当试图序列化一个对象时,会先检查该对象是否已经序列化过,只有该对象未被JVM序列化过,才会将该对象序列化为字节序列输出;
    3)如果此对象已经被序列化过,则直接输出序列化编码号即可。
    

    如下图所示:
    在这里插入图片描述
    6、可选的自定义序列化
    1)使用transient关键字指定不进行序列化的字段。
    使用transient修饰的属性,java序列化时会忽略该属性。而当反序列化时,被transient修饰的属性则赋予默认值对于引用类型则为nullboolean类型为false,基本类型为0

    public class Teacher implements Serializable {
    	private String name;
    	private transient String age;
    	private transient int height;
    	private Person person;
    	
    	public Teacher(String name, String age, int height, Person person) {
    		this.name = name;
    		this.age = age;
    		this.height = height;
    		this.person = person;
    	}
    	// ...省略getter、setter方法
    
    public class WirteOnChange {
    	public static void main(String[] args) throws Exception {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"));
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher.txt"));
    
    		Person person = new Person("索隆", "20");
    		Teacher teacher = new Teacher("鹰眼", "30", 190, person);
    		
    		System.out.println("序列化之前:" + teacher.toString());
    		oos.writeObject(teacher);
    
    		Teacher t1 = (Teacher) ois.readObject();
    
    		System.out.println("序列化之后:" + t1.toString());
    	}
    }
    

    输出结果如下所示:

    序列化之前:Teacher{"name" : "鹰眼"; "age" : "30"; "height" : 190; "person" : Person{'name' :索隆,'age' :20}
    序列化之后:Teacher{"name" : "鹰眼"; "age" : "null"; "height" : 0; "person" : Person{'name' :索隆,'age' :20}
    
    

    2)通过下面的方法可以实现自定义序列化,可以控制序列化的方式或对序列化数据进行编码加密等。

    private void writeObject(java.io.ObjectOutputStream out) throws IOException
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
    private void readObjectNoData() throws ObjectStreamException;
    

    通过重写writeObject()readObject()方法,可选择哪些属性要序列化。如果writeObject使用了某种规则进行序列化,则readObject要使用相反的规则进行反序列化,以便能正确反序列化对象。

    // 对字符串name进行反转加密
    public class Person implements Serializable {
    	private String name;
    	private String age;
    	private int height;
    	
    	// 省略构造函数和getter、setter方法
    
    	private void WriteObject(ObjectOutputStream oos) throws IOException {
    		oos.writeObject(new StringBuilder(this.name).reverse()); // 利用StringBuilder实现字符串反转
    		oos.writeInt(height);
    	}
    
    	private void ReadObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    		this.name = ((StringBuilder) ois.readObject()).reverse().toString();
    		this.height = ois.readInt();
    	}
    

    当序列化流不完整时,readObjectNoData()方法可以正确地初始化反序列化的对象。例如,使用不同类接收反序列化对象,或者序列化流被篡改,系统都会调用readObjectNoData()来初始化反序列化对象。
    3)彻底的自定义序列化
    以下两个方法会在序列化前或反序列化后自动调用,可以实现更加彻底的自定义序列化。

    ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
    ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
    

    writeReplace()方法:在序列化前,会先调用该方法,再调用writeObject方法。此方法可以使用任意对象代替目标序列化对象。

    public class Person implements Serializable {
    	private String name;
    	private String age;
    	
    	// 省略构造方法、getter和setter方法
    
    	private Object writeReplace() throws ObjectStreamException {
    		ArrayList<String> list = new ArrayList<>();
    		list.add(this.name);
    		list.add(this.age);
    		return list;
    	}
    	
    	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
    		ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"));
    
    		Person person = new Person("罗宾", "18");
    		oos.writeObject(person);
    
    		ArrayList<String> list = (ArrayList) ios.readObject();
    		System.out.println(list);
    	}
    

    输出结果如下:

    [罗宾, 18]
    

    readResolve()方法:替代反序列化输出的对象,反序列化出来的对象会被立即丢弃,此方法在readObject()后调用。

    public class Person implements Serializable {
    	private String name;
    	private String age;
    	private int height;
    	
    	// 省略构造方法、getter和setter方法
    	
    	private Object readResolve() throws ObjectStreamException {
    		return new Person("娜美", "23");
    	}
    	
    	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
    		ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"));
    
    		Person person = new Person("罗宾", "18");
    		oos.writeObject(person);
    
    		Person p1 = (Person) ios.readObject();
    		System.out.println(p1);
    	}
    

    输出结果如下:

    Person{'name' :娜美,'age' :23}
    

    四、使用Externalizable接口实现序列化。
    Externalizable接口不同于Serializable接口,该接口需要强制重写两个方法。

    public interface Externalizable extends java.io.Serializable {
        void writeExternal(ObjectOutput out) throws IOException;
        void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    }
    

    测试程序如下:

    public class PersonExternal implements Externalizable {
    	private String name;
    	private int age;
    
    	// 必须提供无参构造函数
    	public PersonExternal() {
    		System.out.println("调用无参构造方法!!");
    	}
    
    	public PersonExternal(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    
    	@Override
    	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    		// 将读取的字符串反转后赋值给name实例变量
    		this.name = ((StringBuilder) in.readObject()).reverse().toString();
    		System.out.println("将name按相同的规则反序列化输出:" + name);
    		this.age = in.readInt();
    	}
    
    	@Override
    	public void writeExternal(ObjectOutput out) throws IOException {
    		// 将name反转后写入二进制流
    		StringBuilder reverse = new StringBuilder(name).reverse();
    		System.out.println("将name反转并序列化写入二进制流:" + reverse.toString());
    		out.writeObject(reverse);
    		out.writeInt(age);
    	}
    
    	@Override
    	public String toString() {
    		// TODO 自动生成的方法存根
    		return "Person{'name' :" + name + ", 'age' :" + age + "}";
    	}
    
    	public static void main(String[] args) throws Exception {
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("PersonExternal.txt"));
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("PersonExternal.txt"));
    
    		System.out.println("序列化ing");
    		oos.writeObject(new PersonExternal("cindy", 23));
    
    		System.out.println("反序列化ing");
    		PersonExternal pe = (PersonExternal) ois.readObject();
    
    		System.out.println(pe.toString());
    	}
    
    }
    

    输出结果:

    序列化ing
    将name反转并序列化写入二进制流:ydarb
    反序列化ing
    调用无参构造方法!!
    将name按相同的规则反序列化输出:brady
    Person{'name' :brady, 'age' :23}
    

    可以看到的是,实现Externalizable接口必须提供public的无参构造器,因为在反序列化的时候需要通过反射创建对象。
    五、序列化版本号serialVersionUID 。
    介绍了那么多关于序列化的内容,我们知道,反序列必须要有class文件,但随着项目的升级,class文件也会随之升级。那么,序列化怎么保证升级前后的兼容性呢?
    Java序列化提供了一个serializableVersionUID的序列化版本号,只要版本号相同,即使更改了序列化属性,对象也可以被正确地反序列化回来。

    public class Person implements Serializable {
    	// 序列化版本号
    	private static final long serialVersionUID = 1227593270102525184L;
    	
    	private String name;
    	private String age;
    	private int height;
    

    但是,如果反序列化使用的class的版本号与序列化时使用的不一致,反序列化会报InvalidClassException异常。
    在这里插入图片描述序列化版本号可自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,这样随着class的升级,就无法正确反序列化;
    不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化。
    接下来列出几种序列化的情况:

    a) 只是修改了方法,反序列化不影响,则无需修改版本号
    b) 只是修改了静态变量,瞬态变量(transient修饰的变量),反序列化不受影响,无需修改版本号
    c) 修改了非瞬态变量,则可能导致反序列化失败。如果新类中实例变量的类型与序列化时类的类型不一致,则会反序列化失败,这时候需要更改serialVersionUID。如果只是新增了实例变量,则反序列化回来新增的是默认值;
    

    六、总结 。

    a) 对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。
    b) 所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口。
    c) 如果想让某个变量不被序列化,使用transient修饰。
    d) 序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
    e) 反序列化时必须有序列化对象的class文件。
    f) 当通过文件、网络来读取序列化后的对象时,必须按照实际写入的顺序读取。
    g) 单例类序列化,需要重写readResolve()方法;否则会破坏单例原则。
    i) 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
    j) 建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级。
    k) 数组不能显式地声明serialVersionUID,因为它们始终都有默认的计算值,但是对于数组类,无需匹配serialVersionUID。
    l) 可以通过序列化和反序列化的方式实现对象的深复制。
    
    展开全文
  • java序列化与反序列化全讲解

    千次阅读 多人点赞 2020-07-27 21:30:05
    Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。 序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中...
  • 1、序列化和反序列化是什么? 如果你看过某些类的源码或者公司的项目,有一些类是实现 Serializable 接口,同时还要显示指定 serialVersionUID 的值。 例如String类: public final class String implements java....
  • 需要一种方式:Java对象 —序列化–> 保存/传输 –反序列化–> Java对象 序列化(编码): 把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程 Java对象 –编码协议–> ...
  • 面试题 - Java序列化和反序列化

    千次阅读 2020-09-15 18:03:22
    Java 序列化的高级认识 引言 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用 ...
  • Java序列化与反序列化最全详解

    千次阅读 2021-09-15 10:40:22
    序列化就是将 java对象 转化为字节序列的过程。 序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。 为什么要把Java对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络...
  • (1)Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程; (2)**序列化:**对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。...
  • java序列化与json序列化

    千次阅读 2020-10-27 12:57:19
    java序列化 Java 序列化可以将一个对象表示为一个字节序列。 (该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型) 可以把这个序列化对象写入文件之后,然后也可以从文件中读取出来,...
  • 详解JAVA序列化

    千次阅读 2021-02-12 16:12:20
    现在开发过程中经常遇到多个进程多个服务间需要交互,或者不同语言的服务之间需要交互,这个时候,我们一般选择使用固定的协议,将数据传输过去,但是在很多语言,比如java等jvm语言中,传输的数据是特有的类对象,...
  • 什么是 java 序列化?什么情况下需要序列化

    万次阅读 多人点赞 2019-06-17 09:34:01
    什么是 java 序列化?什么情况下需要序列化序列化:将 Java 对象转换成字节流的过程。 反序列化:将字节流转换成 Java 对象的过程。 当Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java ...
  • 什么是Java序列化和反序列化?

    千次阅读 2020-11-21 11:11:46
    java对象序列化的意思就是将对象的状态转换成字节流,以后可以通过这些值在生成相同状态的对象,对象序列化就是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输,反序列化...
  • java序列化接口Serializable

    千次阅读 2021-01-29 23:43:37
    类的可序列化性通过实现(implements) java.io.Serializable可序列化接口。 没有实现这个接口的类不会将其任何状态序列化或反序列化。 可序列化类的所有子类型本身可序列化序列化接口没有方法或字段只用于识别可...
  • Java序列化和反序列化为什么要实现Serializable接口

    千次阅读 多人点赞 2019-08-27 11:49:37
    最近公司的在做服务化, 需要把所有model包里的类都实现...(1) 序列化和反序列化是什么? (2) 实现序列化和反序列化为什么要实现Serializable接口? (3) 实现Serializable接口就算了, 为什么还要显示指定serialVers...
  • Java序列化的几种方式

    千次阅读 2021-04-14 15:46:12
    序列化:将序列化的字节序列还原 优点:可以实现对象的”持久性”, 所谓持久性就是指对象的生命周期不取决于程序 原生序列化方式 序列化方式一: 实现Serializable接口(隐式序列化) 通过实现Serializable...
  • java序列化详解

    万次阅读 多人点赞 2018-08-13 15:45:31
    序列化:指堆内存中的java对象数据,通过某种方式把对存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转化成二进制的过程。 即将对象转化为二进制,用于...
  • java序列化之writeObject 和readObject

    千次阅读 2020-06-05 15:21:04
    java中,只有类实现了java.io.serializable接口,该类才能被序列化。 示例Demo1.java: package com.example.demo; import java.io.*; public class Demo1 { public static class Person implements S
  • Java 序列化的几种方式 和反序列化

    千次阅读 2019-04-16 17:19:04
    字节序列:序列化 字节序列–>对象 :反序列化 序列化的用途:把对象的字节序列保存在磁盘上,通常存放在一个文件中;在网络上传送对象的字节序列。 存储在物理磁盘上的:Web服务器中的Session对象。当有 10万...
  • 1:java序列化异常:Caused by: java.io.NotSerializableException: java.security.ProtectionDomain 在运行时突然报序列化异常 Exception in thread "main" java.lang.reflect.InvocationTargetException at sun....
  • Java序列化是什么?你知道什么时候需要序列化吗? 什么是 java 序列化?什么情况下需要序列化? 序列化:将 Java 对象转换成字节流的过程。 反序列化:将字节流转换成 Java 对象的过程。 当 Java 对象...
  • Java 序列化和反序列化(一)Serializable 使用场景 1. 最简单的使用:Serializable 接口 2. 序列化 ID 的问题 3. ...
  • 常见的java序列化/反序列化几种类型

    千次阅读 2019-01-11 01:02:34
    Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程 主要有两种用途: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中; 在网络上传送对象的字节序列。...
  • Kryo序列化与Java序列化

    千次阅读 2018-05-27 23:06:13
    将自定义的类型作为RDD的泛型类型时(比如JavaRDD,Student是自定义类型),所有自定义类型对象,都会进行序列化。因此这种情况下,也要求自定义的类必须实现Serializable接口。 使用可序列化的持久化策略时(比如...
  • java 序列化

    千次阅读 2016-12-02 16:46:38
    虽然你可以用你自己的各种各样的方法来保存 object states,但是java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。 2.什么情况下需要序列化 1. 当你想把内存中的对象状态保存到一个文件或者...
  • Java 序列化java开发面试笔试题

    千次阅读 2019-04-30 10:26:45
    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。 扫描二维码或搜索下图红色VX号...Java 序列化 简介 定义 序列化序列化是将对象转换为字节流。 反...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 584,440
精华内容 233,776
关键字:

java序列化

友情链接: pbovb.rar