gpt4 book ai didi

序列化中的 java.io.InvalidClassException

转载 作者:行者123 更新时间:2023-12-01 14:39:57 24 4
gpt4 key购买 nike

读完连载后,我尝试对书中提供的示例进行实验。以下代码有一些变化,这基本上是从 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);
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)

如果我删除 Animal 构造函数并注释掉 Dog 构造函数中的 super(weight),则输出为

Collar constructor
Dog constructor
after: dog name: Sheru , collar=null
Animal material is:42

我理解这个输出,而且我还了解到,在反序列化期间,可序列化类的父类(super class)构造函数被调用,但这里不存在默认构造函数,因此发生了异常。但我想知道为什么会发生这个异常。

最佳答案

当您尝试从文件中读取时抛出异常:

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 堆栈跟踪可以嵌套,一个异常可以导致另一个异常;这有点尴尬。事实上,在对象的序列化过程中,已经计算出不存在默认构造函数。以下是 involved source code 的摘录:

...
cons = getSerializableConstructor(cl);
...
} else if (cons == null) {
deserializeEx = new InvalidClassException(name, "no valid constructor");
}

这意味着在写入期间,已经很清楚没有有效的构造函数。但是,异常不会抛出,而是与对象一起序列化。后来反序列化的时候,调用这段代码:

void checkDeserialize() throws InvalidClassException {
if (deserializeEx != null) {
InvalidClassException ice =
new InvalidClassException(deserializeEx.classname,
deserializeEx.getMessage());
ice.initCause(deserializeEx);
throw ice;
}
}

这里,抛出了一个“真正的”异常,但其原因被设置为对象序列化期间存储的异常。

这种机制只存在于SUN/Oracle的Java实现中; OpenJDK 在尝试读取时明显抛出异常,并且不会保留写入的堆栈跟踪。

关于序列化中的 java.io.InvalidClassException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16066636/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com