(4)什么是序列化与反序列化、序列化底层原理、为什么说序列化并不安全

序列化是Java 比较基础的一个知识点,现在问: 序列化的底层实现原理是什么? 你会怎么答呢?

基本概念:

序列化: 将对象转化成字节序列的过程。用于对象的传输,和持久化。

反序列化: 与序列化相反,将字节序列转换成对象的过程。

为什么使用序列化?

当俩个进程进行远程通信时,可以相互发送各种类型的数据,比如图片,文字,视频等,而这些数据都会以二进制的形式在网络上传送。

那么当俩个Java进程通信的时,能否实现进程间的对象传送呢?这就需要Java 序列化和反序列化了!

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

使用序列化的好处?

其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),

二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。

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

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

如何实现序列化和反序列化?

package com.test.model;

import io.swagger.annotations.ApiModelProperty;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @Author tanghh
 * 节点(用来生成树形关系)
 * @Date 2019/12/1 16:04
 */
public class TestNode implements Serializable {
    //节点优先级
    private static String nodeHigher="低";
    @ApiModelProperty(value = "序号")
    private Integer id;
    @ApiModelProperty(value = "父节点")
    private Integer parentId;
    @ApiModelProperty(value = "节点名称")
    private String nodeName;
    @ApiModelProperty(value = "节点类型")
    private transient String nodeType;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    public String getNodeName() {
        return nodeName;
    }

    public void setNodeName(String nodeName) {
        this.nodeName = nodeName;
    }


    public String getNodeType() {
        return nodeType;
    }

    public void setNodeType(String nodeType) {
        this.nodeType = nodeType;
    }

    public static String getNodeHigher() {
        return nodeHigher;
    }

    public static void setNodeHigher(String nodeHigher) {
        TestNode.nodeHigher = nodeHigher;
    }

    @Override
    public String toString() {
        return "TestNode{" +
                "id=" + id +
                ", parentId=" + parentId +
                ", nodeName='" + nodeName + '\'' +
                ", nodeType='" + nodeType + '\'' +
                ", nodeHigher='" + nodeHigher + '\'' +
                '}';


    }
}
package com.test.nodefault;

import com.test.model.TestNode;

import java.io.*;

/**
 * @Author tanghh
 * 测试序列化和非序列化
 * @Date 2020/2/23 9:24
 */
public class TestNodeResult {
    public static void main(String[] args) throws Exception {
        serializeFlyPig();
        TestNode node = deserializeFlyPig();
        System.out.println(node.toString());

    }

    /**
     * 序列化
     */
    private static void serializeFlyPig() throws IOException {
        TestNode node = new TestNode();
        node.setNodeName("父节点");
        node.setParentId(0);
        node.setNodeType("第三子节点");
        // ObjectOutputStream 对象输出流,将 flyPig 对象存储到E盘的 flyPig.txt 文件中,完成对 flyPig 对象的序列化操作
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/flyPig.txt")));
        oos.writeObject(node);
        System.out.println(" 对象序列化成功!");
        oos.close();
    }

    /**
     * 反序列化
     */
    private static TestNode deserializeFlyPig() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/flyPig.txt")));
        TestNode person = (TestNode) ois.readObject();
        System.out.println(" 对象反序列化成功!");
        return person;
    }
}

测试结果:

 

serialVersionUID 的作用和用法:

首先,你可以不用自己去赋值,Java会给你赋值,但是,这个就会出现上面的bug,很不安全,所以,还得自己手动的来。
那么,我该怎么赋值,eclipse可能会自动给你赋值个一长串数字。这个是没必要的。
可以简单的赋值个 1L,这就可以啦。。这样可以确保代码一致时反序列化成功。
不同的serialVersionUID的值,会影响到反序列化,也就是数据的读取,你写1L,注意L大些。计算机是不区分大小写的,但是,作为观众的我们,是要区分1和L的l,所以说,这个值,闲的没事不要乱动,不然一个版本升级,旧数据就不兼容了,你还不知道问题在哪。。。

注意事项:

  • 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、如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;
  • 9.类的对象序列化后,类的序列化ID不能轻易修改,不然反序列化会失败。

序列化不安全

(如序列化存储安全、反序列化安全、传输安全等)

反序列化存在安全问题:https://www.jianshu.com/p/fa912ce0426f

参考文章

https://blog.csdn.net/xlgen157387/article/details/79840134

https://blog.csdn.net/u013870094/article/details/82765907

https://blog.csdn.net/Woo_home/article/details/104122068?utm_source=app

发布了54 篇原创文章 · 获赞 49 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/tangthh123/article/details/104492430