Entity实体类为什么要实现Serializable接口才能被序列化?

引起这个疑问,还是从Hibernate使用查询缓存说起;对象实例除了存在于内存,二级缓存还会将对象写进硬盘在需要的时候再读取出来使用,此时就必须提到一个概念:序列化。

程序在运行时实例化出对象,这些对象存在于内存中,随着程序运行停止而消失,但如果我们想把某些对象(一般都是各不相同的属性)保存下来或者传输给其他进程,在程序终止运行后这些对象仍然存在,可以在程序再次运行时读取这些对象的信息,或者在其他程序中利用这些保存下来的对象信息恢复成实例对象。这种情况下就要用到对象序列化反序列化

其实很早就知道的,在Java中常见的几个类,如:Interger/String等,都实现了java.io.Serializable接口。这个序列化接口没有任何方法和域,仅用于标识序列化语意;实现 Serializable 接口的类是可序列化的,没有实现此接口的类将不能被序列化和反序列化。序列化类的所有子类本身都是可序列化的,不再需要显式实现 Serializable 接口。只有经过序列化,才能兼容对象在磁盘文本以及在网络中的传输,以及恢复对象的时候反序列化等操作。

问题一:为何要实现序列化?

答:序列化就是对实例对象的状态(State 对象属性而不包括对象方法)进行通用编码(如格式化的字节码)并保存,以保证对象的完整性和可传递性。

简而言之:序列化,就是为了在不同时间或不同平台的JVM之间共享实例对象

// 经常使用如下:  
public static void main(String[] args) throws Exception {  
    File file = new File("user.ser");  
  
    ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));  
    User user = new User("zhang", 18, Gender.MALE);  
    oout.writeObject(user);  
    oout.close();  
  
    ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));  
    Object newUser = oin.readObject();  
    oin.close();  
    System.out.println(newUser);  
}  

如没有 实现Serializable接口,在序列化时,使用ObjectOutputStream的write(object)方法将对象保存时将会出现异常。其实 java.io.Serializable 只是一个没有属性和方法的空接口,但是问题来了。。。


问题二:为何一定要实现 Serializable 才能进行序列化呢?

使用 ObjectOutputStream 来持久化对象, 对于此处抛出的异常,查看该类中实现如下:

private void writeObject0(Object obj, boolean unshared) throws IOException {  
  // ...  
          // remaining cases  
          if (obj instanceof String) {  
              writeString((String) obj, unshared);  
          } else if (cl.isArray()) {  
              writeArray(obj, desc, unshared);  
          } else if (obj instanceof Enum) {  
              writeEnum((Enum) obj, desc, unshared);  
          } else if (obj instanceof Serializable) {  
              writeOrdinaryObject(obj, desc, unshared);  
          } else {  
              if (extendedDebugInfo) {  
                  throw new NotSerializableException(  
                      cl.getName() + "\n" + debugInfoStack.toString());  
              } else {  
                  throw new NotSerializableException(cl.getName());  
              }  
          }  
  // ...  
}  

从此可知,如果被写对象类型是String、数组、Enum、Serializable,就可以进行序列化,否则将抛出NotSerializableException。

最后提点注意:

  1. 在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,如此引用传递序列化。如果一个对象包含的成员变量是容器类等并深层引用,那么序列化过程开销也较大。
  2. 当字段被声明为 transient 后,默认序列化机制就会忽略该字段。(还有方法就是自定义writeObject方法,见下代码示例)
  3. 在单例类中添加一个readResolve()方法(直接返回单例对象),以保证在序列化过程仍保持单例特性。

此外补充一下,
在路径下jdk中还有另外一种形式的对象持久化,即:外部化(Externalization)。

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

外部化和序列化是实现同一目标的两种不同方法。


通过 Serializable 接口对对象序列化的支持是jdk内支持的 API ,但是java.io.Externalizable的所有实现者必须提供读入和写出的具体实现,怎么实现完全由你自定义。序列化(Serializable )会自动存储所有必要的信息(如属性以及属性类型等),用以反序列化成原来一样的实例,而外部化(Externalizable)则只保存被存储实例中你需要的信息。

示例代码如下:

public class User implements <strong>Externalizable</strong> {  
    private String name;  
    <strong>transient</strong> private Integer age;  // 屏蔽字段  
    private Gender gender;  
  
    <strong>public User</strong>() {  
        System.out.println("none constructor");  
    }  
  
    public User(String name, Integer age, Gender gender) {  
        System.out.println("arg constructor");  
        this.name = name;  
        this.age = age;  
        this.gender = gender;  
    }  
  
    // 实现读写  
    private void <strong>writeObject</strong>(ObjectOutputStream out) throws IOException {  
        out.defaultWriteObject();  
        out.writeInt(age);  
        // 屏蔽gender  
    }  
    private void <strong>readObject</strong>(ObjectInputStream in) throws IOException, ClassNotFoundException {  
        in.defaultReadObject();  
        age = in.readInt();  
    }  
  
    // 具体重写  
    @Override  
    public void <strong>writeExternal</strong>(ObjectOutput out) throws IOException {  
        out.writeObject(name);  
        out.writeInt(age);  
        // 屏蔽gender  
    }  
    @Override  
    public void <strong>readExternal</strong>(ObjectInput in) throws IOException, ClassNotFoundException {  
        name = (String) in.readObject();  
        age = in.readInt();  
    }   
}  

注意,用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。实现Externalizable接口的类必须要提供一个无参的构造器,且访问权限为 public。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值