实例初始值设定项和* 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。