【问题标题】:NullPointer Exception when calling method from a difference class从不同的类调用方法时出现 NullPointerException
【发布时间】:2009-12-16 20:18:22
【问题描述】:

JAVA- 你好, 我正在编写一个扫雷程序(第一个大人物)并且真的被卡住了。根据我要遵循的规范,该程序本身由 2 个类(一个用于逻辑,一个用于 GUI)组成。我在这两门课上都做了很多,但也没有完成。但是,我正在尝试测试从一个类到另一个类的实现调用方法,这就是我卡住的地方。在 GUI 类中,每次用户单击框时,我都尝试在逻辑类中调用方法 openCell(int x, int y)。该逻辑类方法将依次检查方块上是否有地雷、0 或数字,并从 GUI 类调用适当的方法。报错涉及的两种方法如下:

GUI CLASS
public void mouseClicked(MouseEvent e) {
    for (int x = 0 ; x < width ; x++) {
       for (int y = 0 ; y < height ; y++) {
           if (e.getSource() == table[x][y]) {    
                if(e.getButton() == e.BUTTON1) {
                   MinesweeperLogic logicClass = new MinesweeperLogic();
                   logicClass.isOpen(x, y); // <--------------------------- ERROR
                 }}}}}

LOGIC CLASS
boolean openCell(int x, int y) {
isClicked[x][y] = true;
    if(mine[x][y] == true && flag[x][y]==false) {
        return false;
    } else if(neighborBombs(x, y) > 0 && flag[x][y]==false) {
        return true;
    }else {
            marked = true;
            return marked;
    }}

以下是当用户点击游戏中的一个框时我收到的错误报告(它在编译代码时会捕获它):

 Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
 at MinesweeperLogic.isOpen(MinesweeperLogic.java:117)
 at MineSweeperGUI.mouseClicked(MineSweeperGUI.java:126)
 at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
 at java.awt.Component.processMouseEvent(Component.java:6266)
 at javax.swing.JComponent.processMouseEvent(JComponent.java:3255)
 at java.awt.Component.processEvent(Component.java:6028)
 at java.awt.Container.processEvent(Container.java:2041)
 at java.awt.Component.dispatchEventImpl(Component.java:4630)
 at java.awt.Container.dispatchEventImpl(Container.java:2099)
 at java.awt.Component.dispatchEvent(Component.java:4460)
 at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
 at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4247)
 at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
 at java.awt.Container.dispatchEventImpl(Container.java:2085)
 at java.awt.Window.dispatchEventImpl(Window.java:2475)
 at java.awt.Component.dispatchEvent(Component.java:4460)
 at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
 at  java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
 at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
 at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
 at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

最后,如果需要的话,到目前为止,两个类的完整(此时不完整)代码。 (逻辑类中的方法需要按照项目的指令执行特定的操作,并且在逻辑类中必须没有用户交互)。我无法弄清楚究竟是什么导致了错误。任何指导将不胜感激。 (希望这不是很明显的事情,因为即使我得了流感,我也花了过去几个小时试图弄清楚这一点!哈哈)。

import java.awt.* ;
import java.awt.event.* ;
import java.awt.geom.* ;
import javax.swing.* ;
import javax.swing.event.* ;

public class MineSweeperGUI extends JFrame implements ActionListener, MouseListener {  
int width = 10;
int height = 10;
JPanel p = new JPanel();
JButton[][] table = new JButton[width][height];

public void MineSweeper() {
    MinesweeperLogic logicClass = new MinesweeperLogic();
    logicClass.startNewGame(width, height);
    JButton[] button = new JButton[width*height];
    GridLayout layout = new GridLayout (width, height) ;
    p.setLayout(layout);
    for(int x = 0 ; x < width ; x++) {
        for(int y = 0 ; y < height ; y++) {
            table[x][y] = new JButton();
            table[x][y].setPreferredSize(new Dimension(25,25));
            table[x][y].addMouseListener (this);
            p.add(table [x] [y]);
        }
    }       
    this.add(p);
    this.pack();
    this.setVisible(true);
}


public void mouseClicked(MouseEvent e) {
    for (int x = 0 ; x < width ; x++) {
       for (int y = 0 ; y < height ; y++) {
           if (e.getSource() == table[x][y]) {    
                if(e.getButton() == e.BUTTON1) {
                   MinesweeperLogic logicClass = new MinesweeperLogic();
                   logicClass.isOpen(x, y); //<--------------------------------------
               }
           }
       }
   }
 }
public void gameover(int x, int y) {
   table[x][y].setText("*");
}

public static void main(String[]args) {
    MineSweeperGUI guiClass = new MineSweeperGUI();
    guiClass.MineSweeper();
}}    


