0

我已經用Java編寫了一個掃雷遊戲。但是,當我點擊幾個按鈕後,它會拋出一個NullPointerException。下面是完整的程序(恐怕是巨大的,我的意見也不會有多大效果,我想!)用Java編寫的My Minesweeper程序拋出了NullPointerException

import java.util.Random; 
import javax.swing.JToggleButton; 

    /** 
    * This class represents a button in a {@code MineSweeperGrid}. 
    */ 
    @SuppressWarnings("serial") public class MineButton extends JToggleButton { 
     public final boolean safe; // Whether this button hides a mine or not (true if not). 
     private boolean clicked; // Whether this button has been clicked or not. 
     public final int id; // The id number of this button. This can be used to determine the position of the button on 
           // the grid. 

     /** 
     * Constructs a new {@code MineButton} with a randomly generated safety status, and a user defined id. 
     * 
     * @param id 
     *   The identity number of this button, which can be used to determine its position on a grid of buttons. 
     */ 
     public MineButton(int id) { 
      super(); 
      safe = new Random().nextBoolean(); 
      clicked = false; 
      this.id = id; 
     } 

     public boolean hasBeenClicked() { 
      return clicked; 
     } 

    } 


import java.awt.GridLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.JOptionPane; 
import javax.swing.JPanel; 

@SuppressWarnings("serial") public class MineSweeperGrid extends JPanel { 
    public final int size; // The size of this grid. 
    private int noOfMines; // The number of mines in the grid. 
    private int btnsLeft; // The buttons left to clear. 
    private MineButton[][] grid; // The grid itself, which is represented as an array of MineButtons. 

    /** 
    * Creates a new MineSweeperGrid object according to the size specified as the argument. 
    * @param size 
    *   The length or breadth of the grid. The number of buttons in the grid will be size*size. 
    */ 
    public MineSweeperGrid(int size) { 
     super(); // Call the superclass constructor. 
     if(size < 3) // This is minimum limit of the size, because our code won't work properly with a size lesser than 
         // this. 
      throw new IllegalArgumentException("The size of the grid cannot be less than three."); 
     setLayout(new GridLayout(10, 10)); // Set the layout of this panel. 
     this.size = size; 
     grid = new MineButton[size][size]; 
     int id = noOfMines = btnsLeft = 0; // Initialize id, noOfMines & btnsLeft to 0. 

     // Initialize each button of the grid. 
     for(int i = 0; i < (size - 1); i++) 
      for(int j = 0; j < size; j++) { 
       grid[i][j] = new MineButton(id); // Construct the button. 
       if(!grid[i][j].safe) 
        noOfMines++; 
       grid[i][j].addActionListener(new MineListener()); // Add a listener to it (which does not work 
                    // properly). 
       grid[i][j].setEnabled(true); // Disable this button. 
       add(grid[i][j]); // Add it to the grid. 
       id++; 
      } 
     btnsLeft = (size * size) - noOfMines; // Set the number of buttons to be cleared . 
    } 

    /** 
    * 
    * The ActionListener for the MineButtons in our grid. 
    */ 
    private class MineListener implements ActionListener { 
     /* 
     * This is test code. Not working currently! 
     */ 
     @Override public void actionPerformed(ActionEvent evt) { 
      MineButton btn = (MineButton)evt.getSource(); // Get the button which generated this event. 
      btn.setSelected(false); 
      if(!(btn.safe)) { 
       btn.setText("M"); 
       return; 
      } 
      btn.setEnabled(false); // Since now we know he's safe, disable this button. 
      MineButton[] btns = getNeighbours(btn); // Get this button's neighbours, i.e., the 
                // buttons that surround it. 
      int count = 0; // Initialize count to 0. 
      for(int i = 0; i < btns.length; i++) 
       if(!btns[i].safe) 
        count++; 
      btn.setText("" + count + ""); 
      btnsLeft--; 
      if(btnsLeft == 0) { 
       JOptionPane.showMessageDialog(null, "You have cleared all the mines! You win!"); 
       System.exit(0); 

      } 
     } 

