Java对象的序列化与反序列化

    |     2015年3月30日   |   Java面向对象高级特性   |     0 条评论   |    1587

两个进程远程通信时,无论发送什么类型的数据,网络上走的都是二进制序列。发送方要把 Java 对象变成字节序列,接收方再把字节序列恢复成对象。

把 Java 对象转换为字节序列叫做序列化;把字节序列恢复为 Java 对象叫做反序列化。常见用途有两个:把对象的字节序列永久存到磁盘(通常是一个文件);在网络上传送对象。

接口 谁控制过程 反序列化时
Serializable 默认机制;可自定义 writeObject / readObject 不调用构造方法
Externalizable 类自己实现 writeExternal / readExternal 先调用 public 无参构造

一、JDK 类库中的序列化 API

java.io.ObjectOutputStream 是对象输出流,writeObject(Object obj) 把指定对象序列化并写入目标流。java.io.ObjectInputStream 是对象输入流,readObject() 从输入流读字节序列并还原为对象。

只有实现了 Serializable 或 Externalizable 的类,其对象才能被序列化。Externalizable 继承自 Serializable:实现它的类完全自己控制序列化行为;只实现 Serializable 的类走默认方式。

序列化步骤:创建对象输出流(可包装文件输出流等),再调用 writeObject()。反序列化:创建对象输入流,再调用 readObject()。写入和读取的顺序必须一致。

import java.io.*;
import java.util.Date;

public class ObjectSaver {
    public static void main(String[] args) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("objectFile.obj"));
        Customer customer = new Customer("阿蜜果", 24);
        out.writeObject("你好!");
        out.writeObject(new Date());
        out.writeObject(customer);
        out.writeInt(123);  // 写入基本类型数据
        out.close();

        ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("objectFile.obj"));
        System.out.println("obj1=" + (String) in.readObject());
        System.out.println("obj2=" + (Date) in.readObject());
        Customer obj3 = (Customer) in.readObject();
        System.out.println("obj3=" + obj3);
        int obj4 = in.readInt();
        System.out.println("obj4=" + obj4);
        in.close();
    }
}

class Customer implements Serializable {
    private String name;
    private int age;
    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return "name=" + name + ", age=" + age;
    }
}

输出类似:obj1=你好!;obj2=某个 Date;obj3=name=阿蜜果, age=24;obj4=123。

二、实现 Serializable 接口

ObjectOutputStream 只能序列化实现了 Serializable 的对象。默认方式只序列化非 transient 的实例变量,不序列化 transient 字段,也不序列化静态变量。

按默认方式反序列化时:若内存里该类尚未加载,会先加载并初始化;classpath 里没有对应 class 文件则抛 ClassNotFoundException。反序列化不会调用类的任何构造方法。

若要自己控制过程,可提供下面两个私有方法。对象有 writeObject 时就会走它,否则走默认。方法里可以先调 defaultWriteObject() 做默认序列化,再补自己的逻辑;反序列化对应 defaultReadObject()。

private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException

对象里若有敏感信息,按默认方式序列化后在网络上传输可能被截获,可以先加密再写入,读回来再解密。默认序列化会递归遍历整个对象图,图很复杂时又占空间又耗时。把不需要持久化的成员标成 transient,能缩小体积、提高性能。

三、实现 Externalizable 接口

Externalizable 继承 Serializable。实现它之后,序列化行为完全由这个类自己负责。接口声明两个方法:前者负责写,后者负责读。

public void writeExternal(ObjectOutput out) throws IOException
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException

反序列化实现了 Externalizable 的对象时,会先调用类的public 无参构造方法,这和 Serializable 的默认方式不同。若删除无参构造,或把它改成 private、默认、protected,会抛出 java.io.InvalidClassException: no valid constructor。

一句话总结:Serializable 走默认(可加 transient / 自定义读写),Externalizable 完全自己写;两者都是为了把对象变成字节再还原回来。

转载请注明来源:Java对象的序列化与反序列化
本文链接地址:https://ai.zhousir.top/?p=100
回复 取消