如何在构造函数结束之前引用/处理“this”?

时间:2013-12-16 19:26:24

标签: java constructor this

我想到这个问题的具体用法如下,但它更加通用。

我有一个自定义JFrame类,它也可以作为其组件的ActionListener。所以我的构造函数看起来如下所示:

private JButton myButton;

public MyCustomFrame() {
    super();
    myButton.addActionListener(this);
    // ... more stuff
}

我的问题是,这在幕后实际上是如何运作的?如果构造函数是“创建”this引用的对象的内容,那么在构造函数返回之前如何使用this?代码编译和工作完全正常(据我所知),因此对象必须已经在某种意义上“存在”,但我担心这可能会导致无法预料的问题。将“部分构造”的引用传递给addActionListener()(或者只是对它执行任何逻辑)是否有任何危险?或者是否有一些让我安全的幕后魔术?

例如,那些没有默认值但必须由构造函数提供的东西呢?如果我声明了private final String SOME_VALUE;,我理解这应该默认为null,但是在构造函数中为常量提供值之前,该对象不应该完全形成。那么参考文献尽管是最终的,但可能会有不断变化的值吗?

5 个答案:

答案 0 :(得分:9)

Java语言规范指定了instance creation

的步骤
  

[...]

     

接下来,为新的类实例分配空间。如果有   没有足够的空间来分配对象,评估类   实例创建表达式通过抛出一个突然完成   的OutOfMemoryError。

     

新对象包含声明的所有字段的新实例   指定的类类型及其所有超类。 作为每个新领域   实例创建后,它被初始化为默认值(§4.12.5)。

     

接下来,计算构造函数的实际参数,   左到右。如果任何参数评估突然完成,   不评估其右边的任何参数表达式和类   由于同样的原因,实例创建表达式突然完成。

     

接下来,调用指定类类型的选定构造函数。   这导致为每个超类调用至少一个构造函数   类类型。这个过程可以通过显式指导   构造函数调用语句(第8.8节)并在中详细描述   §12.5。

因此,在调用构造函数(这是一个方法)时,您的实例存在默认值。

对于final字段,如果您尝试访问它们,它们似乎也是默认的。例如

public class Driver {

    public static void main(String[] args) {
        new Driver();
    }

    final int value;

    public Driver() {
        print(this);
        value = 3;
    }

    static void print(Driver driver) {
        System.out.println(driver.value);
    }

}

将打印0.如果我能找到它,我将立即返回JLS条目。

我找不到更具体的内容,然后是上面的内容。也许在4.12.4. final Variables

  

最终变量只能分配一次。

您可以认为默认初始化将值设置为0或null,并且赋值会更改它。

答案 1 :(得分:4)

一旦调用构造函数,您的对象从一开始就已经存在,而您只是用值填充它。

如果您传递对象的方法试图使用您尚未在构造函数中声明的值,则会出现这种危险。


您还希望避免让构造函数(以及其他相关方法)以构造函数的用户不希望的方式运行。

如果实例化对象的人没有理由期望构造函数自动将该对象绑定到按钮,那么你可能不应该这样做。

答案 2 :(得分:1)

在构造函数完成之前,

this确实存在。但是,允许引用this在构造函数完成之前转义对象可能会带来危险。

如果您将this引用传递给假设您的对象已完全形成并准备就绪的方法,该怎么办?也许这对你的对象来说很好,但在很多情况下,这可能是危险的。允许其他方法在准备好使用之前访问该对象将对您的程序能够可靠地运行构成严重威胁。

答案 3 :(得分:1)

你是绝对正确的,这是一件坏事,因为this可能只在你使用时被部分初始化。

这就是许多编译器会发出警告的原因。

答案 4 :(得分:0)

不要从构造函数中转义它,因为如果另一个线程读取了构造尚未完成的实例的变量,则该线程可能会读取意外值。

下面是一个例子。

public class A {
  private final int value;
  public A(int value) {
    this.value = value;
    new Thread(new Runnable() { // this escape implicitly
       public void run() {
         System.out.println(value);
       }
    }).start();
  }
  public static void main(String[] args) {
    new A(10);
  }
}

此程序可能会显示Java Memory Model规范中的10以外的值。