public void actionPerformed(ActionEvent e) {
}

public void mouseEntered(MouseEvent e) {
}

public void mousePressed(MouseEvent e) {
}

public void mouseExited(MouseEvent e) {
}

public void mouseReleased(MouseEvent e) {
}




public class MinesweeperLogic {

private int w, h, maxBombs, bombsremaining;
public int width, height;
private boolean mine[][];
private boolean flag[][];
private boolean isClicked[][];
private boolean isZero[][];
private boolean marked;



public void startNewGame(int width, int height) {
    w = width;
    h = height;
    flag = new boolean[w][h];
    isZero = new boolean[w][h];
    isClicked = new boolean[w][h];
    mine = new boolean[w][h];
    maxBombs =(int) Math.floor (width*height*0.15);
    bombsremaining = maxBombs;
    for(int i = 0; i < maxBombs; i++) {
        int x = (int) (Math.random() * (w));
        int y = (int) (Math.random() * (h));
        if (mine[x][y] == false) {
            mine[x][y] = true;
            isClicked[x][y] = false;
            flag[x][y] = false;
        }
    } 
}


int getWidth() {
    return w;
}


int getHeight() {
    return h;
}


boolean openCell(int x, int y) { // <---------------------------------------------
    //MineSweeperGUI guiClass = new MineSweeperGUI();
    isClicked[x][y] = true;
    if(mine[x][y] == true && flag[x][y]==false) {
        //guiClass.gameover(x, y);
        return false;
    } else if(neighborBombs(x, y) > 0 && flag[x][y]==false) {
        return true;
    } else {
            marked = true;
            return marked;
    }}


 boolean markCell(int x, int y) {
     if(flag[x][y] == true) {
            flag[x][y] = false;
            isClicked[x][y] = false;
            bombsremaining++;
            marked = false;
            return marked;
        } else {
            flag[x][y] = true;
            isClicked[x][y] = true;
            bombsremaining--;
                if(mine[x][y]==true) {
                    return true;
                } else {
                    return false;
                }
            }
        }


 boolean isOpen(int x, int y) {
     if(isClicked[x][y] == false) {
         return false;
        } else {
            return true;
        }
}


 boolean isMarked(int x, int y) {
     if(flag[x][y] == true) {
         return true;
        } else {
            return false;
        }
    }


  int getValue(int x, int y) {
      if(mine[x][y] == true) {
          return -1;
        } else {
            return neighborBombs(x, y);
        }
    }


    private int neighborBombs(int x, int y) {  // checks surrounding 8 squares for number of bombs 
        int surBombs = 0;
            for (int q = x - 1 ; q <= x + 1 ; q++) {
                for (int w = y - 1 ; w <= y + 1 ; w++) {
                    while (true) {
                        if (q < 0 || w < 0 || q >= w || w >= h) { 
                            break;
                        }
                        if (mine[q][w] == true) {
                            surBombs++;
                            break;
                        }
                    }   
                }
            }
         return surBombs;
        }
    }

【问题讨论】:

  • 尝试调用方法static并检查!

标签: java nullpointerexception method-call


【解决方案1】:

怎么样:

  1. 查看异常堆栈跟踪的第一行,查看问题出现在哪个类和哪一行
  2. 转到该行,查看在该位置哪些对象可能为“null”
  3. 找出为什么为 null 的对象没有赋值
  4. 如果有多个对象可能为空,您可以System.out.println() 他们,看看哪个是。

