尝试编译这段代码
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
错误。但是如果我将初始化器行更改为
{ System.out.println(this.x); }
它就像一个魅力,打印默认的int值 0 。
这对我来说有点困惑,为什么this
会有所不同?在这种情况下,它不应该是多余的吗?任何人都可以向我解释幕后发生的事情,以明确它是如何运作的吗?
PS:我知道通过在初始化程序之前声明x
也可以使其正常工作。
答案 0 :(得分:11)
我将尝试在编译器层上进行解释。
假设你有一个方法:
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."<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
声明字段x
并在使用之前获取值1
System.out.println
致电。
那么,如果你在{ }
之后根据同样的原因设置它,它为什么不起作用
你不能使用我的第二个例子的代码。在使用之后声明该字段是没有意义的。
那么为什么它适用于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."<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
所以这里发生了什么?很容易说this
关键字加载主对象
堆栈上的引用。之后,可以访问字段x,以便System.out.println
调用可以成功执行。
答案 1 :(得分:5)
JSL 8.6应解释您的编译时错误:
允许实例初始值设定项通过关键字this(§15.8.3)引用当前对象...
使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此。有关控制实例变量的前向引用的精确规则,请参见§8.3.3。
在§8.3.3中,它说:
使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此。具体来说,如果以下全部为真,则为编译时错误:
在使用实例变量后,类或接口C中的实例变量声明以文本形式出现;
在C的实例变量初始值设定项或C的实例初始值设定项中使用简单名称;
使用不在作业的左侧;
C是封闭使用的最里面的类或接口。
这就是为什么写简单名x
会给你错误。
答案 2 :(得分:1)
与JSL(§15.8.3)
一样调用当用作主表达式时,关键字
this
表示一个值,是对调用实例方法的对象的引用(§15.12),或者到正在建造的物体。
This
关键字,然后在后台创建类的实例。当您使用{ System.out.println(this.x); }
类创建i
变量Main
时。
答案 3 :(得分:0)
允许实例初始值设定项通过引用当前对象 关键字this(§15.8.3),使用关键字super(§15.11.2, §15.12),并在范围内使用任何类型变量。
初始化包括执行任何类变量 初始化程序和类Test的静态初始化程序,以文本形式表示 顺序。
jvm在堆栈帧中为x分配内存会发生什么。当您使用this.x时,返回0,因为已经分配了变量。 如果变量在初始化时是静态的,你将得到1。