【问题标题】:"Ghost instances" in Java?Java中的“鬼实例”?
【发布时间】:2017-01-18 18:49:52
【问题描述】:

我在处理不同对象的实例时遇到问题,会发生这种情况:

我一直在用 Java(Swing 和 AWT)开发一个小游戏,我有以下类:

  • App.java
  • Play.java
  • Event.java
  • GameScene.java
  • MenuScene.java
  • Timer.java

地点:

  • App扩展了JFrame,是一个带有应用程序主功能的框架(main),这个类创建游戏窗口,只存在这个JFrame

  • MenuScene 和 GameScene 类是应用程序的场景,例如当你看到菜单,你想看最高分时,它是一个场景,游戏的关卡是一个场景等,但是在这种情况下,我只有两个场景,我在 JPanel 中表示它们: MenuScene 扩展 JPanel 并创建游戏菜单(按钮、图像等),同样适用于 GameScene 类,这也扩展 JPanel 并创建游戏。

  • 其他类(Play、Event、Timer)是简单类,它们具有“游戏逻辑”、键盘控制、定时器、游戏操作,并在 GameScene 类的三个全局变量中实例化。

一切都从 App 开始,创建它的一个实例,并在其构造函数中调用一个方法来“创建”菜单 (MenuScene.java)。现在菜单有一个 JButton 按下时“创建”游戏(GameScene.java)并且这个类有一个 JButton 可以随时返回菜单......这是我遇到问题的地方,因为如果我正在玩并且我返回菜单 游戏仍然存在,我可能会输,这没有任何意义,就好像你在玩但没有看到游戏,而是看到了菜单,有趣的是,图形部分效果很好,即如果我按下一个按钮,它就会删除我有什么并快速绘制我想要的场景。这是因为如果我没记错的话,Play、Timer 和 Event 是在内存中实例化或“存在”的。因此,如果我再次按下“创建游戏”JButton,我会重新创建 GameScene 的第二个实例吗?对于 MenuScene 和 GameScene 也是如此。这个问题有方法解决吗?您认为我应该如何构建应用程序?

我给你一个最重要的类的大纲:

App.java

public class App extends JFrame {
   private JPanel rootPanel;

   public App() {
      //Define frame
      ...
      runScene(new MenuScene(this));
   }

   public void runScene(JPanel scene) {
      destroyScene();

      rootPanel.setBackground(scene.getBackground());
      rootPanel.add(scene);
      rootPane.validate();
      rootPanel.repaint();
   }

   private void destroyScene() {
      rootPanel.removeAll();
      rootPanel.revalidate();
      rootPanel.repaint();
   }


   public static void main(String[] args) { //Main
      new App();
   }
}

MenuScene.java

public class MenuScene extends JPanel {
   private App app;

   public MenuScene(App app) {
      this.app = app;
      //Define JPanel
      ...
      buttonStart.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            app.runScene(new GameScene(app));
        }
      });
   }
}

GameScene.java

public class GameScene extends JPanel {
   private App;
   private Play;
   private Timer;
   private Event; //Define controls (keyboard)

   public GameScene(App app) {
      this.app = app;
      //Define JPanel, Play, Timer and Event
      ...
      buttonBackMenu.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            app.runScene(new MenuScene(app));
        }
      });
   }
}

Play.java

public class Play {
   private JLabel[][] x;

   public Play(JLabel[][] x) { //This matrix is important (is an reference), is instanced in GameScene, this is an problem?
      this.x = x;
      //Define others variables
   }
}

感谢您的帮助。

【问题讨论】:

  • 当您从游戏中按下返回时,您应该拆除/销毁游戏控件,尤其是计时器,您还应该取消引用 app 变量以防止任何循环引用
  • 1) “我给你一个最重要的类的大纲:” 为了尽快获得更好的帮助,请发布minimal reproducible exampleShort, Self Contained, Correct Example。 2) rootPanel.removeAll(); rootPanel.revalidate(); rootPanel.repaint(); 使用CardLayout,如this answer 所示。

标签: java swing design-patterns awt instances


【解决方案1】:

我找到了一个有点奇特的解决方案,但不知道是不是最好的:

最好的方法是,既然 GC 不选择活动的计时器,那么最好停止它们并将其他对象匹配为 null。使用 Singleton 模式,我有一个 Frame 实例,相同的实例将用于任何想要运行另一个场景的类(场景),这里是一个实现:

App.java

   public class App extends JFrame {
       private JPanel rootPanel;
       private static App app;

       private App() {
          super("x");
          createApp();
       }

       public static App getInstance() {
          if (app == null) {
            app = new App();
          }

          return app;
       }

       private void createApp() {
         //Define JFrame, rootPanel, buttons, etc ...
       }

       public void runScene(JPanel scene) {
          rootPanel.removeAll();

          rootPanel.add(scene);
          rootPanel.revalidate();
          rootPanel.repaint();
       }

       public static void main(String[] args) { //Main
          getInstance().runScene(new MenuScene());
       }
    }

GameScene.java

    public class GameScene extends JPanel {

    private Play p;
    private Timer t;
    private Event e; //Define controls (keyboard)
    private JLabel[][] mat;

    public GameScene() {
       //Define JPanel, Matrix, Play, Timer and Event
       ...
       buttonBackMenu.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent x) {
             This method is useful to create another scene for example from another instance other than this (GameScene)
             changeScene(new MenuScene());
         }
       });
    }

    public void changeScene(JPanel scene) {
        e.removeKeyDispatcher(); //You must create a method that allows you to move the event key dispatcher
        t.stopAllTimers(); //You must create a method to stop all timers, or any object that is active.
        t = null;
        e = null;
        p = null;
        //If you have more active objects and work with other instances of other classes they should be "broken" or "stopped" and then match their instance to null
        App.getInstance().runScene(scene);
    }

    //Optional...
    public JLabel[][] getMat() {
       return mat;
    }
 }

Play.java、Event.java、Timer.java (X)

public class X {

   private GameScene g;
   private JLabel[][] mat;

   public X(GameScene g) {
      this.g = g;
      //I would use the instance of the class I need to access the variables I'm going to use, for example:
      this.mat = g.getMat();
   }
} 

【讨论】:

    猜你喜欢
    • 2014-04-20
    • 1970-01-01
    • 2015-02-18
    • 2018-10-15
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 2014-06-10
    • 2012-01-29
    相关资源
    最近更新 更多