Java - 序列化、反序列化及Serializable接口

一 序列化 & 反序列化

    1、序列化:把对象转换为字节序列的过程称为对象的序列化

    2、反序列化:把字节序列恢复为对象的过程称为对象的反序列化.

二 什么场景会涉及序列化 & 反序列化

    1、持久化内存空间中的数据至 物理磁盘 或 数据库 以便长期保存时;

    2、前后端数据交互;

    3、两个进程间的远程通信调用 eg:RPC框架;

    补充说明第二点:服务器与浏览器间数据交互时也会存在序列化的过程,Json实际上就是将一个对象转换为字符串String再与前端交互,而 String 源码中也实现了 Serializable 接口

三 Serializable接口

Java通过  java.io.Serializable 接口实现序列化功能, java.io.Serializable 接口中不提供任何方法或字段,仅用来标识序列化的语义,JVM会在底层帮我们实现序列化和反序列化。未实现此接口的类则无法将其进行序列化或反序列化,序列化功能支持扩展至子类,即只需父类实现了序列化接口,所有子类就可以进行序列化或反序列化。

当对一个未实现序列化接口的类进行序列化时会抛出 java.io.NotSerializableException;

四  serialVersionUID

序列化版本号,JVM在对类进行序列化时会根据类名、接口名、方法名、属性等自动生成 serialVersionUID,然后与序列化后的属性一起进行持久化或网络传输,在反序列化时JVM按照同样的规则再次生成一个 serialVersionUID 并与序列化时生成的serialVersionUID 做对比,如果两者一致则反序列化成功。

如果未显式指定 serialVersionUID,在对类序列化成功后如果新增或修改了类的属性,则在反序列化时JVM按照属性重新生成的 serialVersionUID 会与序列化时生成的不一致,处于安全机制考虑,程序会抛出 java.io.InvalidClassException stream classdesc serialVersionUID = xxxx, local class serialVersionUID = **** 不匹配异常;

如果显式指定 serialVersionUID,JVM在序列化和反序列化时仍会生成一个 serialVersionUID,但会拿显式指定的 serialVersionUID 覆盖自动生成的 serialVersionUID,这样在反序列化时新旧版本的 serialVersionUID 就一致了,此时就可以在序列化后仍可以修改类中的属性或者方法,而不会影响到后期的反序列化流程。

可以说 serialVersionUID 是序列化和反序列化之间彼此匹配的唯一口令;

测试demo:
实现序列化接口的Person实体类:

package com.pojo.model;

import java.io.Serializable;

public class Person implements Serializable{

    private static long serialVersionUID = -234234238976l;

    private String code;
    private String name;

    public Person(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "code='" + code + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

测试类:

package com.study.controller;

import com.study.pojo.model.Person;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class TestController {

    public static void main(String[] args) {
        Person p = new Person("1", "张三");

        // 序列化
        serializable(p);

        // 反序列化
        Person pp = deserializable();
        System.out.println(pp);
    }

    public static void serializable(Person p) {
        try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(new File("E:\\temp/p.txt")));) {
            output.writeObject(p);
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    public static Person deserializable() {
        try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("E:\\temp/p.txt")));) {
            return (Person)input.readObject();
        } catch(IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

}

五 序列化API

java.io.ObjectOutputStream:对象输出流,通过 writeObject(obj) 方法实现对象序列化;

java.io.ObjectInputStream:对象输入流,通过 readObject() 方法实现对象反序列化;

序列化 & 反序列化方法参见上述Demo;

六 例外

1、被 transient 关键字修饰的属性不会被序列化;

2、被 static 关键字修饰的属性不会被序列化;

源码参见:

被 static 关键字修饰的属性不会被序列化是因为被static修饰的属性属于类,随着类的加载被加载,而不属于对象,不需要创建对象去调用static修饰的属性,所以不会被序列化;

猜你喜欢

转载自blog.csdn.net/sxg0205/article/details/108370125