【问题标题】:NotSerializableException in Third-Party Graphics Library第三方图形库中的 NotSerializableException
【发布时间】:2019-04-15 20:53:48
【问题描述】:

我使用 ACM 图形库创建了 Atari Breakout 游戏的克隆,并且刚刚添加了高分界面和功能。玩家的名字和分数应该会显示在 GUI 窗口上(它是成功的),并且会写入一个.dat 二进制文件。

但是,当代码尝试加载现有文件时,我收到以下错误。

writing aborted; java.io.NotSerializableException: acm.graphics.GCanvasListener

我在网上研究过这个错误,似乎可以通过编辑类来实现Serializable来解决。但是,引发此错误的类不是我自己的,而是属于第三方 ACM 图形库的类。我该如何解决这个问题?

我什至不确定为什么首先会导致此错误,因为我尝试序列化的数据只是一个名称和分数e,我没有尝试序列化物体的画布或类似的东西。

主类(称为 Breakout)

public class Breakout extends GraphicsProgram {
    ... // game variables
    public void run() {
        ... // this if clause runs when game ends
        if (brickCounter > 0) {
                removeAll(); // clears screen
                printGameOver(); // displays game over message
                HighscoreManager hm = new HighscoreManager();
                String name = getHighScoreName();
                hm.addScore(name, score);
                hm.displayHighscores();
        }
    }
    ... // game functionality methods
    private String getHighScoreName(){
        IODialog dialog = new IODialog();
        String name = dialog.readLine("Enter your name: ");
        return name;
    }

成绩等级

private class Score implements Serializable {
    private int score;
    private String name;

    public Score(String name, int score) {
        this.score = score;
        this.name = name;
    }

    public int getScore() { return score; }
    public String getName() { return name; }
}

ScoreComparator 类

private class ScoreComparator implements Comparator<Score> {
    public int compare(Score score1, Score score2) {

        int sc1 = score1.getScore();
        int sc2 = score2.getScore();

        if (sc1 > sc2) {
            return -1;
        } else if (sc1 < sc2) {
            return 1;
        } else {
            return 0;
        }
    }
}

HighscoreManager 类

private class HighscoreManager {
    private ArrayList<Score> scores;
    private static final String HIGHSCORE_FILE = ".//bin//scores.dat";
    ObjectOutputStream outputStream = null;
    ObjectInputStream inputStream = null;

    public HighscoreManager() {
        scores = new ArrayList<Score>(10);
    }

    public ArrayList<Score> getScores() {
        loadScoreFile();
        sort();
        return scores;
    }

    private void sort() {
        ScoreComparator comparator = new ScoreComparator();
        Collections.sort(scores, comparator);
    }

    public void addScore(String name, int score) {
        loadScoreFile();
        scores.add(new Score(name, score));
        updateScoreFile();
    }

