How can “this” be referenced/processed before the

2019-04-28 06:59发布

The specific use where I thought of this problem is as follows, but it's much more generalized.

I have a custom JFrame class which also serves as an ActionListener for its components. So my constructor looks something like the following:

private JButton myButton;

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

My question is, how does this actually work behind the scenes? If the constructor is what "creates" the object which is referenced by this, how can I use this before the constructor has returned? The code compiles and works perfectly fine (as far as I can tell), so the object must already "exist" in some sense, but I'm concerned that this may cause unforeseen issues. Is there any danger in passing a "partially constructed" reference to addActionListener() (or just performing any logic with it in general)? Or is there some behind-the-curtain magic happening that keeps me safe?

For example, what about things which don't have default values and must be supplied by the constructor? If I have private final String SOME_VALUE; declared, I understand that this should default to null, but the object isn't supposed to be fully formed until the constant is supplied a value within the constructor. So would the reference, despite being final, possibly have changing values?

5条回答
混吃等死
2楼-- · 2019-04-28 07:09

That Java Language Specification specifies the steps of instance creation

[...]

Next, space is allocated for the new class instance. If there is insufficient space to allocate the object, evaluation of the class instance creation expression completes abruptly by throwing an OutOfMemoryError.

The new object contains new instances of all the fields declared in the specified class type and all its superclasses. As each new field instance is created, it is initialized to its default value (§4.12.5).

Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.

Next, the selected constructor of the specified class type is invoked. This results in invoking at least one constructor for each superclass of the class type. This process can be directed by explicit constructor invocation statements (§8.8) and is described in detail in §12.5.

So by the time the constructor (which is a method) is invoked, your instance exists with default values.

For final fields, it appears those are defaulted as well if you try to access them. For example

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

}

will print 0. I'll be right back with the JLS entry if I can find it.

I couldn't find anything more specific then what is above. Maybe in 4.12.4. final Variables

A final variable may only be assigned to once.

You can take to mean that default initialization puts the value to 0 or null and the assignment changes it.

查看更多
一夜七次
3楼-- · 2019-04-28 07:11

Don't escape this from the constructor because if another thread read variables of the instance which construction hasn't finished yet, the thread may read unexpected value.

A example is below.

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

This program may show a value other than 10 from Java Memory Model specification.

查看更多
在下西门庆
4楼-- · 2019-04-28 07:17

You are absolutely right, that is a bad thing to do as this may only be partially initialized at the time you use it.

This is why many compilers will give a warning about doing it.

查看更多
▲ chillily
5楼-- · 2019-04-28 07:20

this does actually exist before the constructor finishes. However, allowing a reference to this to escape your object before the constructor is complete can present a danger.

What if you passed your this reference to a method that assumes your object is fully formed and ready to go? Maybe this is fine in the case of your object, but in many cases, this could be dangerous. Allowing other methods access to an object before it is ready to be used would present a serious threat to your program being able to run reliably.

查看更多
Explosion°爆炸
6楼-- · 2019-04-28 07:21

Once you call your constructor, your object already exists from the start, and you're just populating it with values.

The danger of this comes if the method you're passing your object to tries to use a value that you haven't declared yet in your constructor.


You also want to avoid having your constructor(and other methods for that matter) behave in such a way that the user of the constructor wouldn't expect.

If the person instantiating your object doesn't have a reason to expect the constructor to automatically bind that object to a button, than maybe you shouldn't do that.

查看更多
登录 后发表回答