实例初始值设定项和*this*关键字

Lui*_*ese 25 java initialization

试图编译这段代码

public class Main {

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

    { System.out.println(x); } //Error here

    int x=1;
}
Run Code Online (Sandbox Code Playgroud)

产生cannot reference a field before it is defined错误.但是如果我将初始化器行更改为

    { System.out.println(this.x); }
Run Code Online (Sandbox Code Playgroud)

它就像一个魅力,打印默认的int值0.

这对我来说有点混乱,为什么会this有所不同呢?在这种情况下,它不应该是多余的吗?任何人都可以解释我幕后发生的事情,以明确它是如何工作的吗?

PS:我知道通过x在初始化程序之前声明它也可以使它工作.

Zel*_*don 11

我将尝试在编译器层上进行解释.

假设你有一个方法:

int x;
x = 1;
System.out.println(x);
Run Code Online (Sandbox Code Playgroud)

编译将成功并执行.如果您将方法更改为:

System.out.println(x);
int x;
x = 1;
Run Code Online (Sandbox Code Playgroud)

它甚至不会与您给出的示例编译相同.

编译器复制的代码{ }初始化器进入 ctor,也是x=1初始化.

正如你所说,如果你x=1{ }初始化器之前设置它就有效.

public class MainC {

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

    int x=1;
    {
      System.out.println(x);
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅以下Java字节码:

  public MainC();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field x:I
         9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        12: aload_0
        13: getfield      #2                  // Field x:I
        16: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        19: return
      LineNumberTable:
        line 1: 0
        line 7: 4
        line 9: 9
        line 10: 19
Run Code Online (Sandbox Code Playgroud)

x声明该字段并1System.out.println调用中使用之前 获取该值.

那么为什么它不起作用如果你设置它之后,{ }你不能使用我的第二个例子的代码.在使用之后声明该字段是没有意义的.

那么为什么它适用于this关键字?!

让我们看一些代码:

public class Main {

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

    { System.out.println(this.x); } //Error here

    int x=1;
}
Run Code Online (Sandbox Code Playgroud)

ctor的相应Java字节码:

  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: aload_0
         8: getfield      #3                  // Field x:I
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        14: aload_0
        15: iconst_1
        16: putfield      #3                  // Field x:I
        19: return
      LineNumberTable:
        line 1: 0
        line 7: 4
        line 9: 14
Run Code Online (Sandbox Code Playgroud)

所以这里发生了什么?轻松说出this关键字加载堆栈上的主对象引用.之后,可以访问字段x,以便System.out.println可以成功执行调用.


dej*_*uth 5

JSL 8.6应解释您的编译时错误:

允许实例初始值设定项通过关键字this(§15.8.3)引用当前对象...

使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此.有关控制实例变量的前向引用的精确规则,请参见§8.3.3.

§8.3.3中,它说:

使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此.具体来说,如果满足以下所有条件,则为编译时错误:

  • 在使用实例变量之后,类或接口C中的实例变量的声明以文本形式出现;

  • 在C的实例变量初始值设定项或C的实例初始值设定项中使用是一个简单的名称 ;

  • 使用不在作业的左侧;

  • C是封闭使用的最内层类或接口.

这就是为什么写简单名称x会给你带来错误.