0

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

我理解这个输出,并且我也得到了这样一个事实,即在反序列化过程中调用了可序列化类的超类构造函数,但这里没有默认构造函数,因此发生了异常。但是我想知道为什么会发生这种异常。

4

3 回答 3

1

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

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 stacktraces 可以嵌套,一个异常可以导致另一个异常;这是一个小小的 ackward。事实上,在对象的序列化过程中,已经计算出没有默认构造函数。以下是相关源代码的摘录:

...
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 在尝试读取时显然会引发异常,并且不会保留堆栈跟踪以防止写入。

于 2013-04-17T20:44:08.267 回答
0

不可序列化的基类 Dog 必须具有可访问的默认构造函数。当您注释掉 Dog(weight) 构造函数时,您强制编译器提供一个,而当您将它留在编译器中时,它不会提供一个。

于 2013-04-17T21:33:05.180 回答
0

有一条规则是,序列化类的父类任何与之关联的类必须实现 Serializable
在您的情况下,当您删除super(weight);then 它检查默认构造函数并正常运行
但如果您放置class Animal implements Serializablethen 代码也可以正常运行。

于 2018-07-01T08:21:18.900 回答