    public void loadScoreFile() {
        try {
            inputStream = new ObjectInputStream(new FileInputStream(HIGHSCORE_FILE));
            scores = (ArrayList<Score>) inputStream.readObject();
        }
        catch (FileNotFoundException e) {
            System.out.println("[Load] File Not Found Error: " + e.getMessage());
        }
        catch (IOException e) {
            System.out.println("[Load] Input/Output Error: " + e.getMessage());
        }
        catch (ClassNotFoundException e) {
            throw new RuntimeException("[Load] Class Not Found Error: " + e.getMessage());
        }
        finally {
            try {
                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (IOException e) {
                System.out.println("[Load] Input/Output Error: " + e.getMessage());
            }
        }
    }

    public void updateScoreFile() {
        try {
            outputStream = new ObjectOutputStream(new FileOutputStream(HIGHSCORE_FILE));
            outputStream.writeObject(scores);
        }
        catch (FileNotFoundException e) {
            System.out.println("[Update] File Not Found Error: " + e.getMessage());
        }
        catch (IOException e) {
            System.out.println("[Update] Input/Output Error: " + e.getMessage());
        }
        finally {
            try {
                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (IOException e) {
                System.out.println("[Update] Input/Output Error: " + e.getMessage());
            }
        }
    }

    public void displayHighscores() {
        int max = 10;
        ArrayList<Score> scores;
        scores = getScores();
        int x = scores.size();

        if (x > max) {
            x = max;
        }

        removeAll(); // clears screen
        int npos = 160;
        int spos = 160;

        for (int i = 0; i < x; i++) {
            GLabel showName = new GLabel(scores.get(i).getName(), (getWidth() / 2.0) - 100, (getHeight() / 2.0) - npos);
            showName.move(-showName.getWidth() / 2, -showName.getHeight());
            showName.setColor(Color.WHITE);
            add(showName);
            npos -= 40;
        }

        for (int i = 0; i < x; i++) {
            GLabel showScore = new GLabel(Integer.toString(scores.get(i).getScore()), (getWidth() / 2.0) + 100, (getHeight() / 2.0) - spos);
            showScore.move(-showScore.getWidth() / 2, -showScore.getHeight());
            showScore.setColor(Color.WHITE);
            add(showScore);
            spos -= 40;
        }
    }

运行应用程序后:

[Load] Input/Output Error: writing aborted; java.io.NotSerializableException: acm.graphics.GCanvasListener
[Update] Input/Output Error: acm.graphics.GCanvasListener
[Load] Input/Output Error: writing aborted; java.io.NotSerializableException: acm.graphics.GCanvasListener

【问题讨论】:

标签: java exception serialization libraries notserializableexception


【解决方案1】:

您的任务是从您的姓名和分数结构中找到对 UI 组件的隐藏引用。许多 GUI 应用程序使用大量内部类,这可能是缺少的链接。

当你有这样的课程时:

class MyGame {
    private SomeUIWidget widget;
    class TopScore implements Serializable {
        String name;
        int score;
        ...
    }
    ...
}

TopScore 中有一个隐藏成员,它引用了MyGame 的“封闭实例”,包括它的SomeUIWidget 成员。当您尝试序列化 TopScore 实例时,所有其他实例都会被拖入其中。

您可以简单地将TopScore 声明为static 嵌套类。这意味着没有封闭实例,并且仅用于对其他代码隐藏 TopScore 类。但是,我建议将TopScore 设置为顶级类,在它自己的文件中,因为其他对象很可能希望以不同的方式使用这些对象——也就是说,它似乎是你的一部分的候选对象公共 API。

这是一个有根据的猜测,没有任何实际代码。要获得更好的答案,请将您的代码减少到演示问题所需的最低限度,并将其包含在您的问题中。

【讨论】:

  • 目前代码都在一个类和文件中,尽管有几个私有类嵌套在主公共类中。我用一些代码编辑了我的问题。你是说如果我将这些私有类移到它们自己的文件中,这个问题很可能会得到解决?
  • @Arbiter 至少,将static 修饰符添加到您的Score 类中。考虑将Score 移动到它自己的文件中。内部类最有用的方法是让类可以访问封闭对象的私有成员。 Score 不需要它。嵌套类(static 被另一个类包围)对于隐藏任何其他类不需要的类最有用。我可以很容易地想象在 UI 类之外读取或写入 Score 实例的用例,这表明移动到它自己的文件。将所有代码放在一个类和文件中不会扩展。
  • 谢谢,解决了我的问题。您是绝对正确的,我不确定为什么我决定将所有内容都移到一个文件中。我现在将重构我的代码,以确保不再有这样的冲突。出于兴趣,你会怎么做呢?比较器、分数和高分管理器类很容易移动,但是您是否建议为突破(主)类中的其他任何东西创建一个新类?只是想知道而已。
  • @Arbiter 很难说,但很可能我的 UI 类或多个类最终仍会包含许多私有内部类(或匿名内部类):那些通过更改一些来处理 UI 事件的封闭类的状态。这些类对其封闭对象有很强的依赖性,并且可以在那里愉快地生活。但如果你有其他类(如ScoreHighcoreManagerScoreComparator)对封闭类没有任何依赖,我会将它们移出。
【解决方案2】:

您应该转到字段 namescore 所在的班级,并添加例如public class nameclass implements Serializable。我希望它对你有用。

【讨论】:

  • 我已将其添加到我的分数课程中(请参阅更新的问题)但问题仍然存在......
  • 我知道private class Score 在一个班级里面 ¿我说的对吗?如果是这样,您应该阅读这篇文章:link
  • 实际上它说内部类不能使它们成为Serializable
猜你喜欢
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 2019-08-16
  • 1970-01-01
相关资源
最近更新 更多