【讨论】:

  • 虽然这将有助于修复 NullPointerException,但有问题的代码非常错误,以至于无法让他更接近真正拥有一个有效的扫雷游戏。
  • 学习使用调试器比 System.out.println 更可取
  • 对于这样简单的事情,调试器会增加开销
  • 如果您使用的是 Eclipse,请双击该行以创建断点,然后单击另一个按钮来运行您的程序,而不是输入其他代码(您必须删除之后)。除非您出于某种原因害怕调试器,否则调试代码实际上对我来说似乎更简单。 ;)
  • 调试代码有助于您了解代码实际是如何工作的,而不是您认为它是如何工作的。 (即使您编写了代码也适用!)
【解决方案2】:

您的堆栈跟踪会告诉您:

线程“AWT-EventQueue-0”中的异常 java.lang.NullPointerException 在 MinesweeperLogic.isOpen(MinesweeperLogic.java:117)

因此您可以转到第 117 行并确定哪些引用可能为空。 (看起来你在获取堆栈跟踪后在 117 处添加了一行,然后注释掉了新行,所以我要在这里硬着头皮说堆栈跟踪实际上是指现在所谓的第 118 行:isClicked[x][y] = true;) 在这种情况下,唯一可以为 null 的是 isClicked[][]

再深入一点,我们可以看到您确实在 startNewGame() 中初始化了 isClicked,但显然该实例正在丢失。发生这种情况有两个原因:首先,构造函数中的 logicClass 超出范围,因为它不是该类的成员。第二个(这可能是解决第一个问题的失败尝试),在mouseClicked 中,您创建一个新的MinesweeperLogic(以及新创建的 null isClicked),而不是使用您之前在@987654325 中创建的那个@构造函数。

您应该进行其他一些重构来清理代码,但是将 logicClass 设为成员并删除重复的实例化应该可以解决直接的问题。

使用调试器可能会对您有所帮助,并自己逐步解决问题以准确了解正在发生的事情。

【讨论】:

    【解决方案3】:

    数组(flag、isZero、isClicked)未初始化,当您尝试使用它们时为空。您需要 GUI 类包含逻辑类的实例并始终使用相同的实例。否则,每个 GUI 操作都将在不同的游戏中发生!

    【讨论】:

    • 我同意。每次单击鼠标时,您都会创建一个新的 MinesweeperLogic 实例。您需要保留在 MineSweeper() 方法中创建的实例。
    【解决方案4】:

    知道逻辑类中的第 117 行是什么会很有帮助。我的猜测是 minemine[x]flaggedflagged[x] 为空。

    也就是说,您的“遍历整个表格以查看单击了哪个表格”的技术并没有给我带来特别的启发。

    编辑:实际上,问题在于 isOpen 数组为空,因为您只是实例化了该类。

    EDIT2:好的,所以它是我最初列出的其中之一。我的猜测是全部,因为你只是实例化了这个类,而且它使用的是默认的无操作构造函数。

    【讨论】:

    • 有一个彩色箭头指向第一块代码中的第 117 行。
    • 它完全没有指向那条线,它实际上指向的是一个完全不同的类中的一条线。
    【解决方案5】:

    您说错误发生在 mouseClicked() 方法中,但是堆栈跟踪显示不同:

    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
     at MinesweeperLogic.isOpen(MinesweeperLogic.java:117)
     at MineSweeperGUI.mouseClicked(MineSweeperGUI.java:126)
    

    这是说 NPE 发生在 MineSweeperLogic 的第 117 行。在该行上使用您的调试器,或插入打印语句来找出什么是空的,从那里,您可以找出原因。

    【讨论】:

      【解决方案6】:

      请注意,您可以在这样的一行中获取此异常

      myMethod(myNumber); 由于自动拆箱,“myNumber”是 Long 类型,但当前为 null。希望 JVM 能更好地报告这一点。修复是不传入null,它不会自动转换为零......

      【讨论】:

        猜你喜欢
        • 2019-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-22
        • 2018-02-01
        • 1970-01-01
        • 2017-09-01
        • 1970-01-01
        相关资源
        最近更新 更多