     /** 
     * Method which computes the buttons surrounding the button passed as parameter. 
     * 
     * @param btn 
     *   The button passed as parameter. 
     * @return The buttons surrounding this button as an array of buttons. 
     * @throws IllegalArgumentException 
     *    If the parameter passed is null. 
     */ 
     private MineButton[] getNeighbours(MineButton btn) { 
      int x = btn.id/size, y = btn.id % size; 
      System.out.println(x + ", " + y); 
      if((x == 0) && (y == 0)) // This is the first button in the grid, in the top left corner. 
       return new MineButton[] {grid[x][y + 1], grid[x + 1][y], grid[x + 1][y + 1]}; // This is OK. 

      else if((x == (size - 1)) && (y == (size - 1))) // This is the last button in the grid, in the lower right 
                  // corner. 
       return new MineButton[] {grid[x - 1][y - 1], grid[x - 1][y], grid[x][y - 1]}; // This is OK. 

      else if((x == 0) && (y == (size - 1))) // This is the button in the top right corner. 
       return new MineButton[] {grid[x][y - 1], grid[x + 1][y], grid[x + 1][y - 1]}; 

      else if((x == (size - 1)) && (y == 0)) // This is the button in the lower left corner. 
       return new MineButton[] {grid[x][y + 1], grid[x - 1][y], grid[x - 1][y + 1]}; // This is OK. 

      else if(x == 0) // This is a button belonging to the first row. 
       return new MineButton[] {grid[x][y + 1], grid[x + 1][y], grid[x + 1][y + 1], grid[x + 1][y - 1], 
         grid[x][y - 1]}; // This is OK. 

      else if(y == 0) // This is a button belonging to the first column. 
       return new MineButton[] {grid[x][y + 1], grid[x + 1][y], grid[x + 1][y + 1], grid[x - 1][y], 
         grid[x - 1][y + 1]}; // This is OK. 

      else if(x == (size - 1)) // This is a button in the last row. 
       return new MineButton[] {grid[x][y - 1], grid[x - 1][y], grid[x - 1][y - 1], grid[x - 1][y + 1], 
         grid[x][y + 1]}; // This is OK.; 

      else if(y == (size - 1)) // This is a button in the last column. 
       return new MineButton[] {grid[x][y - 1], grid[x - 1][y], grid[x - 1][y - 1], grid[x + 1][y], 
         grid[x + 1][y - 1]}; // This is OK. 

      // For all other buttons. 
      return new MineButton[] {grid[x + 1][y + 1], grid[x + 1][y], grid[x][y + 1], grid[x - 1][y - 1], 
        grid[x - 1][y], grid[x][y - 1], grid[x - 1][y + 1], grid[x + 1][y - 1]}; // This is OK. 

     } 
    } 
} 


import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.JFrame; 
import javax.swing.JMenu; 
import javax.swing.JMenuBar; 
import javax.swing.JMenuItem; 
import javax.swing.JPanel; 

import org.mandal.data.MineSweeperGrid; 

public class MineSweeperUI { 

    private JFrame frame; 
    private JPanel content; 
    private ButtonHandler listener; 
    private MineSweeperGrid grid; 
    private JMenuBar menuBar; 
    private JMenu file; 
    private JMenuItem newGame; 
    private JMenuItem options; 
    private JMenuItem close; 
    private JMenu help; 
    private JMenuItem instructions; 
    private JMenuItem about; 

    /** 
    * 
    */ 
    private class ButtonHandler implements ActionListener { 
     @Override public void actionPerformed(ActionEvent evt) { 
     } 
    } 

    /** 
    * Launch the application. 
    */ 
    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override public void run() { 
       try { 
        MineSweeperUI window = new MineSweeperUI(); 
        window.frame.setVisible(true); 
       } catch(Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    /** 
    * Create the application. 
    */ 
    public MineSweeperUI() { 
     initialize(); 
    } 

    /** 
    * Initialize the contents of the frame. 
    */ 
    private void initialize() { 
     frame = new JFrame("MineSweeper v1.00"); 
     frame.setResizable(false); 
     frame.setBounds(100, 100, 450, 300); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     content = new JPanel(); 
     content.setLayout(null); 

     grid = new MineSweeperGrid(10); 
     grid.setBounds(0, 0, 444, 251); 
     content.add(grid); 

     menuBar = new JMenuBar(); 
     frame.setJMenuBar(menuBar); 

     file = new JMenu("File"); 
     menuBar.add(file); 

     newGame = new JMenuItem("New Game"); 
     file.add(newGame); 

     options = new JMenuItem("Options..."); 
     file.add(options); 

     close = new JMenuItem("Close"); 
     file.add(close); 

     help = new JMenu("Help"); 
     menuBar.add(help); 

     instructions = new JMenuItem("How to play?"); 
     help.add(instructions); 

     about = new JMenuItem("About..."); 
     help.add(about); 

     frame.setContentPane(content); 
    } 
} 

正如你所看到的,該程序還沒有完成,還有一些部位有已被簡化(例如,當您打礦時顯示「M」,而不是處理丟失)以進行調試。這是堆棧跟蹤:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException 
    at org.mandal.data.MineSweeperGrid$MineListener.actionPerformed(MineSweeperGrid.java:68) 
    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.JToggleButton$ToggleButtonModel.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) 
+2

你可以看到異常堆棧跟蹤行號?如果是,那麼調試該行,看看什麼是null/ – 2013-02-18 12:58:34

+0

我該怎麼做?但編譯器說這行:'if(!btns [i] .safe)'(第二類的第68行)。 – 2013-02-18 12:59:38

+0

@ JavaNewbie_M107如果你在'if(!btns [i] .safe)'這個行中獲得NPE,那麼你的'btns'變量可能是null。 – 2013-02-18 13:02:04

回答

4

看來:

for(int i = 0; i < (size - 1); i++) 

應該是:

for(int i = 0; i < size; i++) 

否則你就不會填補了最後一次迭代的最後陣列grid[size - 1]grid[size - 1]指的是一個空數組:

{null, null, null, ...} // its length = size 

或者換句話說:

grid[size - 1][0] == null 
grid[size - 1][1] == null 
grid[size - 1][2] == null 
grid[size - 1][3] == null 
... 
grid[size - 1][size - 1] == null 
相關問題