📜  Java中带有继承的对象序列化

📅  最后修改于: 2020-03-23 01:24:24             🧑  作者: Mango

先决条件: 继承
序列化是一种将对象的状态转换为字节流的机制。反序列化是相反的过程,其中字节流用于在内存中重新创建实际的Java对象。该机制用于持久化对象。
关于继承有一些序列化的情况:
情况1:如果超类是可序列化的,则子类是可自动序列化的。如果超类是可序列化的,则默认情况下,每个子类都是可序列化的。因此,即使子类没有实现Serializable接口(并且它的超类实现了Serializable),我们也可以序列化子类对象。

// Java展示如果超类是可序列化的,则子类是可自动序列化的
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
// 超类 A
// 实现了序列化接口
class A implements Serializable
{
    int i;
    // 构造函数
    public A(int i)
    {
        this.i = i;
    }
}
// 子类 B
// 没有实现序列化接口
class B extends A
{
    int j;
    // 构造函数
    public B(int i, int j)
    {
        super(i);
        this.j = j;
    }
}
// 测试代码
public class Test
{
    public static void main(String[] args)
            throws Exception
    {
        B b1 = new B(10,20);
        System.out.println("i = " + b1.i);
        System.out.println("j = " + b1.j);
        /* 序列化B对象 */
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        // 序列化B对象方法
        oos.writeObject(b1);
        // 关闭stream
        oos.close();
        fos.close();
        System.out.println("对象被序列化");
        /* 反序列化B对象 */
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        // 反序列化B对象方法
        B b2 = (B)ois.readObject();
        // 关闭stream
        ois.close();
        fis.close();
        System.out.println("对象被反序列化");
        System.out.println("i = " + b2.i);
        System.out.println("j = " + b2.j);
    }
}

输出:

i = 10
j = 20
对象被序列化
对象被反序列化
i = 10
j = 20
  1. 如果超类不可序列化,则子类仍可以序列化:即使超类未实现Serializable接口,但如果子类本身实现Serializable接口,我们也可以对子类对象进行序列化。因此,可以说要序列化子类对象,超类不必是可序列化的。但是在这种情况下,超类实例在序列化期间会发生什么。以下过程对此进行了说明。

    当一个类可序列化但其超类不是可序列化时会发生什么?

    • 序列化:进行序列化时,如果任何实例变量都从不可序列化的超类继承,则JVM会忽略该实例变量的原始值,并将默认值保存到文件中。
    • 反序列化:反序列化时,如果存在任何不可序列化的超类,则JVM将在超类中执行实例控制流。为了在一个类中执行实例控制流,JVM将始终调用该类的默认无参数构造函数。因此,每个不可序列化的超类都必须包含默认构造函数,否则我们将获得运行时报错。
      // Java展示序列化子类时,超类不必可序列化
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.io.Serializable;;
      // 超类 A
      // A没有实现序列化接口
      class A
      {
          int i;
          // 参数化构造器
          public A(int i)
          {
              this.i = i;
          }
          // 默认构造器
          public A()
          {
              i = 50;
              System.out.println("A的构造器被调用");
          }
      }
      // 子类B
      // 实现序列化接口
      class B extends A implements Serializable
      {
          int j;
          // 参数化构造器
          public B(int i,int j)
          {
              super(i);
              this.j = j;
          }
      }
      // 测试代码
      public class Test
      {
          public static void main(String[] args)
                  throws Exception
          {
              B b1 = new B(10,20);
              System.out.println("i = " + b1.i);
              System.out.println("j = " + b1.j);
              // 序列化B对象
              FileOutputStream fos = new FileOutputStream("abc.ser");
              ObjectOutputStream oos = new ObjectOutputStream(fos);
      
              oos.writeObject(b1);
              // 关闭streams
              oos.close();
              fos.close();
              System.out.println("对象被序列化");
              // 反序列化B对象
              FileInputStream fis = new FileInputStream("abc.ser");
              ObjectInputStream ois = new ObjectInputStream(fis);
              
              B b2 = (B)ois.readObject();
              // 关闭streams
              ois.close();
              fis.close();
              System.out.println("对象被反序列化");
              System.out.println("i = " + b2.i);
              System.out.println("j = " + b2.j);
          }
      }

      输出:

      i = 10
      j = 20
      对象被序列化
      A的构造器被调用
      对象被反序列化
      i = 50
      j = 20

  2. 如果超类是可序列化的,但是我们不希望子类被序列化:在Java中,没有直接的方法可以防止子类被序列化。程序员实现此目标的一种可能方法是在子类中实现writeObject()readObject()方法,并且需要从这些方法中抛出NotSerializableException异常。这些方法分别在序列化和反序列化期间执行。通过重写这些方法,我们仅实现了自己的自定义序列化。
    // Java程序展示如何防止子类被序列化
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.NotSerializableException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    // 超类A
    // 实现序列化接口
    class A implements Serializable
    {
        int i;
        // 参数化构造器
        public A(int i)
        {
            this.i = i;
        }
    }
    // 子类B
    // B没有实现序列化接口
    class B extends A
    {
        int j;
        // 构造器
        public B(int i,int j)
        {
            super(i);
            this.j = j;
        }
        // 通过实现writeObject方法,
        // 我们可以阻止子类实现序列化
        private void writeObject(ObjectOutputStream out) throws IOException
        {
            throw new NotSerializableException();
        }
        // 通过实现readObject方法,
        // 我们可以阻止子类实现反序列化
        private void readObject(ObjectInputStream in) throws IOException
        {
            throw new NotSerializableException();
        }
    }
    // 测试代码
    public class Test
    {
        public static void main(String[] args)
                throws Exception
        {
            B b1 = new B(10, 20);
            System.out.println("i = " + b1.i);
            System.out.println("j = " + b1.j);
            // 序列化B对象
            //Saving of object in a file
            FileOutputStream fos = new FileOutputStream("abc.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
    
            oos.writeObject(b1);
            // 关闭streams
            oos.close();
            fos.close();
            System.out.println("对象被序列化");
            // 反序列化B对象
            FileInputStream fis = new FileInputStream("abc.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);
    
            B b2 = (B)ois.readObject();
            // 关闭streams
            ois.close();
            fis.close();
            System.out.println("对象被反序列化");
            System.out.println("i = " + b2.i);
            System.out.println("j = " + b2.j);
        }
    }

    输出:

    i = 10
    j = 20
    Exception in thread "main" java.io.NotSerializableException
        at B.writeObject(Test.java:44)