序列化中的java.io.InvalidClassException
在阅读了序列化之后,我尝试对书中提供的示例执行一个实验。下面的代码有一些变化,这基本上是从SCJP的书中挑选出来的序列化中的java.io.InvalidClassException,java,exception,serialization,Java,Exception,Serialization,在阅读了序列化之后,我尝试对书中提供的示例执行一个实验。下面的代码有一些变化,这基本上是从SCJP的书中挑选出来的 import java.io.FileInputStream; public class SerializationTest { public static void main(String[] args) { Collar c = new Collar(4); Dog d = new Dog(c, "Sheru", 32);
import java.io.FileInputStream;
public class SerializationTest {
public static void main(String[] args) {
Collar c = new Collar(4);
Dog d = new Dog(c, "Sheru", 32);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(
"C:\\Users\\dell\\Desktop\\NewDir\\DogState.txt");
oos = new ObjectOutputStream(fos);
oos.writeObject(d);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// ***************************************************************************************************
// //
Dog restore = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(
"C:\\Users\\dell\\Desktop\\NewDir\\DogState.txt");
ois = new ObjectInputStream(fis);
restore = (Dog) ois.readObject();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
fis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("after: dog name: "+ restore.name +" , collar=" + restore.getCollar());
System.out.println("Animal material is:" + restore.getWeight());
}
}
// Intentionally added parameterized constructor so that default constructor is not called.
class Animal{
int weight = 42;
public Animal(int weight) {
this.weight = weight;
System.out.println("animal constructor");
}
}
class Dog extends Animal implements Serializable {
String name;
transient Collar collar;
public Collar getCollar() {
return collar;
}
public void setCollar(Collar collar) {
this.collar = collar;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Dog(Collar collar, String name, int weight) {
super(weight);
System.out.println("Dog constructor");
this.collar = collar;
this.name = name;
}
}
class Collar {
int size;
public Collar(int size) {
System.out.println("Collar constructor");
this.size = size;
}
}
这里我的问题是为什么会发生InvalidClassException,请解释一下异常的根本原因。
电流输出为
Collar constructor
animal constructor
Dog constructor
java.io.InvalidClassException: Dog; Dog; no valid constructor
at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:39)
Caused by: java.io.InvalidClassException: Dog; no valid constructor
at java.io.ObjectStreamClass.<init>(Unknown Source)
at java.io.ObjectStreamClass.lookup(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:18)
Exception in thread "main" java.lang.NullPointerException
at SerializationTest.main(SerializationTest.java:54)
Collar constructor
Dog constructor
after: dog name: Sheru , collar=null
Animal material is:42
我理解这个输出,并且我还得到这样一个事实:在反序列化过程中,可序列化类的超类构造函数被调用,但这里不存在默认构造函数,因此发生了异常。但我想知道为什么会发生此异常。在您尝试从文件中读取时引发异常:
at java.io.ObjectInputStream.readObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:39)
堆栈跟踪清楚地指示程序在尝试读取对象时中止。可能会让您感到困惑的是,第二个堆栈跟踪指向写入:
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:18)
但你似乎跳过了这句非常重要的话:
Caused by: java.io.InvalidClassException: Dog; no valid constructor
Java堆栈跟踪可以嵌套,一个异常可以导致另一个异常;这里有点偏僻。事实上,在对象序列化期间,已经计算出没有默认构造函数。以下是一段摘录:
这意味着在写入过程中,已经很明显没有有效的构造函数。但是,不会引发异常,而是与对象一起序列化。稍后,反序列化时,将调用以下代码:
void checkDeserialize() throws InvalidClassException {
if (deserializeEx != null) {
InvalidClassException ice =
new InvalidClassException(deserializeEx.classname,
deserializeEx.getMessage());
ice.initCause(deserializeEx);
throw ice;
}
}
这里,抛出了一个“真实”异常,但其原因被设置为对象序列化期间存储的异常
这种机制只在SUN/Oracle的Java实现中存在;OpenJDK显然在尝试读取时抛出异常,并且不会阻止堆栈跟踪写入。不可序列化的基类狗必须具有可访问的默认构造函数。当您注释掉Dog(weight)构造函数时,您强制编译器提供一个构造函数,而当您将它留在编译器中时,编译器不提供构造函数。有一条规则,序列化类的父类或任何关联类必须实现可序列化。
在您的情况下,当您删除
超级(weight)构造函数时
然后它检查默认构造函数并正常运行但是如果您将
类设置为可序列化的,那么代码也会正常运行。Java不知道如何在没有默认构造函数的情况下构造类的新实例。@MattBall但是在编写对象时出现初始错误,在编写时,我将对象提供给JVM,JVM应该将其存储(序列化)到文件中。此时(存储对象)需要创建对象的原因和位置。我读到反序列化时返回一个对象(调用非序列化的超类构造函数初始化该值)。但这里的问题始于writeObject()而不是readObject(),可能是
void checkDeserialize() throws InvalidClassException {
if (deserializeEx != null) {
InvalidClassException ice =
new InvalidClassException(deserializeEx.classname,
deserializeEx.getMessage());
ice.initCause(deserializeEx);
throw ice;
}
}