Java中的序列化与反序列化——Externalizable(三)

本文介绍了Java的Externalizable接口,它是Serializable接口的子接口,用于提供更细粒度的序列化控制。实现Externalizable需手动编写writeExternal和readExternal方法。文章通过示例展示了如何使用该接口进行序列化和反序列化操作,并强调了在继承关系中处理超类字段的重要性,以及使用Externalizable需要注意的点。
摘要由CSDN通过智能技术生成

1、概述

大家好,我是欧阳方超。在上一次的的博文中——Java中的序列化与反序列化——transient(二),我们提到,在 Java 中,如果一个类想要实现序列化,需要实现Serializable 接口并定义一个 serialVersionUID 字段。然而,如果想要更加灵活地控制序列化过程,可以使用 Externalizable 接口。今天我们就来具体看一下Externalizable。

2、Externalizable 接口

2.1、Externalizable的使用

首先需要注意的是,Externalizable也是一个接口,它继承自 Serializable 接口,并且在序列化和反序列化时提供了更细粒度的控制。与 Serializable 接口不同的是,实现 Externalizable 接口需要手动实现 writeExternal() 和 readExternal() 方法,这两个方法负责实现对象的序列化和反序列化过程。下面是一个实现 Externalizable 接口的示例:

import java.io.*;

public class Person implements Externalizable {
    private String name;
    private int age;

    public Person() {
        // 必须提供一个默认的构造函数
    }

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

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = in.readInt();
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

在上面的示例中,Person 类实现了 Externalizable 接口,并覆盖了 writeExternal() 和 readExternal() 方法,分别负责将对象写入输出流和从输入流读取对象。注意,为了实现 Externalizable 接口,必须提供一个默认的构造函数。下面的代码中创建Person类的对象并对其进行序列化及反序列化操作:

import java.io.*;

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Tom", 19);

        //序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\home\\person.ser"))) {
            person.writeExternal(oos);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\home\\person.ser"))) {
            Person loaded = new Person();
            loaded.readExternal(ois);
            System.out.println(loaded.getName() + " " + loaded.getAge());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

使用ObjectOutputStream将person对象序列化到person.ser文件中。在接下来的程序中,使用ObjectInputStream从person.ser中恢复Person对象loaded。通过调用 loaded.readExternal() 来反序列化对象,并通过获取name和age属性来确保被正确重建。程序输出结果为:

Tom 19

2.2、在继承上的处理

当一个类实现了 Externalizable 接口并且它的超类也实现了 Externalizable 接口时,需要手动序列化和反序列化超类的字段。
具体来说,我们需要在实现 writeExternal() 和 readExternal() 方法时,显式地调用超类的 writeExternal() 和 readExternal() 方法,将超类的字段写入输出流或从输入流中读取超类的字段。
以下是一个示例,假设我们有一个 Person 类继承自 User 类,它们都实现了 Externalizable 接口:

import java.io.*;

public class User implements Externalizable {
    private String username;

    public User() {}

    public User(String username) {
        this.username = username;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(username);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        username = (String) in.readObject();
    }

    public String getUsername() {
        return username;
    }
}

public class Person extends User implements Externalizable {
    private int age;

    public Person() {}

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

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        super.writeExternal(out);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        super.readExternal(in);
        age = in.readInt();
    }

    public int getAge() {
        return age;
    }
}

在上面的示例中,Person 类继承自 User 类,并分别实现了 writeExternal() 和 readExternal() 方法。在 writeExternal() 方法中,我们首先调用了超类 User 的 writeExternal() 方法,将 username 字段写入输出流。然后,我们再将 age 字段写入输出流。
在 readExternal() 方法中,我们首先调用了超类 User 的 readExternal() 方法,从输入流中读取 username 字段。然后,我们再从输入流中读取 age 字段。
通过这种方式,我们可以手动序列化和反序列化超类的字段,确保在序列化和反序列化时超类的字段也能正确地被处理。

2.3、要点

使用 Externalizable 接口需要注意以下几点:

  1. Externalizable 接口的序列化和反序列化过程需要手动实现,相对于 Serializable 接口会更加复杂和繁琐。
  2. 与 Serializable 接口不同的是,Externalizable 接口中的默认构造函数必须是 public 的,否则在反序列化时会抛出 InvalidClassException 异常。
  3. 对于实现了 Externalizable 接口的类,如果它的超类也实现了 Externalizable 接口,则需要在子类中手动序列化和反序列化超类的字段。

3、总结

Externalizable接口提供了一种比简单的Serializable更灵活和可控的序列化机制。它使您可以在需要反序列化时采取不同的行动,而不是简单地遵循官方的序列化方法。希望这篇博客有助于大家更好地理解Externalizable和在项目中使用它。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值