关于java:Java序列化素质三连是什么为什么需要如何实现

9次阅读

共计 5686 个字符,预计需要花费 15 分钟才能阅读完成。

遇到这个 Java Serializable 序列化这个接口,咱们可能会有如下的问题

  • 什么叫序列化和反序列化
  • 作用,为啥要实现这个 Serializable 接口,也就是为啥要序列化
  • 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 生成个文件。

从运行后果上看:

  • 他实现了对象的序列化和反序列化。
  • transient 润饰的属性,是不会被序列化的。我设置的奥迪四个圈的车不见啦,成了 null。my god。
  • 你先别着急说,这个动态变量 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 的形容

(留神比照一下,这个截图的两段话,就是对应上面的 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 值的要求。

写在最初

欢送大家关注我的公众号【惊涛骇浪如码】,海量 Java 相干文章,学习材料都会在外面更新,整顿的材料也会放在外面。

感觉写的还不错的就点个赞,加个关注呗!点关注,不迷路,继续更新!!!

正文完
 0