二维数组中的 NullPointerException
Posted
技术标签:
【中文标题】二维数组中的 NullPointerException【英文标题】:NullPointerException in 2D array 【发布时间】:2013-06-26 23:46:17 【问题描述】:我编写了这段代码(自包含),它引发了一个让我感到困惑的 NPE。它涉及模型中使用的Ray
对象的二维数组。 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 < rays[ii].length; jj++)
Ray ray = rays[ii][jj];
log.log(Level.INFO, "Ray: " + ray);
ray.resolve();
public JPanel getContainer()
if (container == null)
container = new JPanel(new BorderLayout(5, 5));
container.setBorder(new EmptyBorder(4, 4, 4, 4));
ImageIcon icon = new ImageIcon(getCanvas());
output = new JLabel(icon);
container.add(new JScrollPane(output), BorderLayout.CENTER);
return container;
public void refreshCanvas()
model = new CustomModel(5, 6, .66f);
canvas = null;
BufferedImage bi = getCanvas();
output.setIcon(new ImageIcon(bi));
public void initializeRays(Graphics2D g)
log.log(Level.INFO, "initializeRays()");
int startAngle = 1;
int numberAngles = defaultAngleNumber;
int inc = 90 / numberAngles;
int numberPositions = defaultPositionNumber;
Ray[][] rays = new Ray[numberAngles][numberPositions];
g.setTransform(AffineTransform.getTranslateInstance(0d, 0d));
for (int ii = rays.length - 1; ii > 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)
...
【问题讨论】:
你知道从一个问题中粘贴代码的感觉有多好吗,不让 IntelliJ 显示大量红色,并且实际使用运行?至少为此向你致敬。 应该这样问问题:) @Makoto 作为SSCCE 文档的作者,我可以向您保证,是的,我熟悉这种经历。 ;) 我犹豫是否称其为 SSSCE,因为在超过 200 LOC 时,许多人不会认为它“短”,但它始终是自给自足的。 【参考方案1】:通过我的调试器查看它,看起来ray[0]
的所有内容都是空的。 其他的一切都存在。
所以,通过跟踪挖掘,我相信首先找到了数据放在那里的原因,以及为什么第一行是空的:
我们打电话给f.add(gui.getContainer());
。这本身似乎是无害的,但让我们看看 getContainer
在做什么......
我们实例化ImageIcon icon = new ImageIcon(getCanvas());
。再次,无害,但让我们再次进入兔子洞......
然后我们(最终)在getCanvas()
中调用initializeRays(g);
!这里出了什么问题? 这个: for (int ii = rays.length - 1; ii > 0; ii--)
好吧,你倒数倒数。很奇怪,只有在极少数情况下才应该这样做,但我想你有你的理由。但是,倒数有两个问题:
你需要从长度 n-1 开始,你这样做;和 您的循环条件必须大于或等于 0。您确实不具有该条件。将其更改为包含大于或等于测试,您将不再有任何 NPE。
【讨论】:
冠军!修复是for (int ii=0; ii<rays.length; ii++)
。至于为什么我要向后迭代数组。老实说,我不记得了!前进.. :)
(自我注意)为自己的后部提供硬踢 a) 不计算屏幕上的光线数量并检查它们是否等于预期的数量。 & b) 不检查getRays()
输出的start(清楚地 将第一个索引显示为null
)。 c) 对循环使用倒数索引(没有明显原因)。以上是关于二维数组中的 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章