精华内容
下载资源
问答
  • 遇到这个 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亲生的,你写或者不写,只要实现了接口,他就是存在的,在序列化的时候,他还真就序列化,存起来了。不然在反序列化的时候,就没的对比了嘛,因为他是亲生的,所以,咱手动添加的静态,和这个静态是不能比的。

     

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

    展开全文
  • public class SerializeHelper { /// <summary>... /// 二进制序列化器 /// </summary> public static void BinarySerialize() { //使用二进制序列化对象 string fileName = Path.<
  • 序列化和反序列化的详解

    万次阅读 多人点赞 2018-09-19 09:09:29
    1、序列化和反序列化的定义:  (1)Java序列化就是指把Java对象转换为字节序列的过程  Java反序列化就是指把字节序列恢复为Java对象的过程。  (2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可...

    一、基本概念

    1、序列化和反序列化的定义:

        (1)Java序列化就是指把Java对象转换为字节序列的过程

            Java反序列化就是指把字节序列恢复为Java对象的过程。

       (2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。

           反序列化的最重要的作用:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。

       总结:核心作用就是对象状态的保存和重建。(整个过程核心点就是字节流中所保存的对象状态及描述信息)

     

    2、json/xml的数据传递:

     在数据传输(也可称为网络传输)前,先通过序列化工具类将Java对象序列化为json/xml文件。

    在数据传输(也可称为网络传输)后,再将json/xml文件反序列化为对应语言的对象

     

    3、序列化优点:

     ①将对象转为字节流存储到硬盘上,当JVM停机的话,字节流还会在硬盘上默默等待,等待下一次JVM的启动,把序列化的对象,通过反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间(永久性保存对象)。

     ②序列化成字节流形式的对象可以进行网络传输(二进制形式),方便了网络传输。

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

     

    4、序列化算法需要做的事:

      ① 将对象实例相关的类元数据输出。

      ② 递归地输出类的超类描述直到不再有超类。

      ③ 类元数据输出完毕后,从最顶端的超类开始输出对象实例的实际数据值。

      ④ 从上至下递归输出实例的数据。

     

    二、Java实现序列化和反序列化的过程

       1、实现序列化的必备要求:

           只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常) 

       2、JDK中序列化和反序列化的API:

          ①java.io.ObjectInputStream:对象输入流。

              该类的readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。

         ②java.io.ObjectOutputStream:对象输出流。

              该类的writeObject(Object obj)方法将将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。

     3、实现序列化和反序列化的三种实现:

      ①若Student类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化。

                 ObjectOutputStream采用默认的序列化方式,对Student对象的非transient的实例变量进行序列化。 
                 ObjcetInputStream采用默认的反序列化方式,对Student对象的非transient的实例变量进行反序列化。

      ②若Student类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。

               ObjectOutputStream调用Student对象的writeObject(ObjectOutputStream out)的方法进行序列化。 
               ObjectInputStream会调用Student对象的readObject(ObjectInputStream in)的方法进行反序列化。

      ③若Student类实现了Externalnalizable接口,且Student类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

               ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。 
               ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。

    4、序列化和反序列化代码示例

    public class SerializableTest {
            public static void main(String[] args) throws IOException, ClassNotFoundException {
                //序列化
                FileOutputStream fos = new FileOutputStream("object.out");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                Student student1 = new Student("lihao", "wjwlh", "21");
                oos.writeObject(student1);
                oos.flush();
                oos.close();
                //反序列化
                FileInputStream fis = new FileInputStream("object.out");
                ObjectInputStream ois = new ObjectInputStream(fis);
                Student student2 = (Student) ois.readObject();
                System.out.println(student2.getUserName()+ " " +
                        student2.getPassword() + " " + student2.getYear());
        }
    
    }
    
    public class Student implements Serializable{                             
                                                                              
        private static final long serialVersionUID = -6060343040263809614L;   
                                                                              
        private String userName;                                              
        private String password;                                              
        private String year;                                                  
                                                                              
        public String getUserName() {                                         
            return userName;                                                  
        }                                                                     
                                                                              
        public String getPassword() {                                         
            return password;                                                  
        }                                                                     
                                                                              
        public void setUserName(String userName) {                            
            this.userName = userName;                                         
        }                                                                     
                                                                              
        public void setPassword(String password) {                            
            this.password = password;                                         
        }                                                                     
                                                                              
        public String getYear() {                                             
            return year;                                                      
        }                                                                     
                                                                              
        public void setYear(String year) {                                    
            this.year = year;                                                 
        }                                                                     
                                                                              
        public Student(String userName, String password, String year) {       
            this.userName = userName;                                         
            this.password = password;                                         
            this.year = year;                                                 
        }                                                                     
    }                                                                         
                                                                              

     ①序列化图示

    ②反序列化图示

    三、序列化和反序列化的注意点:

    ①序列化时,只对对象的状态进行保存,而不管对象的方法;

    ②当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

    ③当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

    ④并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:

    • 安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;

    • 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;

    ⑤声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。

    ⑥序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:

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

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

    ⑦Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;

    ⑧如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;

    注意:浅拷贝请使用Clone接口的原型模式。

    展开全文
  • 序列化和反序列化的底层实现原理是什么?

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

    序列化和反序列化作为Java里一个较为基础的知识点,大家心里也有那么几句要说的,但我相信很多小伙伴掌握的也就是那么几句而已,如果再深究问一下Java如何实现序列化和反序列化的,就可能不知所措了!遥记当年也被问了这一个问题,自信满满的说了一大堆,什么是序列化、什么是反序列化、什么场景的时候才会用到等,然后面试官说:那你能说一下序列化和反序列化底层是如何实现的吗?一脸懵逼,然后回家等通知!

    一、基本概念

    1、什么是序列化和反序列化

    (1)Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程;

    (2)**序列化:**对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存了Java对象的状态以及相关的描述信息。序列化机制的核心作用就是对象状态的保存与重建。

    (3)**反序列化:**客户端从文件中或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。

    (4)本质上讲,序列化就是把实体对象状态按照一定的格式写入到有序字节流,反序列化就是从有序字节流重建对象,恢复对象状态。

    2、为什么需要序列化与反序列化

    我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。

    那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的!如何做到呢?这就需要Java序列化与反序列化了!

    换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。

    当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。

    总的来说可以归结为以下几点:

    (1)永久性保存对象,保存对象的字节序列到本地文件或者数据库中;
    (2)通过序列化以字节流的形式使对象在网络中进行传递和接收;
    (3)通过序列化在进程间传递对象;

    3、序列化算法一般会按步骤做如下事情:

    (1)将对象实例相关的类元数据输出。
    (2)递归地输出类的超类描述直到不再有超类。
    (3)类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。
    (4)从上至下递归输出实例的数据

    二、Java如何实现序列化和反序列化

    1、JDK类库中序列化和反序列化API

    (1)java.io.ObjectOutputStream:表示对象输出流;

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

    (2)java.io.ObjectInputStream:表示对象输入流;

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

    2、实现序列化的要求

    只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常!

    3、实现Java对象序列化与反序列化的方法

    假定一个User类,它的对象需要序列化,可以有如下三种方法:

    (1)若User类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化

    ObjectOutputStream采用默认的序列化方式,对User对象的非transient的实例变量进行序列化。
    ObjcetInputStream采用默认的反序列化方式,对对User对象的非transient的实例变量进行反序列化。

    (2)若User类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。

    ObjectOutputStream调用User对象的writeObject(ObjectOutputStream out)的方法进行序列化。
    ObjectInputStream会调用User对象的readObject(ObjectInputStream in)的方法进行反序列化。

    (3)若User类实现了Externalnalizable接口,且User类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

    ObjectOutputStream调用User对象的writeExternal(ObjectOutput out))的方法进行序列化。
    ObjectInputStream会调用User对象的readExternal(ObjectInput in)的方法进行反序列化。

    4、JDK类库中序列化的步骤

    步骤一:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\object.out"));
    

    步骤二:通过对象输出流的writeObject()方法写对象:

    oos.writeObject(new User("xuliugen", "123456", "male"));
    

    5、JDK类库中反序列化的步骤

    步骤一:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

    ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));
    

    步骤二:通过对象输出流的readObject()方法读取对象:

    User user = (User) ois.readObject();
    

    说明:为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。

    6、序列化和反序列化的示例

    为了更好地理解Java序列化与反序列化,举一个简单的示例如下:

    public class SerialDemo {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
    	    //序列化
            FileOutputStream fos = new FileOutputStream("object.out");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            User user1 = new User("xuliugen", "123456", "male");
            oos.writeObject(user1);
            oos.flush();
            oos.close();
    		//反序列化
            FileInputStream fis = new FileInputStream("object.out");
            ObjectInputStream ois = new ObjectInputStream(fis);
            User user2 = (User) ois.readObject();
            System.out.println(user2.getUserName()+ " " + 
    	        user2.getPassword() + " " + user2.getSex());
            //反序列化的输出结果为:xuliugen 123456 male
        }
    }
    
    public class User implements Serializable {
        private String userName;
        private String password;
        private String sex;
        //全参构造方法、get和set方法省略
    }
    
    

    object.out文件如下(使用UltraEdit打开):

    这里写图片描述

    注:上图中0000000h-000000c0h表示行号;0-f表示列;行后面的文字表示对这行16进制的解释;对上述字节码所表述的内容感兴趣的可以对照相关的资料,查阅一下每一个字符代表的含义,这里不在探讨!

    类似于我们Java代码编译之后的.class文件,每一个字符都代表一定的含义。序列化和反序列化的过程就是生成和解析上述字符的过程!

    序列化图示:

    这里写图片描述

    反序列化图示:

    这里写图片描述

    三、相关注意事项

    1、序列化时,只对对象的状态进行保存,而不管对象的方法;

    2、当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

    3、当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

    4、并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:

    • 安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;

    • 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;

    5、声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。

    6、序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:

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

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

    7、Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;

    8、如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;

    四、总结

    看到这里,可能已经让我们很满足了,毕竟已经知道了我们平时使用的序列化和反序列化是如何进行操作的,Java给我们提供了哪些接口可供使用,也比我们最初知道的简单的什么是序列化、反序列化以及作用多了很多!后续内容我们也会不断在讨论和更新!


    参考文章:

    1、https://zhidao.baidu.com/question/688891250408618484.html
    2、https://blog.csdn.net/morethinkmoretry/article/details/5929345
    3、https://www.jianshu.com/p/edcf7bd2c085
    4、https://blog.csdn.net/xiaocaidexuexibiji/article/details/22692097

    在这里插入图片描述

    【视频福利】2T免费学习视频,搜索或扫描上述二维码关注微信公众号:Java后端技术(ID: JavaITWork)回复:1024,即可免费获取!内含SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,Jenkins、Nexus、Docker、ELK等等免费学习视频,持续更新!

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

    千次阅读 多人点赞 2020-06-28 13:25:46
    1、序列化和反序列化定义 Java序列化是指把Java对象转换为字节序列的过程。 Java反序列化是指把字节序列恢复为Java对象的过程。 2、序列化和反序列化的作用 (1)序列化作用 在传递和保存对象时,保存对象的...

    🍅 Java学习路线:搬砖工逆袭Java架构师

    🍅 简介:Java领域优质创作者🏆、CSDN哪吒公众号作者✌ 、Java架构师奋斗者💪

    🍅 扫描主页左侧二维码,加入群聊,一起学习、一起进步 

    🍅 欢迎点赞 👍 收藏 ⭐留言 📝  

    面试官:兄弟,说说你对transient的理解和感悟

    哪吒:what?还有感悟?

    一、基本概念

    1、序列化和反序列化定义

    Java序列化是指把Java对象转换为字节序列的过程。

    Java反序列化是指把字节序列恢复为Java对象的过程。

    2、序列化和反序列化的作用

    (1)序列化作用

    在传递和保存对象时,保存对象的完整性和可传递性。
    对象转换为有序字节流,可以在网络上传输或者保存在本地文件(一般json/xml文件居多)中。

    (2)反序列化作用

    根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。

    二、序列化和反序列化的优缺点

    1、优点

    (1)将对象转为字节流存储到硬盘上,当JVM停机的时候,字节流还会在硬盘上等待,等待下一次JVM启动,把序列化的对象,通过反序列化转为原来的对象,并且序列化的二进制序列能够减少存储空间(永久性保存对象)。

    (2)序列化为字节流形式的对象可以进行网络传输(二进制形式)。

    (3)通过序列化可以在进程间传递对象。

    2、缺点

    (1)无法跨语言

    无法跨语言是Java序列化最致命的问题。

    对于跨进程的服务调用,服务提供者可能是Java之外的其它语言,当我们需要和其它语言交互时,Java序列化就难以胜任。

    事实上,目前几乎所有流行的Java RPC通信框架,都没有使用Java序列化作为编解码框架,原因就是它无法跨语言,而这些RPC框架往往需要支持跨语言调用。

     Java RPC通信框架简介

    RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,作用是有助于系统的垂直拆分,使系统更易扩展。Java中RPC框架比较多,各有特色,广泛使用的有RMI、Hession、Dubbo等。

    1、RMI(远程方法调用):

    JAVA自带的远程方法调用工具,不过有一定的局限性,毕竟是JAVA语言最开始时的设计,后来很多框架的原理都基于RMI。

    2、Hessian(基于HTTP的远程方法调用):

    基于HTTP协议传输,在性能方面还不够完美,负载均衡和失效转移依赖于应用的负载均衡器,Hessian的使用则与RMI类似,区别在于淡化了Registry的角色,通过显示的地址调用,利用HessianProxyFactory根据配置的地址create一个代理对象,另外还要引入Hessian的Jar包。

    3、Dubbo(淘宝开源的基于TCP的RPC框架)

    Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

    (2)序列化后流的长度比通过缓冲区处理要大的多。



    (3)序列化性能太低

    三、序列化使用场景

    1、分布式传递对象,或者网络传输,需要序列化

    2、我调用你的jvm的方法,结果返回到我的jvm上进行处理

    3、序列化可以保持对象的状态
    比如:tomcat关闭以后会把session对象序列化到SESSIONS.ser文件中,等下次启动的时候就把这些session再加载到内存里面来。

    4、数据传输并复原

    在j2ee中页面与后台使用的比较多。尤其是在列表中的时候使用尤为突出。

    比如:一个人员的列表保存起来的话,你可以将这个列表序列化,传到后台,然后再反序列化成person对象直接进行对象的保存。

    5、比如EJB远程调用 分布式存储,缓存存储等

    6、像银行卡、密码这些字段不能被序列化

    四、序列化和反序列化的注意事项

    1、Java序列化的方式

    实现 Serializable 接口:可以自定义 writeObject、readObject、writeReplace、readResolve 方法,会通过反射调用。

    实现 Externalizable 接口:需要实现 writeExternal 和 readExternal 方法。

    2、序列化ID问题

    虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。

    3、静态字段不会序列化

    序列化时不保存静态变量,这是因为序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。

    4、transient

    transient代表对象的临时数据。

    如果你不想让对象中的某个成员被序列化可以在定义它的时候加上 transient 关键字进行修饰,这样,在对象被序列化时其就不会被序列化。

    transient 修饰过的成员反序列化后将赋予默认值,即 0 或 null。

    有些时候像银行卡号这些字段是不希望在网络上传输的,transient的作用就是把这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

    5、父类的序列化

    当一个父类实现序列化,子类自动实现序列化;而子类实现了 Serializable 接口,父类也需要实现Serializable 接口。

    6、当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化

    7、并非所有的对象都可以序列化

    (1)安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;

    (2)资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;

    8、序列化解决深拷贝问题

    如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存,这是能用序列化解决深拷贝的重要原因。

    五、代码实例

    1、实体类

    package javase.transientpackage;
     
    import java.io.Serializable;
     
    public class User implements Serializable {
        private static final long serialVersionUID = 1L;
        private String username;
        private transient String password;
     
        //construct、setter、getter
    }

    2、ObjectOutputStream实现序列化和ObjectInputStream实现反序列化 

    package javase.transientpackage;
     
    import java.io.*;
     
    public class TransientTest {
        public static void main(String[] args) {
            try {
                SerializeUser();
                DeSerializeUser();
            } catch (IOException e) {
                e.printStackTrace();
            }catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
     
        //序列化
        private static void SerializeUser() throws IOException{
            User user = new User();
            user.setUsername("素小暖");
            user.setPassword("123456");
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://data.txt"));
            oos.writeObject(user);
            oos.close();
            System.out.println("普通字段序列化:username=  "+user.getUsername());
            System.out.println("添加了transient关键字序列化:password=  "+user.getPassword());
        }
     
        //反序列化
        private static void DeSerializeUser() throws IOException, ClassNotFoundException {
            File file = new File("D://data.txt");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            User user = (User)ois.readObject();
            System.out.println("普通字段反序列化:username=  "+user.getUsername());
            System.out.println("添加了transient关键字反序列化:password=  "+user.getPassword());
        }
    }

    3、控制台输出

    展开全文
  • 新手小白学JAVA 初识序列化与反序列化

    万次阅读 多人点赞 2021-03-17 13:41:48
    序列化/反序列化 1.1 概述 序列化(Serialization)是将对象的状态信息转换为可以存储或传输形式的过程. 在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后可以通过从存储区中读取或者帆序列化对象的状态,...
  • 1.序列化与反序列化的概念 2.子类实现Serializable接口,父类没有实现,子类可以序列化吗? 3.类中存在引用对象,这个类对象在什么情况下可以实现序列化? 4.同一个对象多次序列化之间有属性更新,前后的序列化有...
  • JsonTools是一个不错的JSON序列化和反序列化功能包
  • Java对象的序列化(Serialization)和反序列化详解

    万次阅读 多人点赞 2018-02-13 15:56:02
    1.序列化和反序列化 序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如档案或记忆体缓冲等,在网络传输过程中,可以是字节或者XML等格式;而...
  • 进行代码检查时,Coverity工具在进行json转换时,报Unsafe Deserialization错误,字面意思是不安全的反序列化,根本原因就是反序列化会有漏洞导致的。 看完下文反序列化漏洞的原理后,我们就知道该如何解决这个问题...
  • Java序列化与反序列化

    万次阅读 多人点赞 2012-09-18 16:48:57
    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨。  1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程;而Java...
  • 秒懂Java序列化与反序列化

    千次阅读 多人点赞 2018-06-09 14:40:43
    什么是反序列化?为什么需要序列化?如何序列化?应该注意什么?本文将从这几方面来论述。 定义 什么是序列化?什么是反序列化? 序列化: 把Java对象转换为字节序列的过程。 反序列化:把字节序列恢复为Java...
  • java序列化与反序列化

    2009-02-18 14:09:11
    序列化与反序列化 序列化与反序列化 序列化与反序列化 序列化与反序列化 序列化与反序列化
  • SpringBoot的序列化和反序列化

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

    千次阅读 2020-09-05 13:04:20
    介绍 Java 的序列化与反序列化
  • Gson自定义序列化与反序列化

    千次阅读 2018-07-28 12:49:42
    在使用Gson的时候我们经常需要根据自己的需求定义序列化和反序列化的规则,本文主要讲解如何优雅地进行Gson的序列化和反序列化。 序列化 方法 实现JsonSerializer接口 注册自定义的序列化实现 demo ...
  • java序列化和反序列化

    千次阅读 2018-07-20 18:26:41
    反序列化:将二进制数据(字节序列)恢复为对象的过程称为反序列化; transient:修饰需要序列化的对象的某些属性,使得被修饰的属性不会被序列化和反序列化; serialVersionUID:需要序列化的对象的成员属性,...
  • LocalDateTime序列化反序列化

    千次阅读 2018-08-29 15:43:56
    LocalDateTimeTime序列化与反序列化 LocalDateTime时间的序列化和反序列化比较复杂,很多时候无法反序列化成功,主要场景包括 springmvc自带的LocalDateTime序列化与反序列化 jackson处理后的LocalDateTime序列化...
  • 排序、序列化及反序列化

    千次阅读 2019-11-20 12:31:09
    序列化、反序列化 一、概念 序列化(Serialization)是指把结构化对象转化为字节流。 反序列化(Deserialization)是序列化的逆过程。把字节流转为结构化对象。 Java序列化(java.io.Serializable) 当要在进程...
  • 序列化和反序列化的简单理解

    万次阅读 多人点赞 2016-05-10 19:24:12
    一、序列化和反序列化的概念  把对象转换为字节序列的过程称为对象的序列化。  把字节序列恢复为对象的过程称为对象的反序列化。  对象的序列化主要有两种用途:  1) 把对象的字节序列永久地保存到硬盘上,...
  • 皮卡丘中的PHP反序列化

    千次阅读 多人点赞 2021-08-25 10:49:53
    PHP反序列化序列化反序列化PHP中的魔法函数pikachu漏洞 以下内容来自pikachu靶场,自己记录用于复习PHP反序列化的利用. 真他妈后悔上课的时候没好好学PHP,现在搞web很是吃力.一步一步的走是很重要的. 序列化 序列...
  • java 常用序列化和反序列化框架使用demo ,java 常用序列化和反序列化框架使用demo
  • java序列化和反序列化以及序列化ID的作用分析

    万次阅读 多人点赞 2017-08-07 20:26:07
    java序列化和反序列化以及序列化ID的作用分析
  • .Net序列化与反序列化

    千次阅读 2020-03-30 17:38:46
    序列化就是把一个对象保存到一个文件或数据库字段中去,反序列化就是在适当的时候把这个文件再转化成原来的对象使用。其目的是以某种存储形成使自定义对象持久化,或者将这种对象从一个地方传输到另一个地方。 1、...
  • Java实现 LeetCode 297 二叉树的序列化与反序列化

    万次阅读 多人点赞 2020-03-05 17:03:06
    297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原...
  • JAVASE序列化和反序列化讲解

    千次阅读 2021-03-03 22:19:08
    序列化对象Studentmain实现截图序列化集合反序列化反序列化对象反序列化集合判断反序列化是否是集合反序列化集合代码注意transientStudent类注意反序列化代码序列化代码执行结果name为NULL解释:序列化版本号有什么...
  • 序列化和反序列化总结

    万次阅读 2020-08-13 20:25:43
    简介 对象的序列化 如上图,我们把一个Person对象以流的方式,写入到文件中保存,叫写对象,也叫对象序列化,对象中包含的不仅仅是字符,使用字节流。 ObjectOutputStream:对象的序列...关于序列化与反序列化,其实就
  • C#高级序列化与反序列化_C#高级序列化与反序列化_C#高级序列化与反序列化_C#高级序列化与反序列化
  • C++ JSON 序列化与反序列化

    热门讨论 2013-06-22 14:23:31
    C++ JSON 序列化与反序列化 相关的博客文章见:http://blog.csdn.net/TragicGuy

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 320,132
精华内容 128,052
关键字:

反序列化