Java序列化为什么要实现Serializable?

111 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

前言

在java中对象想要序列化一般都会实现Serializable接口,但是我们发现Serializable接口中并没有任何方法:

public interface Serializable {
}

那么它是如何实现对象序列化和反序列化的呢?

序列化实现

我们来一段代码看看如何序列化一个对象:

static class Person implements Serializable {
private String name;
private int age;
​
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
​
public static void main(String[] args) throws Exception {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("person.txt"));
outputStream.writeObject(new Person("zouwei"20));
outputStream.close();
}

对象在实现了序列化接口Serializable后,我们就可以使用ObjectOutputStream把这个对象序列化成二进制数据了;最终拿到的二进制文件内容如下:

aced 0005 7372 003c 636f 6d2e 6578 616d
706c 652e 6177 6573 6f6d 6572 6f63 6b65
746d 712e 636f 6e74 726f 6c6c 6572 2e54
6573 7443 6f6e 7472 6f6c 6c65 7224 5065
7273 6f6e fe5a c16f 6caf e98b 0200 0249
0003 6167 654c 0004 6e61 6d65 7400 124c
6a61 7661 2f6c 616e 672f 5374 7269 6e67
3b78 7000 0000 1474 0006 7a6f 7577 6569

如果没有实现序列化接口Serializable,在代码运行时将报错:

Exception in thread "main" java.io.NotSerializableException: com.example.Person
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
at com.example.main(TestController.java:78)

这个报错的意思就是Person类没有实现Serializable接口;

Serializable如何起作用

我们可以查看一下ObjectOutputStream.writeObject()源码,里面有这么一段代码:

else if (obj instanceof Serializable) {
   // obj属于Serializable类型,才能序列化
   writeOrdinaryObject(obj, desc, unshared);
}else {
   // 否则抛出NotSerializableException异常
   if (extendedDebugInfo) {
       throw new NotSerializableException(
                       cl.getName() + "\n" + debugInfoStack.toString());
  } else {
       throw new NotSerializableException(cl.getName());
  }
}

所以我们发现Serializable接口其实就是一个标签,它是用来告知序列化工具,这个对象是可以进行序列化操作;

除了通过实现Serializable接口来达到序列化的目的,java还提供了Externalizable接口让我们实现自定义序列化操作:

public interface Externalizable extends java.io.Serializable {
 
   void writeExternal(ObjectOutput out) throws IOException;
​
   void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

我们依然使用Person类来举例子:

static class Person implements Externalizable {
private String name;
private int age;
private String idCardNumber;
​
public Person(String name, int age, String idCardNumber) {
this.name = name;
this.age = age;
this.idCardNumber = idCardNumber;
}
​
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.name);
out.writeObject(this.age);
}
​
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = (String) in.readObject();
this.age = (int) in.readObject();
}
}

通过实现Externalizable接口,我们在序列化的时候特地没有将字段idCardNumber序列化进去,因为在实际业务当中,有一些数据因为敏感性原因导致不允许序列化,那么我们就可以通过实现Externalizable来自己处理;

另外值得一提的是,如果字段被transient关键字修饰了的话,也是不会被序列化的;