小问题7 | Java序列化的应用场景、序列化方法及代码示例

小问题7 | Java序列化的应用场景、序列化方法及代码示例

前置问题:小问题6 | Java中如何实现深拷贝?实现深拷贝的三种方法

Java序列化是指把Java对象转换为字节序列的过程。

Java序列化的应用场景

Java中的序列化主要应用于以下场景:

  • 对象状态持久化 - 将对象保存到文件或数据库中
  • 网络传输优化 - 通过序列化减少网络传输数据量
  • RPC等的应用基础 - 远程过程调用需要以流的形式处理请求和响应

下面介绍Java序列化的方法:

Java序列化的方法
1. Serializable接口

一个类的对象要想序列化,必须先实现Serializable接口:Serializable接口只是一个标识,不需要实现任何方法。

2. transient关键字

transient关键字可以防止个别敏感字段被序列化,字段加了transient后就不会被序列化。

3. Externalizable接口

通过实现Externalizable接口可以自定义序列化逻辑:

在writeExternal方法中可以只序列化需要的字段,自定义序列化逻辑。

在readExternal方法中可以根据写入顺序读取数据,重建对象。

这样通过Externalizable接口实现的自定义序列化可以只序列化必要字段,提高效率。并可以在序列化与反序列化中加入自定义处理逻辑。

注意,static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。

下面是一个代码示例:

public class Person implements Externalizable {
    public Person(){

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private String name;
    private int age;

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("开始执行自定义的序列化方法:writeExternal");
        out.writeUTF(name);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("开始执行自定义的序列化方法:readExternal");
        this.name = in.readUTF();
        this.age = in.readInt();
    }

}
Java序列化的应用示例

下面的代码示例通过将对象保存到文件中并读取,演示Java序列化将对象持久化到文件系统中的作用,其中的Person类使用上文实现Externalizable的类:

public static void main(String[] args) {
    try {
        // 对象输出流
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("E:\\test序列化.txt")));
        // 对象
        Person person = new Person();
        person.setName("张三");
        person.setAge(20);
        // 序列化对象
        out.writeObject(person);
        // 关闭流
        out.close();

        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("E:\\test序列化.txt")));
        Person p2 = (Person) ois.readObject();
        ois.close();

        // 验证反序列化成功
        System.out.println(p2.getName());
        System.out.println(p2.getAge());
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

执行结果:

在这里插入图片描述

然而,序列化存在一些局限性,我们在实际应用中,不推荐使用JDK自带的序列化

为什么不推荐使用 JDK 自带的序列化?

我们很少或者说几乎不会直接使用 JDK 自带的序列化方式,主要原因有下面这些原因:

  • 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
  • 性能差:相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。并且Java序列化需要将对象转换为字节流的形式,序列化和反序列化时需要进行大量的IO操作,效率较低。
  • 存在安全问题:序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
  • 版本兼容性问题:由于Java序列化序列化的是对象状态,而不是对象结构,因此当对象类定义发生变化时,可能会导致反序列化失败或出现意外的结果。

是的,有朋友可能产生新的问题,既然JDK自带的序列化是不被推荐的,那么——

除了JDK序列化以外,有哪些序列化框架?该如何选型?

我们带着问题继续看。

————————————————————

本专栏是【小问题】系列,旨在每篇解决一个小问题,并秉持着刨根问底的态度解决这个问题可能带出的一系列问题。

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值