在类的构造函数中初始化的实例变量在重写方法中变为null



这里有一个简单的类,它扩展了ACM图形库中一个名为GRect的类。Grect是一个可以绘制为GCanvas(也是ACM的一部分)的基本矩形。我想要实现的是一个新的对象,它是一个矩形,但有一个随矩形移动的附加标签。

在我的代码中,我创建了一个名为labelRect的类,它扩展了GRect,并包含一个GLabel类型的实例变量"rectLabel",该变量在labelRects构造函数中初始化。我想覆盖GRect的一些方法,这样当labledRect被移动时,rectLabel就会随之移动。

我的问题是,尽管"rectLabel"被声明为实例变量,并在构造函数中初始化,但它在重写的方法"setLocation"中变为NULL。我还尝试在声明时初始化"rectLabel",但出现了同样的问题。

import acm.graphics.*;
public class labeledRect extends GRect {
    //GLabel declared as an instance variable
    public GLabel rectLabel;
    public labeledRect(double x, double y, double width, double height, String theLabel) {
        //Call GRect constructor
        super(x, y, width, height);
        //Label initialized. 
        //Location setting and adding to the canvas works fine here. 
        rectLabel = new GLabel(theLabel);
        rectLabel.setLocation(
                    x + (width / 2) - (rectLabel.getWidth() / 2),
                    y + (height / 2) + (rectLabel.getAscent() / 2.2));
    }
    public void setLocation(double x, double y)
    {
        //Setting GRect's location. Works correctly./
        super.setLocation(x, y);
        //Attempt to set the label's location 
        //and get a NullPointer exception for rectLabel
        rectLabel.setLocation(
                super.getX() - (rectLabel.getWidth() / 2),
                super.getY() - (rectLabel.getHeight() / 2));
    }
}

考虑到变量是一个公共字段,很难确切地推断是什么将其设置为null。这是你应该改变的第一件事。字段几乎应该总是是私有的。

接下来,您说setLocation是一个重写的方法——它在GRect构造函数中被调用了吗?如果是这样,则在值在labeledRect构造函数中设置之前,将调用,这很可能是问题的原因。

在Java中,超类构造函数在子类中任何代码的之前执行,既在实例变量初始化器之前,也在构造函数主体之前所有变量都将具有其默认值。这就是为什么从构造函数中调用虚拟方法是个坏主意。

(顺便说一句,名称labeledRect不遵循Java命名约定。)