NullPointerException in a 2D Array

I wrote this code (myself) that throws the NPE, which confused me. It includes a 2D array of Ray objects used in the model. Ray objects are initialized and set to the model. When they are required outside the model, the code calls model.getRays() . At this moment, the code iterates the 2D array and registers each object, because .. Only 2-3 lines after the getRays() code are getRays() , the first element of the 2D array is null !

There are no threads that can interfere with the objects in the array, so I'm confused by the way they can be null a few seconds after testing that they contain valid objects.

What is wrong with this code and how to fix it?

 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; } } 

Output

This shows the last part of the output, checking that getRays() will return, and then the 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) ... 
+4
source share
1 answer

Looking at it through my debugger, everything looks like this: ray[0] is null. Everything else is present.

So, digging into the trace, I believe that I found the reason why the data gets there, and why the first line is empty:

  • Call f.add(gui.getContainer()); . This seems harmless in itself, but let's see what getContainer does ...

  • We create an instance of ImageIcon icon = new ImageIcon(getCanvas()); . Again, harmless, but go down the rabbit hole again ...

  • Then we call (finally) initializeRays(g); in getCanvas() ! What's going on here? This is: for (int ii = rays.length - 1; ii > 0; ii--)

OK, you are counting back. Strange, and this needs to be done only in rare cases, but I think you have your own reasons. But, there are two catches to count back:

  • You need to start from scratch n-1, what do you do; and
  • Your loop condition must be greater than or equal to 0. You do not have this condition.

Modify it to include a greater or equal test, and you no longer have NPE.

+4
source

Source: https://habr.com/ru/post/1488820/


All Articles