来自FileInputStream的NullPointerException readObject()

时间:2014-11-30 04:21:14

标签: java

我正在尝试从文件加载2D数组。我可以使用这段代码保存它:

        // Save the array to a file using ObjectOutputStream
        ObjectOutputStream os;
        try {
            os = new ObjectOutputStream(new FileOutputStream("savestate.dat"));
            os.writeObject(playingField);
            os.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

然后我尝试使用以下方法加载文件:

        // First, load file using ObjectInputStream 
        ObjectInputStream is = null;
        try {
            is = new ObjectInputStream(new FileInputStream("savestate.dat"));
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        // Then, read object and cast it as EnhancedMinesweeperTile[][]     
        try {
            playingField = (EnhancedMinesweeperTile[][]) is.readObject();
        } catch (ClassNotFoundException | IOException e1) {
            e1.printStackTrace();
        }

但是在尝试加载它时我得到一个NullPointerException:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at java.awt.Container.updateGraphicsData(Unknown Source)
at java.awt.Container.updateGraphicsData(Unknown Source)
at java.awt.Container.updateGraphicsData(Unknown Source)
at java.awt.Container.updateGraphicsData(Unknown Source)
at java.awt.Container.updateGraphicsData(Unknown Source)
at java.awt.Component.setGraphicsConfiguration(Unknown Source)
at java.awt.Window.setGraphicsConfiguration(Unknown Source)
at java.awt.Window.initGC(Unknown Source)
at java.awt.Window.initDeserializedWindow(Unknown Source)
at java.awt.Window.readObject(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at java.io.ObjectStreamClass.invokeReadObject(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at java.awt.Component.readObject(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at java.io.ObjectStreamClass.invokeReadObject(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readArray(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readArray(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at EnhancedMinesweeper.actionPerformed(EnhancedMinesweeper.java:316)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

在EnhancedMinesweeper.actionPerformed(EnhancedMinesweeper.java:316)是这一行: playingField =(EnhancedMinesweeperTile [] [])is.readObject();

我知道我的数组不是null。我将其初始化为数据成员,如下所示:

    private static EnhancedMinesweeperTile[][] playingField = new EnhancedMinesweeperTile[10][10]; // initialize a 2D array of Tiles

然后我会初始化每个元素:

        for (int i = 0; i < 10; i++){
        for (int j = 0; j < 10; j++){
            playingField[i][j] = new EnhancedMinesweeperTile(i, j);
        }
    }

这有什么问题?为什么我可以将其保存到文件但我无法从文件中加载它?这让我疯了。

1 个答案:

答案 0 :(得分:2)

在我看来,EnhancedMinesweeperTile扩展或引用了java.awt.Component的后代。我会在AWTSwing“棘手”中调用序列化实现,这就是异常发生的地方。我的建议:不要序列化UI类。如果没有看到EnhancedMinesweeperTile的代码,就无法说出为什么会有NullPointerException

我看到你保存EnhancedMinesweeperTile[][]的原因是你要保存并加载扫雷游戏的状态。更简洁,更不易出错,更灵活的解决方案是应用MVC - Model View Controller设计模式。在您的情况下,它意味着将EnhancedMinesweeperTile类拆分为两个类。一类关心瓷砖的视图和控制。还有一个关心模型,国家的课程。然后,在序列化/反序列化期间,您只需关心模型,而不关心UI。

除了不易出错之外,模型与UI的分离提供了解耦,这反过来又有一些优势。

一个是您更改加载/保存的格式而不更改任何有关UI代码的内容。另一个是您可以更改UI,即从Swing更改为SWT或JavaFX,而无需更改有关模型和I / O代码的任何内容。

如果您的EnhancedMinesweeperTile已经是模型,而不是UI,那么您需要在该类中实现writeObject(ObjectOutputStream),以便从模型中分离模型它知道你何时序列化它。序列化序列化对象图,如果EnhancedMinesweeperTile直接或间接地知道UI的某些内容,它也会被序列化。堆栈跟踪表明EnhancedMinesweeperTile甚至知道java.awt.Window

最后提供了一些关于简化I / O代码并使其不易出错的小提示。

void loadFile() {
    try (final ObjectInputStream is = new ObjectInputStream(new FileInputStream("savestate.dat"))) {
        playingField = (EnhancedMinesweeperTile[][]) is.readObject();
    } catch (ClassNotFoundException | IOException e1) {
        e1.printStackTrace();
    }
}

此代码有一些优点。 is null不可能发生。因为它是一个带有单独名称的小函数,在您的情况下,您可以从actionPerformed(ActionEvent)调用,您可以在堆栈跟踪中看到更多细节(一个命名良好的函数)。而且因为该功能名称很好,所以不需要任何额外的评论。