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

     

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

    展开全文
  • Java中的序列化到底是什么

    千次阅读 多人点赞 2020-06-29 14:07:46
    序列化的目的是为了方便数据的传递以及存储到磁盘上(把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,这时我们就需要将对象转换成字节流才能进行网络传输。对于这种通用的操作,就出现了序列化

    我们都知道,新建一个对象的时候实现 Serializeable 接口,但为什么要这么做?什么时候这样子做?这样子做会不会出现幺蛾子?阿淼一个三连差点把自己都问懵逼了……

    那接下来,大家就和阿淼一起简单了解一下这个知识点吧……

    序列化的定义是:将一个对象编码成一个字节流(I/O);而与之相反的操作被称为反序列化。

    序列化的目的是为了方便数据的传递以及存储到磁盘上(把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,这时我们就需要将对象转换成字节流才能进行网络传输。对于这种通用的操作,就出现了序列化来统一这些格式)。

    简单来说序列化就是一种用来处理对象流的机制。将对象转化成字节序列后可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。

    使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。比如 Redis 将对象当做字符串存储的时候,如果对象实现了序列化,则只需要将对象直接存储即可(java会自动将对象转换成序列化后的字节流);否则需要自己将对象转换成 json 字符串存储,不过 json 字符串相对更加节省内存空间一些。

    通常建议:程序创建的每个JavaBean类都实现 Serializeable 接口。但是实现 Serializeable 接口也需要小心谨慎。正如《Effective Java》中第 74 条提到的那样:
    在这里插入图片描述

    如何实现序列化

    在 Java 中,只要一个类实现了 java.io.Serializable 接口,它就可以被序列化(枚举类也可以被序列化)。

    例如:每个枚举类型都会默认继承类java.lang.Enum,而Enum类实现了Serializable接口,所以枚举类型对象都是默认可以被序列化的。

    // DeletedEnum 类,表示删除状态
    @Getter
    @AllArgsConstructor
    public enum DeletedEnum {
    	NO_DELETED(0,"未删除"),
    	DELETED(1,"已删除");
    
    	public final Integer status;
    	public final String name;
    }
    

    下图是 java.lang.Enum 类:
    在这里插入图片描述

    而一个普通的类想实现序列化,只需要实现 Serializable 接口即可:

    @Data
    public class User implements Serializable {
        //序列化版本号
        private static final long serialVersionUID = 1111013L;
    
        transient private String name;
        private int age;
    
        public static void main(String[] args) {
            User user = new User();
            user.setAge(12);
            user.setName("小路飞");
            System.out.println(user);
        }
    }
    

    输出结果如下:

    User(name=小路飞, age=12)
    

    那为什么一个类实现了 Serializable 接口,它就可以被序列化呢?

    这是因为它使用 ObjectOutputStream 来持久化对象到文件中,使用了 writeObject 方法,该方法又调用了如下方法:

        /**
         * Underlying writeObject/writeUnshared implementation.
         */
        private void writeObject0(Object obj, boolean unshared) throws IOException {
            ……
            // remaining cases
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
            ……
        }
    

    从上述代码可知,如果被写对象的类型是String,或数组,或 Enum,或 Serializable,那么就可以对该对象进行序列化,否则将抛出 NotSerializableException。

    :String 类型的对象、枚举类型的对象、数组对象,都是默认可以被序列化的,并生成默认的序列化版本号。

    我看网上的资料都有讲使用 Externalizable 接口和使用 transient 关键字来实现序列化的方法,但阿淼感觉用的不多,所以在这里就不过多的赘述了,况且它们其实都是基于 Serializable 接口实现的序列化。

    如何自动生成序列化版本号

    idea IDE

    安装 serialVersionUID 插件即可。
    在这里插入图片描述

    eclipse

    一般来说有两种生成方式:

    • 一个是默认的1L,比如:private static final long serialVersionUID = 1L;
    • 一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。实现序列化后,类名上会出现黄色波浪下划线,选择第一项,添加已生成的串行版本标识即可自动生成一个。

    序列化版本号的用处

    java.io.Serializable 的文档中的解释是这样的:
    在这里插入图片描述

    大致意思是如下

    因为反序列化必须拥有 class 文件,但随着项目的升级,class 文件也会升级,序列化怎么保证升级前后的兼容性呢?

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

    ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
    

    只要版本号相同,即使更改了序列化属性,对象也可以正确被反序列化回来。

    @Data
    public class User implements Serializable {
        //序列化版本号    
        private static final long serialVersionUID = 1111013L;
        private String name;
        private int age;
    }
    

    如果反序列化使用的 class 的版本号与序列化时使用的不一致,反序列化会报 InvalidClassException 异常。
    在这里插入图片描述
    如果可序列化的类未明确声明 serialVersionUID ,则序列化运行时将根据该类的各个方面为该类计算默认的 serialVersionUID 值,如Java(TM)对象序列化规范中所述。但是,强烈建议所有可序列化的类显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对类详细信息高度敏感,而类详细信息可能会根据编译器的实现而有所不同,因此可能在反序列化期间导致意外的 InvalidClassExceptions。

    而且,默认值不利于 jvm 间的移植,可能class文件没有更改,但不同 jvm 可能计算的规则不一样,这样也会导致无法反序列化。

    因此,为了保证不同Java编译器实现之间的 serialVersionUID 值一致,可序列化的类必须声明一个显式的 serialVersionUID 值。强烈建议显式 serialVersionUID 声明在可能的情况下使用 private 修饰符,因为此类声明仅适用于立即声明的类 serialVersionUID字段作为继承成员不起作用。

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

    参考

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

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

    序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。确切地说,代码执行序列化需要特殊的权限:即指定了 SerializationFormatter 标志的 SecurityPermission。在默认策略下,通过 Internet 下载的代码或 Internet 代码不会授予该权限;只有本地计算机上的代码才被授予该权限。

    通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。

    对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存不可序列化的重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。

    摘自百度百科:https://baike.baidu.com/item/序列化/2890184?fr=aladdin

    java中一般将需要序列化的对象实现serializable接口。如果你想某个字段不序列化,将其声明为transient.

    序列化的原因是想将对象转换成流,方便存储和在网络上传输。

    什么情况下会用到序列化?

     1当你想把内存中的对象写入到硬盘时
     2当你想用套接字在网络上传输对象时
     3当你想通过RMI调用对象时
    (RMI是什么东西?):RMI总结来说就是远程调用对象,在一个jvm上调用另一个jvm的对象。
    

    序列化需要注意的事项

     1序列化只保存对象的状态,而不管对象的方法。
    
     2当一个父类实现了序列化,它的子类也自动实现序列化,不用显示进行实现了。
    
     3当一个实例对象引用其他对象,当序列化该对象时也把引用的对象进行了实例化。
    

    参考链接:https://www.cnblogs.com/mkl34367803/p/10659776.html


    展开全文
  • 分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!... 任何序列化该类的尝试都会因NotSerializableException而...Java序列化是一个重要概念,但它很少用作持久性解决方案,...

    分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net

    任何序列化该类的尝试都会因NotSerializableException而失败,但这可以通过在Java中为其设置瞬态(trancient)变量来轻松解决。

    Java序列化相关的常见问题

    Java序列化是一个重要概念,但它很少用作持久性解决方案,开发人员大多忽略了Java序列化API。

    大多数商业项目使用数据库或内存映射文件或只是普通文件,来满足持久性要求,只有很少的项目依赖于Java中的序列化过程。

    对于那些不熟悉Java序列化的人,Java序列化是用来通过将对象的状态存储到带有.ser扩展名的文件来序列化Java中的对象的过程,并且可以通过这个文件恢复重建Java对象状态,这个逆过程称为deserialization。

    什么是Java序列化

    序列化是把对象改成可以存到磁盘或通过网络发送到其他运行中的Java虚拟机的二进制格式的过程,并可以通过反序列化恢复对象状态。Java序列化API给开发人员提供了一个标准机制,通过java.io.Serializable和java.io.Externalizable接口,ObjectInputStream及ObjectOutputStream处理对象序列化。Java程序员可自由选择基于类结构的标准序列化或是他们自定义的二进制格式,通常认为后者才是最佳实践,因为序列化的二进制文件格式成为类输出API的一部分,可能破坏Java中私有和包可见的属性的封装。

    如何序列化

    让Java中的类可以序列化很简单,你的Java类只需要实现java.io.Serializable接口,JVM就会把Object对象按默认格式序列化。让一个类是可序列化的需要有意为之,类可序列化会是一个长期代价,可能会因此而限制你修改或改变其实现。当你通过实现添加接口来更改类的结构时,添加或删除任何字段都可能会破坏默认序列化,这可以通过自定义二进制格式使不兼容的可能性最小化,但仍需要大量的努力来确保向后兼容性。序列化如何限制你更改类的能力的一个示例是SerialVersionUID。如果不显式声明SerialVersionUID,则JVM会根据类结构生成其结构,该结构依赖于类实现的接口和可能更改的其他几个因素。假设你新版本的类文件实现了另一个接口,JVM将生成一个不同的SerialVersionUID,当你尝试加载旧版本的程序序列化的旧对象时,你将获得无效类异常InvalidClassException。

    1.Java中的可序列化接口和可外部化接口之间的区别是什么?

    这是Java序列化访谈中最常问的问题。下面是我的版本:Externalizable给我们提供writeExternal()和readExternal()方法,这让我们灵活地控制Java序列化机制,而不是依赖于Java的默认序列化。正确实现Externalizable接口可以显著提高应用程序的性能。

    2.可序列化的方法有多少?如果没有方法,那么可序列化接口的用途是什么?

    可序列化Serializalbe接口存在于java.io包中,构成了Java序列化机制的核心。它没有任何方法,在Java中也称为标记接口。当类实现java.io.Serializable接口时,它将在Java中变得可序列化,并指示编译器使用Java序列化机制序列化此对象。

    3.什么是serialVersionUID?如果你不定义这个,会发生什么?

    serialVersionUID是一个private static final long型ID,当它被印在对象上时,它通常是对象的哈希码,你可以使用serialver这个JDK工具来查看序列化对象的serialVersionUID。SerialVerionUID用于对象的版本控制。也可以在类文件中指定serialVersionUID。不指定serialVersionUID的后果是,当你添加或修改类中的任何字段时,则已序列化类将无法恢复,因为为新类和旧序列化对象生成的serialVersionUID将有所不同。Java序列化过程依赖于正确的序列化对象恢复状态,并在序列化对象序列版本不匹配的情况下引发java.io.InvalidClassException无效类异常。

    4.序列化时,你希望某些成员不要被序列化?你如何实现?

    如果你不希望任何字段是对象的状态的一部分,根据你的需要,声明它为静态或瞬态,这样就不会是在Java序列化过程中被包含在内。

    5.如果类中的一个成员未实现可序列化接口,会发生什么情况?

    如果尝试序列化实现可序列化的类的对象,但该对象包含对不可序列化类的引用,则在运行时将引发不可序列化异常NotSerializableException,在可序列化类中添加新字段时要注意。

    6.如果类是可序列化的,但其超类不是,则反序列化后从超类继承的实例变量的状态如何?

    Java序列化过程仅在对象层次都是可序列化的结构中继续,即实现Java中的可序列化接口,并且从超类继承的实例变量的值将通过调用构造函数初始化,在反序列化过程中不可反序列化超类。一旦构造函数链启动,就不可能停止,因此,即使层次结构中较高的类实现可序列化接口,也将执行构造函数。

    7.是否可以自定义序列化过程,或者是否可以覆盖Java中的默认序列化过程?

    答案是肯定的,你可以。我们都知道,对于序列化一个对象需调用ObjectOutputStream.writeObject(saveThisObject),并用ObjectInputStream.readObject()读取对象,但Java虚拟机为你提供的还有一件事,是定义这两个方法。如果在类中定义这两种方法,则JVM将调用这两种方法,而不是应用默认序列化机制。你可以在此处通过执行任何类型的预处理或后处理任务来自定义对象序列化和反序列化的行为。需要注意的重要一点是要声明这些方法为私有方法,以避免被继承、重写或重载。由于只有Java虚拟机可以调用类的私有方法,你的类的完整性会得到保留,并且Java序列化将正常工作。

    8.假设新类的超级类实现可序列化接口,如何避免新类被序列化?

    如果类的Super类已经在Java中实现了可序列化接口,那么它在Java中已经可以序列化,因为你不能取消接口,它不可能真正使它无法序列化类,但是有一种方法可以避免新类序列化。为了避免Java序列化,你需要在类中实现writeObject()和readObject()方法,并且需要从该方法引发不可序列化异常NotSerializableException。这是自定义Java序列化过程的另一个好处。

    9.在Java中的序列化和反序列化过程中要用到哪些方法?

    Java序列化由java.io.ObjectOutputStream类完成。该类是一个筛选器流,它封装在较低级别的字节流中,以处理序列化机制。要通过序列化机制存储任何对象,我们调用ObjectOutputStream.writeObject(savethisobject),要反序列化该对象,我们调用ObjectInputStream.readObject()方法。调用writeObject()方法在Java中触发序列化过程。关于readObject()方法,需要注意的一点且是很重要一点是,它用于持久性读取字节,并从这些字节创建对象,并返回一个对象,该对象需要强制类型转换为正确的类型。

    10.假设你有一个类,它序列化并存储在持久性存储中,然后修改了该类以添加新字段。如果对已序列化的对象进行反序列化,会发生什么情况?

    这取决于类是否具有其自己的serialVersionUID。正如我们从上面的问题知道,如果我们不提供serialVersionUID,则Java编译器将生成它,通常它等于对象的哈希代码。通过添加任何新字段,有可能为该类新版本生成的新serialVersionUID与已序列化的对象不同,在这种情况下,Java序列化API将引发java.io.InvalidClassException,因此建议在代码中拥有自己的serialVersionUID,并确保在单个类中始终保持不变。

    11.Java序列化机制中的兼容更改和不兼容更改是指什么?

    真正的挑战在于通过添加任何字段、方法或删除任何字段或方法来更改类结构,方法是使用已序列化的对象。根据Java序列化规范,添加任何字段或方法都面临兼容的更改和更改类层次结构或取消实现的可序列化接口,有些接口在非兼容更改下。对于兼容和非兼容更改的完整列表,建议阅读Java序列化规范。

    12.我们可以通过网络传输一个序列化的对象吗?

    是的,你可以通过网络传输序列化对象,因为Java序列化对象仍以字节的形式保留,字节可以通过网络发送。你还可以将序列化对象存储在磁盘或数据库中作为Blob。

    13.在Java序列化期间,哪些变量未被序列化?

    这个问题问得不同,但目的还是一样的,Java开发人员是否知道静态和瞬态变量的细节。由于静态变量属于类,而不是对象,因此它们不是对象状态的一部分,因此在Java序列化过程中不会保存它们。由于Java序列化仅保留对象的状态,而不是对象本身。瞬态变量也不包含在Java序列化过程中,并且不是对象的序列化状态的一部分。

    展开全文
  • 密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存...
  • Java序列化实现原理研究

    万次阅读 多人点赞 2018-05-31 17:25:39
    1.什么是序列化和反序列化 序列化 是指将Java对象保存为二进制字节码的过程。 反序列化 将二进制字节码重新转成Java对象的过程。 2.为什么序列化 我们知道,一般Java对象的生命周期比Java...
  • Java】对象的序列化和克隆详解

    千次阅读 2019-05-20 23:20:48
    虽然知道他们的作用是对象序列化,但是具体的功能却还是一知半解,所以花了些时间去系统地了解了一下他们。 正文 克隆也经常被称为拷贝(copy),比如很多面试官都会问深拷贝和浅拷贝,就是深克隆和浅克隆。 序列化...
  • java 序列化机制和自定义序列化

    千次阅读 2019-04-08 17:02:24
    序列化是将对象保存在磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的java对象转成与平台无关的二进制流,从而将这种二进制文件持久的保存在磁盘上。其他程序一旦获得了这种二进制的流。就可以将...
  • java序列化和反序列化,面试必备

    千次阅读 多人点赞 2020-04-26 17:04:19
    意义:序列化机制允许将实现序列化Java对象转换为字节序列,并将字节序列保存在磁盘中,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使地对象可以脱离程序的运行而独立存在。 使用场景:所有在网络上...
  • 序列化是从一个对象(Object)转化为一个字节流(byte stream)的过程。而反序列化恰恰相反,是在内存中使用字节流构建一个确切的 Java 对象的过程。 2. 序列化与反序列化 Java 序列化的过程是与平台无关的...
  • (1)Java序列化是指把Java对象转换为字节序列的过程,而Java序列化是指把字节序列恢复为Java对象的过程; (2)**序列化:**对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。...
  • Java内部类序列化

    千次阅读 2018-12-27 15:10:02
    类所有的成员属性实现序列化接口Serializable Java的嵌套类(nested class)一共有四种: static nested class 静态嵌套类  inner class 内部类(非静态)  local class 本地类(定义在方法内部)  anonymous cla....
  • Java序列化的几种方式

    千次阅读 2019-04-08 17:44:05
    序列化和反序列化 序列化:可以将对象转化成一个字节序列,便于存储。 反序列化:将序列化的字节序列还原 优点:可以实现对象的”持久性”, 所谓持久性就是指对象的生命周期不取决于程序。 原生序列化方式 序列化...
  • 在深拷贝与浅拷贝中,提到可以采用「序列化与反序列化」的方式来...在Java中,序列化是指将Java对象转换为字节序列的过程,而反序列化是指将字节序列转换为Java对象的过程。其中,字节序列即是二进制数据,可以方便...
  • 序列化就是一种用来处理对象流的...序列化是为了解决在对对象流进行读写操作时所引发的问题。 序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标
  • 什么是Java序列化

    千次阅读 2020-06-27 21:14:51
    Java序列化一、序列化的含义、意义及使用场景二、序列化实现的方式2.1 Serializable(1)普通序列化(2)成员引用序列化(3)同一对象序列化多次的机制(4)java序列化算法潜在的问题(5)可选的自定义序列化...
  • java中类的序列化

    2017-02-23 17:58:17
     序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复...
  • java序列化详解

    万次阅读 多人点赞 2018-08-13 15:45:31
    序列化:指堆内存中的java对象数据,通过某种方式把对存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转化成二进制的过程。 即将对象转化为二进制,用于...
  • 序列化是为了解决在对象流进行读写操作时所引发的问题。 序列化的实现: 将需要被序列化的类实现 Serializable 接口,该接口没有需要实现的方法,implements Serializable 只是为了标注该对象是可...
  • 序列化和反序列化的底层实现原理是什么?

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

    千次阅读 2019-06-12 16:34:50
    (1)Java序列化是指把Java对象转换为字节序列的过程,而Java序列化是指把字节序列恢复为Java对象的过程; (2)序列化:对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传...
  • Java序列化工具 gadgetinspector 初窥

    千次阅读 2019-09-18 10:21:16
    作者:Longofo@知道创宇404实验室 时间:2019年9月4日 ...起因 一开始是听@Badcode师傅说的这个工具,在Black Hat 2018的一个议题...这是一个基于字节码静态分析的、利用已知技巧自动查找从source到sink的反序列化利用...
  • Java面试题大全(2020版)

    万次阅读 多人点赞 2019-11-26 11:59:06
    发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~ 本套Java面试题大全,全的不能再全,哈哈~ 一、Java 基础 1. JDK 和 JRE 有什么区别? JDK:Java ...
  • 带你了解Java序列化(Serializable)与反序列化

    千次阅读 多人点赞 2020-03-25 21:30:42
    这篇可帮助你大体了解Java中的序列化(Serializable)。包括为什么需要它,如何工作,何时使用它,相关概念(serialVersionUID和transient)以及有关序列化和反序列化的其他必要信息。本教程中的序列化示例保持简单...
  • 本文介绍了Java序列化的基本概念,序列化和反序列化的使用方法,以及实现原理等,比较全面地总结序列化相关知识点,并且使用具体例子来加以佐证。 具体代码在我的GitHub中可以找到 ...
  • 1.我们知道在java中有序列化的概念 序列化的过程就是将对象转变成字节码,反序列化即是从字节码转换成对象的过程一般情况下要求实现Serializable接口,此接口中没有定义任何成员,只是起到标记对象是否可以被序列化...
  • 1.序列化是什么 Java平台允许我们创建对象 但是只有当JVM运行时,这些对象才存在,也就是说对象的声明周期不回避JVM的生命周期长 但我们在硬盘上存储对象,或者通过网络传输对象的时候已经脱离了JVM,已经不是java...
  • java序列化之后,对象的引用关系?

    千次阅读 2014-07-24 19:44:38
    今天写代码的时候用到序列化,不过突然想到这个问题。 于是写了一些测试代码,得出两个结论。 如果两个对象存在引用关系,...如果两个对象是是另一个类对象C的成员序列化C,反序列化C之后,A和B的引用关系还存在。
  • 0 前言 全是干货的技术殿堂 文章收录在我的 GitHub 仓库,欢迎Star/fork: ...Java序列化是指把Java对象保存为二进制字节码的过程,Java序列化是指把二进制码重新转换成Java对象的过程。 那么为什么需要序列...
  • Java序列化

    千次阅读 2015-01-05 15:01:54
    通过序列化机制,我们可以把Java内存中的对象转换成二进制字节流,这样就可以把Java对象存储到磁盘中,或者在网络中传输Java对象。 1.1序列化的含义和意义 序列化机制允许将实现序列化Java对象转换成字节序列,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,129
精华内容 26,851
关键字:

java的成员是引用的序列化

java 订阅