2021.6.23:序列化/Serialization

概念

关于序列化,我曾在Python序列化一节说过,现在再复习一遍。

序列化:内存中的数据(我们看到的Java对象)变为存储/传输时的二进制数据,即byte [ ] 数组

反序列化:把之前的byte[ ]数组重新变成Java对象

 

模块:java.io.Serializable

这是一个接口,并且其中没有任何方法,即空接口。这样的空接口称之为“标记接口”( Marker Interface )实现标记接口的类只是给自身贴了个“标记”,并没有增加任何方法

 

序列化

java.io.ObjectOutputStream

ObjectOutputStream将一个Java对象转化为byte[ ]。它负责把一个Java对象写入一个字节流

import java.io.*;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try(ObjectOutputStream output = new ObjectOutputStream(buffer)){
            //写入int、String、Object
            output.writeInt(12345);
            output.writeUTF("Hello");
            output.writeObject(Double.valueOf(123.456));
        }
        System.out.println(Arrays.toString(buffer.toByteArray()));
    }
}

[-84, -19, 0, 5, 119, 11, 0, 0, ... ,-108, -32, -117, 2, 0, 0, ... , 119]

ObjectOutputStream可以写入基本类型、String、实现了Serializable接口的Object

由于写入Object类型需要大量类型信息,所以写入的内容很多。

反序列化

java.io.ObjectInputStream

从一个byte[ ]读取Java对象

    try(ObjectInputStream input = new ObjectInputStream(...)){
        int n = input.readInt();
        String s = input.readUTF();
        Double d = (Double) input.readObject();
    }

除了可以直接读取基本类型String之外,调用readObject()可以直接返回一个Object。需要通过强制转换将之转化为特定类型。

readObject()可能抛出的异常:ClassNotFoundException未找到对应ClassInvalidClassExceptionClass不匹配

ClassNotFoundException

在一台PC上的一个Java程序中,处理在另一台电脑上写的Java程序,但是这台电脑上却没有在另一台电脑上定义的类,比如Person,所以无法反序列化。

InvalidClassException

序列化的Person有一个int类型的age字段,但是反序列时,将Person类型定义的age字段改成了long类型,所以导致class不兼容。

为了避免这种class定义变动导致的不兼容,Java序列化允许class定义一个特殊的serialVersionUID静态变量,用于标识Java类序列化版本,通常可以由IDE自动生成

如果增加或者修改了字段,可以修改serialVersionUID的值,这样就能自动阻止不匹配的class版本

public class Person implements Serializable{
    private static final long serialVersionUID=2709425254321887;
}

注意

反序列化时,由JVM直接构造Java对象不调用构造方法,因此构造方法内部的代码,在反序列化时根本不会执行

安全性

因为Java的序列化机制可以导致一个实例能直接从byte[ ]创建,而不经过构造方法,因此它存在一定的安全隐患

更好的序列化方法是通过JSON这样的通用数据结构来实现,只输出基本类型String的内容,而不存储任何代码相关的内容。

此外,Java序列化机制只适用于Java,如果要和其它语言交换数据,需要使用通用序列化方法,如JSON

posted @ 2021-06-24 14:51  ShineLe  阅读(52)  评论(0)    收藏  举报