什么时候会调用readObjectNoData

时间:2017-09-29 04:00:20

标签: java serialization

我正在阅读Effective Java Item74。我对java对象的序列化感兴趣。但是一种方法" readObjectNoData"让我困惑。

以下是JavaDoc的解释

  

对于可序列化对象,readObjectNoData方法允许类在反序列化子类实例的情况下控制其自己字段的初始化,并且序列化流不会将所讨论的类列为反序列化对象的超类。这可能发生在接收方使用不同版本的反序列化实例的类而不是发送方的情况下,并且接收方的版本扩展了未由发送方版本扩展的类。如果序列化流已被篡改,也可能发生这种情况;因此,readObjectNoData对于正确初始化反序列化的对象非常有用,尽管它有一个“敌对的”#34;或不完整的源流。

它说当我序列化一个对象时,它是旧版本,它不扩展父类,但是当我反序列化时,该类升级到新版本并扩展了其他类。

我真的想玩这个。所以我有一个员工类

public class Employee implements Serializable { //v1
  public String name;
  public int age;
  protected String address;
  static final long serialVersionUID = 1L;

  public Employee()
  {
    name= "John";
    age = 1;
    address ="N/A";
  }

  public Employee(String name , int age, String address)
  {
    this.name = name;
    this.age = age;
    this.address = address;
  }
}

和序列化类

public class Se {
    public static void main(String [] args) {
        Employee e = new Employee();

        try {
            FileOutputStream fileOut =
                new FileOutputStream("tmp.txt");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(e);
            out.close();
            fileOut.close();
            System.out.printf("Serialized data is saved in tmp.txt");
        }catch(IOException i) {
            i.printStackTrace();
        }
    }
}

将Employee对象序列化为tmp.txt后,我更改了Employee类

public class Employee extends Person { //v2
  protected String address ;
  static final long serialVersionUID = 1L;
  public Employee()
  {
    super();
    address ="N/A";
  }

  public Employee(String name , int age, String address)
  {
    super(name,age);
    this.address = address;
  }
  private void readObjectNoData() throws ObjectStreamException {
      name = "John";
      age = 1;
  }
}

这是我的Person类

class Person implements Serializable{
  protected String name;
  protected int age;

  Person() {
    this("John",1);
  }
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

当我通过反序列化的类

反序列化时
public class De {
  public static void main(String [] args) {
    Employee e = null;
    try {
      FileInputStream fileIn = new FileInputStream("tmp.txt");
      ObjectInputStream in = new ObjectInputStream(fileIn);
      e = (Employee) in.readObject();
      in.close();
      fileIn.close();
    }catch(IOException i) {
      i.printStackTrace();
      System.out.println("IOException");
      return;
    }catch(ClassNotFoundException c) {
      System.out.println("Employee class not found");
      c.printStackTrace();
      return;
    }

    System.out.println("Deserialized Employee...");
    System.out.println("Name: " + e.name);
    System.out.println("Address: " + e.address);
    System.out.println("Age: " + e.age);
  }
}

输出始终显示

Deserialized Employee...
Name: null
Address: N/A
Age: 0

即使我在readObjectNoData方法中设置调试点,它也永远不会被触发,我在网上找不到任何可运行的例子,所以我在这里提供了这么多代码。有谁知道我错过了什么?

任何意见都将不胜感激。

1 个答案:

答案 0 :(得分:2)

这是一个独特的案例,其中类的反序列化器具有基于子类(Person)的类(Employee)的版本。

子类可以说“如果我的基类在序列化数据中,那就没关系了 - 只需填空。

  

编辑以下

尝试使用这两个版本进行“反序列化”。我的意思是序列化您的v1员工,然后尝试以下:

import java.io.Serializable;
public class Employee extends Person implements Serializable { //v2
  protected String address ;
  static final long serialVersionUID = 1L;

  public Employee()
  {
    super();
    address ="N/A";
  }

  public Employee(String name , int age, String address)
  {
    super(name,age);
    this.address = address;
  }

}
  

import java.io.ObjectStreamException;
import java.io.Serializable;

class Person implements Serializable{
   protected String name;
   protected int age;

   Person() {

 }

Person(String name, int age) {
   this.name = name;
   this.age = age;
}
private void readObjectNoData() throws ObjectStreamException {

  name = "cccc";
  age = 1;
  throw new IllegalArgumentException();
 }
}

您将看到异常

Exception in thread "main" java.lang.IllegalArgumentException
    at test.serialization.Person.readObjectNoData(Person.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at java.io.ObjectStreamClass.invokeReadObjectNoData(ObjectStreamClass.java:1089)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1955)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at test.serialization.De.main(De.java:22)

希望有所帮助