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

试图编译这段代码

public class Main { public static void main(String args[]) { new Main(); } { System.out.println(x); } //Error here int x=1; } 

生成一个cannot reference a field before it is defined错误cannot reference a field before it is defined 。 但是,如果我将初始化行更改为

  { System.out.println(this.x); } 

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

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

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

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

假设你有一个像这样的方法:

 int x; x = 1; System.out.println(x); 

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

 System.out.println(x); int x; x = 1; 

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

编译器将{ }初始化程序的代码复制到ctor以及x=1初始化。

如你所说,如果在{ }初始化器之前设置x=1 ,它就可以工作。

 public class MainC { public static void main(String args[]) { new MainC(); } int x=1; { System.out.println(x); } } 

请参阅以下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."":()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 

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

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

那么为什么它与this关键字一起使用?!

让我们看一些代码:

 public class Main { public static void main(String args[]) { new Main(); } { System.out.println(this.x); } //Error here int x=1; } 

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."":()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 

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

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

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

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

在§8.3.3中 ,它说:

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

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

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

  • 使用不在作业的左侧;

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

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

与JSL一样( §15.8.3 )

当用作主表达式时,关键字this表示一个值,该值是对调用实例方法的对象 (第15.12节 )的引用 ,或者是对正在构造的对象的引用

调用This关键字然后在后台创建类的实例。 当你调用{ System.out.println(this.x); } 变量i是用Main类创建的。

允许实例初始值设定项通过关键字this(§15.8.3)引用当前对象,以使用关键字super(§15.11.2,§15.12),并在范围内使用任何类型变量。

初始化包括以文本顺序执行类Test的任何类变量初始化器和静态初始化器。

会发生什么是jvm在堆栈帧中为x分配内存。 当您使用this.x时,返回0,因为已经分配了变量。 如果变量在初始化时是静态的,您将得到1。