2D数组中的NullPointerException

我写了这个代码(自包含),它引发了一个令我困惑的NPE。 它涉及模型中使用的2D对象数组。 Ray对象被初始化并设置为模型。 当在模型外部需要它们时,代码调用model.getRays() 。 此时代码迭代2D数组并记录每个对象,因为..在代码调用getRays()之后仅仅2-3行,2D数组的第一个元素为null

没有任何线程可能会干扰数组中的对象,所以我很困惑它们在测试它们包含有效对象后的瞬间如何为null

这段代码有什么问题?如何解决?

 import java.awt.*; import java.awt.event.ActionEvent; import java.awt.geom.*; import java.awt.image.BufferedImage; import java.util.logging.*; import javax.swing.*; import javax.swing.border.EmptyBorder; public class TestNullOn2DArray { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { GUI gui = new GUI(); JFrame f = new JFrame("Test Null On 2D Array"); f.add(gui.getContainer()); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.setLocationByPlatform(true); f.pack(); f.setMinimumSize(f.getSize()); f.setVisible(true); gui.plot(); } }; SwingUtilities.invokeLater(r); } } class GUI { Logger log = Logger.getAnonymousLogger(); private JPanel container; JLabel output; private JMenuBar menuBar; private BufferedImage canvas; private CustomModel model = new CustomModel(5, 1, .66f); private int defaultPositionNumber = 5; private int defaultAngleNumber = 5; public void plot() { Ray[][] rays = model.getRays(); log.log(Level.INFO, "Rays: " + rays); for (int ii = 0; ii < rays.length; ii++) { for (int jj = 0; jj  0; ii--) { for (int jj = 0; jj < rays[0].length; jj++) { GeneralPath gp = new GeneralPath(); double rads = 2d * Math.PI * (startAngle + (inc * ii)) / 360d; double x = 400d; double y = 100d; double yStart = y - (Math.sin(rads) * 100d); double xStart = x + (Math.cos(rads) * 100d); float r = 2f * (float) ((rads / Math.PI)); float b = (float) jj / (float) rays[0].length; Color color = new Color(r, 1 - r, b, .6f); g.setColor(color); g.drawLine((int) x, (int) y, (int) xStart, (int) yStart); gp.moveTo(xStart, yStart); gp.lineTo(x, y); gp.closePath(); Ray ray = new Ray(gp, color); log.log(Level.INFO, "" + ray); rays[ii][jj] = ray; } } model.setRays(rays); if (output != null) { output.repaint(); } } public BufferedImage getCanvas() { if (canvas == null) { Dimension size = model.getPreferredSize(); int w = size.width; int h = size.height; canvas = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g = canvas.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, w, h); g.setColor(new Color(125, 27, 155, 127)); initializeRays(g); g.dispose(); } return canvas; } } class CustomModel { private int width; private int layers; private float offset; private Ray[][] rays; private Logger log = Logger.getAnonymousLogger(); public CustomModel(int width, int layers, float offset) { this.width = width; this.layers = layers; this.offset = offset; } public Line2D.Double getLineOfLasSegment() { Line2D.Double line = new Line2D.Double(0d, 0d, 1d, 1d); return line; } public Dimension getPreferredSize() { int w = 600; int h = 300; Dimension prefSize = new Dimension(w, h); return prefSize; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getLayers() { return layers; } public void setLayers(int layers) { this.layers = layers; } public Ray[][] getRays() { for (int ii = 0; ii < rays.length; ii++) { for (int jj = 0; jj < rays[ii].length; jj++) { log.log(Level.INFO, "Ray: " + this.rays[ii][jj]); } } return rays; } public void setRays(Ray[][] rays) { this.rays = rays; for (int ii = 0; ii < rays.length; ii++) { for (int jj = 0; jj < rays[ii].length; jj++) { //Ray ray = rays[ii][jj]; this.rays[ii][jj] = rays[ii][jj]; log.log(Level.INFO, "Ray: " + this.rays[ii][jj]); } } } public float getOffset() { return offset; } public void setOffset(float offset) { this.offset = offset; } } class Ray { private final Logger logger = Logger.getAnonymousLogger(); private GeneralPath path; private boolean started = false; private Color color; Ray(GeneralPath path, Color color) { this.path = path; this.color = color; } public void resolve() { logger.log(Level.INFO, "..resolving."); } public GeneralPath getPath() { return path; } public boolean isStarted() { return started; } @Override public String toString() { String s = "Ray: " + " started=" + started; return s; } } 

产量

这显示了输出的最后一部分,检查getRays()将返回什么,然后是NPE。

 // ... Jun 29, 2013 4:56:55 PM CustomModel getRays INFO: Ray: Ray: started=false Jun 29, 2013 4:56:55 PM GUI plot INFO: Rays: [[LRay;@1a9876e Jun 29, 2013 4:56:55 PM GUI plot INFO: Ray: null Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at GUI.plot(TestNullOn2DArray.java:52) at TestNullOn2DArray$1.run(TestNullOn2DArray.java:26) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:727) at java.awt.EventQueue.access$200(EventQueue.java:103) at java.awt.EventQueue$3.run(EventQueue.java:688) at java.awt.EventQueue$3.run(EventQueue.java:686) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:697) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146) ... 

通过我的调试器查看它,看起来ray[0]所有内容都为空。 其他一切存在。

因此,通过跟踪挖掘,我相信已经找到了数据放在首位的原因, 以及为什么第一行为空:

  1. 我们调用f.add(gui.getContainer()); 。 这本身似乎无害,但让我们看看getContainer在做什么……

  2. 我们实例化ImageIcon icon = new ImageIcon(getCanvas()); 。 再次,无害,但让我们再次走下兔子洞……

  3. 然后我们调用(finally) initializeRays(g);getCanvas() ! 这里出了什么问题? 这个: for (int ii = rays.length - 1; ii > 0; ii--)

好的,你倒数了。 很奇怪,只有在极少数情况下才应该这样做,但我想你有理由。 但是,有两个向后计数:

  • 你需要从n-1开始,你这样做; 和
  • 您的循环条件必须大于或等于 0.您没有这个条件。

将其更改为包含大于或等于测试,并且您将不再拥有NPE。