第 18 章 - java IO系统 - 序列化与反序列化 - Externalizable接口的代替,static字段不会序列化(p597)

第 18 章 - java IO系统 - 序列化与反序列化 - Externalizable接口的替代,static字段不会序列化(p597)

第 18 章 - java IO系统 - 序列化与反序列化 - Externalizable接口的替代,static字段不会序列化(p597)

 

一. 如果不想实现Externalizable接口(为什么不想?难道是因为接口方法是public的,谁都可以调用么?),

可以使用下面方法替代:

 

1. 对象依然是要实现Serializable接口.

2. 为对象添加下面的两个方法,注意方法由private修饰,方法的签名必须和下面的一样.

  

 private void writeObject(ObjectOutputStream stream) throws IOException
   private void readObject(ObjectInputStream strea) throws IOException,ClassNotFoundException

 

   

   这里的设计比较奇怪和混乱,因为private方法也能被调用了,Thinking in Java 作者对此进行了批评.

   

3. 在有了上面的两个方法后,对象序列化时,ObjectOutputStream.writeObject()方法调用时,会检查

   所传递的Serializable对象看看是否有上面的writeObject方法,如果有,则跳出正常的序列化过程,

   改成调用Serializable对象的writeObject方法.反序列化时ObjectInputStream的readObject与之

   类似.

   

   另外,可以在2中的writeObject中调用defaultWriteObject()来执行默认的序列化过程(默认的writeObject).

   

   下面的示例代码中,采用这种替代方式,使用了defaultWriteObject()来处理默认的序列化操作,而对于由transient

   修饰的字段则额外进行了处理.

   

   

package io;

import java.io.*;

public class SerialCtl implements Serializable
{
  private static final long serialVersionUID = 3587339559164696206L;
  
  private String           a;
  private transient String b;

  public SerialCtl(String aa, String bb)
  {
    a = "Not Transient: " + aa;
    b = "Transient: " + bb;
  }

  public String toString()
  {
    return a + "\n" + b;
  }

  //这个方法签名,必须是这样的.
  private void writeObject(ObjectOutputStream stream) throws IOException
  {
    stream.defaultWriteObject(); //可以按照默认的序列化程序进行
    stream.writeObject(b);
  }

  //这个方法签名,必须是这样的.
  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
  {
    stream.defaultReadObject();
    b = (String) stream.readObject();
  }

  public static void main(String[] args) throws IOException, ClassNotFoundException
  {
    SerialCtl sc = new SerialCtl("Test1", "Test2");
    System.out.println("Before:\n" + sc);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    ObjectOutputStream o = new ObjectOutputStream(buf);
    o.writeObject(sc);
    
    // Now get it back:
    ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
    SerialCtl sc2 = (SerialCtl) in.readObject();
    System.out.println("\nAfter:\n" + sc2);
  }
}

 

 

二. 静态字段不会初始化. 在一个地方序列化了对象,在另一个地方反序列化时,static字段值恢复成类定义是的默认值.   

   示例.首先,执行下面的类生成序列化文件:

   

package io;

import java.io.*;

class Student1 implements Serializable
{
  private static final long serialVersionUID = 1L;
  private String            name;
  private transient String  password;
  private static int        count            = 0;

  public Student1(String name, String password)
  {
    System.out.println("调用Student的带参的构造方法");
    this.name = name;
    this.password = password;
    count++;
  }

  public String toString()
  {
    return "人数: " + count + " 姓名: " + name + " 密码: " + password;
  }
}

public class TestStaticSerialable1
{
  public static void main(String args[])
  {
    try
    {

      FileOutputStream fos = new FileOutputStream("test.obj");
      ObjectOutputStream oos = new ObjectOutputStream(fos);

      Student1 s1 = new Student1("张三", "12345");
      Student1 s2 = new Student1("王五", "54321");

      oos.writeObject(s1);
      oos.writeObject(s2);

      oos.close();

      FileInputStream fis = new FileInputStream("test.obj");
      ObjectInputStream ois = new ObjectInputStream(fis);

      Student1 s3 = (Student1) ois.readObject();
      Student1 s4 = (Student1) ois.readObject();
      //人数: 2 姓名: 王五 密码: null
      //这里人数是2,看起来好像静态变量序列化了,其实是因为这里反序列化时,Class文件已经加载了导致的.
      //假如在另外一个类中反序列化,则重新加载Student的Class文件,然后人数字段就变成初始值0了.
      //参见下面TestStaticSerialable类

      System.out.println(s3);
      System.out.println(s4);

      ois.close();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    catch (ClassNotFoundException e1)
    {
      e1.printStackTrace();
    }
  }
  /**
   * 输出:
   * 调用Student的带参的构造方法
调用Student的带参的构造方法
人数: 2 姓名: 张三 密码: null
人数: 2 姓名: 王五 密码: null
   */
}

 

 

读取序列化文件的类,导致重新加载Student的Class文件,静态变量恢复成默认值了.

package io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class TestStaticSerialable
{
  public static void main(String[] args)
  {
    try
    {

      FileInputStream fis = new FileInputStream("test.obj");
      ObjectInputStream ois = new ObjectInputStream(fis);

      Student1 s3 = (Student1) ois.readObject();
      Student1 s4 = (Student1) ois.readObject();
      //人数: 0 姓名: 王五 密码: null
      //这里人数0,表示静态变量没有序列化

      System.out.println(s3);
      System.out.println(s4);

      ois.close();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    catch (ClassNotFoundException e1)
    {
      e1.printStackTrace();
    }
  }
  /**
   * 输出:
人数: 0 姓名: 张三 密码: null
人数: 0 姓名: 王五 密码: null
   */
}