精华内容
下载资源
问答
  • 对象序列化涉及的类和接口
    千次阅读
    2022-03-30 15:30:10

    简述

    开发时遇到redis序列化数据报错,这里记录下

    错误信息

    org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [cn.gov.tdt.imgonline.web.template.SessionUser]

    问题定位

    一般涉及到的常见的redis序列化错误的都是:

    1. 序列化器替换,比如换为string serializer

    2. 默认redis的序列化器,序列化的对象需要实现Serializable接口

    然后这个问题关键词定位

    DefaultSerializer + ‘requires a Serializable payload ’ 

    所以,结论就是 实现Serializable接口

    解决

    sessionUser实现Serializable接口,并

    private static final long serialVersionUID = ...;

    更多相关内容
  •  为什么实现了java.io.Serializable接口才能被序列化  transient的作用是什么  怎么自定义序列化策略  自定义的序列化策略是如何被调用的  ArrayList对序列化的实现有什么好处  Java对象序列化  ...
  • 遇到这个 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段话,就能解释43楼的问题,静态属性不会被序列化,但是却又有一个特殊的静态属性,会被序列化,没办法,这个静态属性是亲生的。自带的。)

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

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

     

    最后更新一下

    1,(针对25楼的留言:序列化的类的所有成员变量是不是都要是基本类型或实现Serializable接口的类型?)

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

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

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

    大师兄

    大师兄

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

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

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

     

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

    展开全文
  • 对象序列化与反序列化详解

    千次阅读 2016-09-30 10:47:45
    Java对象序列化时参与序列化的内容包含以下几个方面:1.属性,包括基本数据类型、数组以及其他对象的引用; 2.类名。不能被序列化的内容有以下几个方面:1.方法。 2.有static修饰的属性。 3.有transient修饰的...

    【1】 序列化与反序列化

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

    简单来说:

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

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

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

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

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

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

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

    对象序列化内容与机制

    ① Java对象序列化时参与序列化的内容包含以下几个方面:

    • 属性,包括基本数据类型、数组以及其他对象的引用;
    • 类名。

    ② 不能被序列化的内容有以下几个方面

    • 方法。
    • 有static修饰的属性。
    • 有transient修饰的属性。

    ③ 对象的序列化

    对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

    序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。

    序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。

    如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:

    • Serializable
    • Externalizable

    ExternalizableSerializable接口的子类,有时我们不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性。

    显示定义serialVersionUID的用途

    凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:

    private static final long serialVersionUID;
    

    serialVersionUID用来表明类的不同版本间的兼容性。如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显示声明。

    • 希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID。
    • 不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID。

    Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

    当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。

    如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。


    默认序列化机制

    如果仅仅只是让某个类实现Serializable接口,而没有其它任何处理的话,则就是使用默认序列化机制。

    使用默认机制,在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,同样地,这些其它对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。


    【2】使用对象流序列化对象

    若某个类实现了 Serializable 接口,该类的对象就是可序列化的:

    ① 序列化和反序列化

    • 序列化

      创建一个 ObjectOutputStream调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象。注意写出一次,操作flush()

    • 反序列化

      创建一个 ObjectInputStream调用 readObject() 方法读取流中的对象

    强调:如果某个类的字段不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化。

    • 序列化一个对象将可能得到整个对象序列。

    在序列化过程中不仅保留当前类对象的数据,而且递归保存对象引用的每个对象的数据。将整个对象层次写入字节流中,这也就是序列化对象的“深复制”,即复制对象本身及引用的对象本身。

    另外,添加“transient”关键字的不能被序列化,读取时也不可被恢复,该属性值保持默认初始值。


    ② 实例demo
    序列化:将对象写入到磁盘或者进行网络传输。

    # 要求对象必须实现序列化
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test3.txt"));
    Person p = new Person("韩梅梅",18,"中华大街",new Pet());
    oos.writeObject(p);
    oos.flush();
    oos.close();
    

    反序列化:将磁盘中的对象数据源读出。

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test3.txt"));
    Person p1 = (Person)ois.readObject();
    System.out.println(p1.toString());
    ois.close();
    

    ③ 使用的Person对象

    package com.web.test;
    import java.io.Serializable;
    public class Person  implements Serializable{
    	private String name;
    	private int age;
    	private String sex;
    	
    	// get set tostring...
    }
    
    

    ④ 序列化对象的读写

    package com.web.test;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.OutputStream;
    
    import org.junit.Test;
    
    public class TestObject {
    	
    	@Test
    	public void testWrite() throws IOException{
    		ObjectOutputStream oos = null;
    		String destFile = "D:\\person111.txt";
    		File file = new File(destFile);
    		if (!file.exists()) {
    			file.createNewFile();
    		}
    		/*每次写入不是覆盖而为追加*/
    		FileOutputStream fos = new FileOutputStream(file,true);
    		if (file.length()<1) {
    			oos = new ObjectOutputStream(fos);
    		}else {
    			/*使用重写的MyObjectOutputStream,后续写入时,不写入header;
    			 * 否则会报:java.io.StreamCorruptedException: 
    			 * invalid type code: AC错误。*/
    			oos = new MyObjectOutputStream(fos);
    		}
    		oos.writeObject("Test");
    		
    		oos.flush();
    		
    	}
    	/*
    	 * fileName:准备读取的字节文件
    	 * */
    	@Test
    	public void testRead() throws Exception{
    		String fileName = "D:\\person111.txt";
    		FileInputStream fis = new FileInputStream(fileName);
    		ObjectInputStream ois = new ObjectInputStream(fis);
    		
    		String name = (String) ois.readObject();
    		String name1 = (String) ois.readObject();
    		System.out.println(name+"888"+name1);
    		
    	}
    	/*重写ObjectOutputStream*/
    	class MyObjectOutputStream extends ObjectOutputStream { 
    	public MyObjectOutputStream() throws IOException {  
    	       super(); 
    	}
    	 public MyObjectOutputStream(OutputStream out) throws IOException {
    	  super(out);
    	 } 
    
    	 protected void writeStreamHeader() throws IOException { 
    	   return;
    	 }
    	}
    }
    
    

    ⑤ 使用一个ObjectInputStream读取两个对象

    @Test
    public void testWrite1() throws Exception{
    	ObjectOutputStream oos = null;
    	String destFile = "D:\\person11.txt";
    	File file = new File(destFile);
    	if (!file.exists()) {
    		file.createNewFile();
    	}
    	/*每次写入不是覆盖而为追加*/
    	FileOutputStream fos = new FileOutputStream(file,true);
    	oos = new ObjectOutputStream(fos);
    	for(int i=0;i<=2;i++){
    	oos.writeObject(new Person("liuxing",12,"boy"));
    	}
    	/*默认调用flush()*/
    	oos.close();
    	
    	FileInputStream fis = new FileInputStream(destFile);
    	ObjectInputStream ois = new ObjectInputStream(fis);
    	
    	Person p = (Person) ois.readObject();
    	System.out.println(p.getName());
    	Person p1 = (Person) ois.readObject();
    	System.out.println(p1.getName());
    
    }
    

    ⑥ transient关键字的作用

    在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

    java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。


    【3】反序列化的注入漏洞问题

    1.WebLogic, WebSphere, JBoss, Jenkins, and OpenNMS等java应用都曾受过该漏洞影响。Apache Commons Collections这样的基础库非常多的Java应用都在用。一旦编程人员误用了反序列化这一机制,使得用户输入可以直接被反序列化,就能导致任意代码执行,这是一个极其严重的问题,WebLogic等存在此问题的应用可能只是冰山一角。

    2.首先拿到一个漏洞应用,需要找到一个接受外部输入的序列化对象的接收点,即反序列化漏洞的触发点。我们可以通过审计源码中对反序列化函数的调用(例如readObject())来寻找,也可以直接通过对应用交互流量进行抓包,查看流量中是否包含序列化数据来判断,如java序列化数据的特征为以标记(ac ed 00 05)开头。

    确定了反序列化输入点后,再考察应用的Class Path中是否包含相应的基础库,如Apache Commons Collections库,可通过“grep -R InvokerTransformer .”确认是否包含exp需要的类库(把“InvokerTransformer”删除干净则此漏洞便无法触发)。

    在这里插入图片描述
    新版本虽然抓不到端口信息了,但是exp需要的基础类库仍然存在。

    解决方法:可以加入防火墙过滤相应端口的通信。假若反序列化可以设置Java类型的白名单,那么问题的影响就小了很多。使用加密通信,如SSL。

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

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

    • 应用层
    • 传输层
    • 网络层
    • 网络接口层
      在这里插入图片描述
      如上图所示,OSI 七层协议模型中,表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据。此外,OSI 七层协议模型中的应用层、表示层和会话层对应的都是 TCP/IP 四层模型中的应用层,所以序列化协议属于 TCP/IP 协议应用层的一部分。

    tcp/ip是事实标准,分4层。osi模型是国际标准,分7层。我们有时会看到如右图所示五层模型,这是从概念上来理解的。将网络接口层分为了数据链路层和物理层。
    在这里插入图片描述

    参考博文:
    序列化和反序列化、以及反序列化注入问题要点
    JAVA反序列化漏洞
    序列化与反序列化

    展开全文
  • Java IO对象处理流以及序列化

    ⭐写在前面⭐

    🧭Java IO流学习
    🎉 内容回顾
    Java IO流介绍及文件操作
    Java IO字节流
    Java IO字符流
    Java IO节点流和处理流
    📢今天我们进行Java IO对象处理流以及序列化 的学习,感谢你的阅读,内容若有不当之处,希望大家多多指正,一起进步!!!
    ♨️如果觉得博主文章还不错,可以👍三连支持⭐一下哦😀

    Java IO对象处理流以及序列化

    需求

    1. int num = 100;这个int数据保存在文件中,注意不是100数字,而是int 100,并且能够从这个文件中直接恢复出来int 100
    2. Student student = new Student("小明",18)这个student对象保存到文件中,并且能够从文件中恢复出来。

    🚀解读
    其实上面的要求就是把对象进行 序列化 反序列化 的操作。

    在这里插入图片描述

    序列化和反序列化

    序列化 就是在保存数据时,保存 数据的值 数据类型
    反序列化 就是在恢复数据时,恢复 数据的值 数据类型

    🚀注意
    需要让某个对象支持序列化机制,则必须让其类是可序列化的,让一个类实现可序列化,可以让该类是实现Serializable接口。

    通过查看源码我们发现Serializable接口并没有任何方法,它仅仅起到一个标识的作用,即该类可以序列化。

    Serializable接口

    public interface Serializable {
    }
    

    像基本数据类型可以实现序列化,因为其对应的包装类实现了Serializable接口。
    在这里插入图片描述

    序列化目的

    对象序列化的目的是将对象保存到磁盘上,或者允许在网络中传输对象。对象序列化的机制允许把内存中的Java对象转化为与平台无关的二进制流,从而允许把这种二进制流持久化保存到磁盘上,通过网络将这种二进制流传输到另一个网络节点,其他程序一旦获取到这种二进制流,都可以将二进制流恢复成之前Java的对象。

    对象流介绍

    功能: 提供了对基本类型或对象类型的序列化和反序列化的方法

    ObjectOutputStream 提供 序列化功能
    ObjectInputStream 提供 反序列化功能

    ObjectOutputStream
    在这里插入图片描述
    ObjectInputStream
    在这里插入图片描述

    ObjectOutputStream使用

    将一个Student对象保存在磁盘work.txt文件中,文件路径:e:\Java\work.txt

    Student类

    public class Student implements Serializable {//实现Seriable接口
        private String name;
        private int age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    

    ObjectOutputSteamTest 类

    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    
    public class ObjectOutputSteamTest {
        public static void main(String[] args) {
            String pathFile = "e:\\Java\\work.txt";
            ObjectOutputStream objectOutputStream = null;
    
            try {
                objectOutputStream = new ObjectOutputStream(new FileOutputStream(pathFile));//打开对象输出流
                objectOutputStream.writeObject(new Student("小明",18));//写入student对象
                System.out.println("数据保存完毕~~~");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {//关闭流
                try {
                    if (objectOutputStream != null) {
                        objectOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    运行结果:
    因为涉及字节码的问题,会出现乱码的情况。
    在这里插入图片描述

    ObjectInputStream使用

    work.txt文件中的Student对象读取出来,文件路径:e:\Java\work.txt
    Student类 同上
    ObjectInputSteamTest 类

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    
    public class ObjectInputStreamTest {
        public static void main(String[] args) {
            String pathFile = "e:\\Java\\work.txt";
            ObjectInputStream objectInputStream = null;
    
            try {
                objectInputStream = new ObjectInputStream(new FileInputStream(pathFile));//打开对象输入流
                Object o = (Student)objectInputStream.readObject();//读取对象
                System.out.println(o);//打印对象
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {//关闭流
                try {
                    if (objectInputStream != null) {
                        objectInputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    运行结果:
    在这里插入图片描述

    transient关键字

    transient关键字的作用是用来控制属性的序列化,在属性声明上加上该关键字,可以阻止该变量被序列化到文件中。在反序列化后,transient关键字所修饰变量设为初始值,如int类型的是0,对象性为null

    比如,我现在修改一下上述示例中的 Student类

    public class Student implements Serializable {//实现Seriable接口
        private String name;
        private int age;
        private transient int password;
    //省略构造方法和toString方法
    }
    

    重新在文件中写入对象,然后读取对象,观察运行结果。
    在这里插入图片描述
    🚀 transient关键字作用

    一定程度上保证序列化对象的安全,在对象的传输过程中,对象中可能包含一些敏感信息,比如密码,身份证号等,希望在序列化时不保存则可以添加transient关键字,在序列化中对应字段不会进行保存

    serialVersionUID

    serialVersionUID的作用

    1. 一个类如果要用于传输或长久地存到硬盘,必须变成二进制的形式,再次用到就需要将二进制还原回来,这就是序列化于反序列化,但怎么保证中间不出错呢,那么就需要为实现serialable的类生成一个serialVersionUID,它是唯一的,即是这个类改变了一个空格都会发生改变,但如果实现生成了,就不会再发生变化。
    2. 用来兼容不同的版本

    serialVersionUID的生成

    1. 默认的1L
    2. 通过类名,接口名,成员方法即属性等来生成一个64位的哈希字段,serialVersionUID的生成是依赖于Java编译器的,不同的编译器对于同一个类可能导致serialVersionUID的不同。

    idea生成serialVersionUID

    打开idea的设置
    在这里插入图片描述
    打开设置后,如下图找到选项并勾选。
    16)
    把光标放在类名上,按ALT+ENTER回车生成
    在这里插入图片描述
    此时我们重新将student写入文件。
    在这里插入图片描述
    对Student类添加一个属性,再次生成serialVersionUID
    在这里插入图片描述
    再次把对象从文件中读取出来会发现读取失败,产生InvalidClassException异常
    在这里插入图片描述
    🚀结论:

    当对一个类添加或者修改类中的任何的属性,已经序列化的类将无法恢复,因为新类生成的serialVersionUID与旧类序列化的对象不同,Java中反序列化的过程依赖于正确的serialVersionUID恢复序列化对象状态,当serialVersionUID不匹配时会抛出异常,用来表示类中不同的版本不兼容问题。

    展开全文
  • 标签:优秀博客:接口...注:一个对象序列化后,它的所有子类对象也将自动序列化。当一个声明要实现Serializable接口时,只是表明该参加串行化协议,而不需要实现任何特殊的方法。要想序列化对象,必须先创建...
  • Java序列化和序列化

    千次阅读 2021-07-21 08:53:59
    a,什么叫序列化和序列化 b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化 c,serialVersionUID 这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字,迷惑ing。 我刚刚见到这个...
  • 将需要被序列化实现serializable接口(标机接口),该接口没有需要实现的方法,implements serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:fileOutputStream)来构造一个objectOut...
  • 序列化的实现:将需要被序列化实现Serializable接口(标记接口),该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个...
  • 只要这个实现了Serilizable接口,这个的所有属性方法都会自动序列化。 public class Person implements Serializable {} 如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络...
  • 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个实现了Serilizable接口,这个的所有属性...
  • JAVA中的对象序列化格式

    千次阅读 2018-04-02 18:14:11
    JAVA中的对象序列化  1.网络传输时对象序列化,而字符串就不用... 字符串不用序列化的原因: 字符串是已经实现了Serializable接口的,所以它已经是序列化了的  2.Java序列化格式详解 RPC的世界,由于涉及到进...
  • 使用C++进行对象序列化

    千次阅读 2017-03-02 16:18:15
    1 什么是序列化 程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台...简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序
  • 掌握对象序列化的作用。 掌握Serializable接口的作用。 可以使用ObjectOutputStream进行对象的序列化操作。 可以使用ObjectInputStream进行对象的反序列化操作。 掌握Externalizable接口的作用及与Serializable接口...
  • 序列化的实体是个对象,结果也是个对象,并非是格式化文本,你在记事本里看到的购物信息保存记录,其实不是对象序列化的结果,而是对象输出的格式化文本,真正的序列化对象是看不懂的。 序列化的应用场景 在实际使用...
  • 一、序列化和序列化: 在进行对象操作的实现时,涉及到好几个关键词Serializabletransient 什么是序列化和序列化序列化:将对象转化为字节的过程称为序列化过程 (存储到本地磁盘) 反序列化:将...
  • java对象序列化的用途

    千次阅读 2016-03-21 10:02:13
    对象序列化是对象持久化的一种实现方法,它是将一个对象的属性方法转化为一种序列化的格式以用于存储传输,反序列化就是根据这些保存的信息重建对象的过程。 Java对象序列化机制一般来讲有两种用途: Java的...
  • Java 串行化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象...序列化就是一种用
  • 为了防止反序列失败,该对象需提供一个默认的serialVersionUID(该值在反序列化的时候会进行校验校验失败并抛出InvalidClassException异常)。 现在需要将一个对象返回给前端,那么该对象是否需要实现 Serializable...
  • java安全(五)java反序列化

    千次阅读 2022-03-27 14:02:42
    序列化 在调用RMI时,发现接收发送数据都是反序列...比如,JacksonFastjson这类序列化库,在JSON(XML)的基础上进行改造,通过特定的语法来传递对象. RMI使用java等语言内置的序列化方法,将一个对象转化成一串二进制
  • 例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。也是对象持久化的一种机制 序列化场景: 一般用于读写I/O流文件的时候,或是...
  • 几种常用序列化和序列化方法

    千次阅读 2019-12-30 21:48:38
    序列化和序列化几乎是工程师们每天都要面对的事情,但是要精确掌握这两个概念并不容易:一方面,它们往往作为框架的一部分出现而湮没在框架之中;另一方面,它们会以其他更容易理解的概念出现,例如加密、持久化。...
  • 序列化、反序列化、反射的关系

    千次阅读 2019-06-03 22:33:10
    直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,静态编译:在编译时确定类型,绑定对象,即通过。动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低...
  • 一、重要理解   1.啥是序列化??  序列化:就是将数据转成一串字节序列---持久化  反序列化:就是将一串字节序列转成相应的数据     2.对象输出输入流:(也是... 输出流:将对象序列化-- ObjectOutputStr...
  • 面试题 - Java序列化和序列化

    千次阅读 2020-09-15 18:03:22
    将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化需要实现 Serializable 接口,使用 ObjectInputStream ...
  • Java中为什么实体需要实现序列化

    千次阅读 多人点赞 2017-07-14 19:53:11
    一、什么是Java对象序列化 Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输,一般当我们使用...
  • C++对象序列化

    千次阅读 2013-03-21 15:34:53
    什么是序列化 程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络...简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序列化相对的是
  • java 的transient关键字的作用是需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。transient使用小结1)一旦变量被transient修饰,...
  • 对象序列化/反序列化大家应该都比较熟悉:序列化就是将object转化为可以传输的二进制,反序列化就是将二进制转化为程序内部的对象序列化/反序列化主要体现在程序I/O这个过程中,包括网络I/O磁盘I/O。 那么什么...
  • Java序列化和序列化存在的意义

    千次阅读 2018-10-09 08:57:21
    文章来源: 1 javabean为什么要实现序列化?...2 java 中为什么实体需要实现序列化 https://blog.csdn.net/thinkingcao/article/details/75133183 3 java为什么要实现serializable序列化 http://www.cnblog...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 97,160
精华内容 38,864
热门标签
关键字:

对象序列化涉及的类和接口