实例初始化器和 *this* 关键字
Instance initializer and *this* keyword
正在尝试编译这段代码
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
也可以使它工作。
如 JSL (§15.8.3)
When used as a primary expression, the keyword this
denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed.
This
关键字,被调用后会在后台创建class的实例。当您调用 { System.out.println(this.x); }
时,变量 i
是用 Main
class 创建的。
我会尝试在编译层解释。
假设你有这样的方法:
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;
}
对应的Java字节码为ctor:
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
关键字加载 Main 对象
堆栈上的引用。之后可以访问字段 x,因此可以成功执行 System.out.println
调用。
JSL 8.6 应该解释你的编译时错误:
Instance initializers are permitted to refer to the current object via the keyword this (§15.8.3) ...
Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. See §8.3.3 for the precise rules governing forward reference to instance variables.
在§8.3.3中说:
Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. Specifically, it is a compile-time error if all of the following are true:
The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of C or an instance initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
这就是为什么写简单的名称 x
会给您带来错误。
Instance initializers are permitted to refer to the current object via
the keyword this (§15.8.3), to use the keyword super (§15.11.2,
§15.12), and to use any type variables in scope.
Initialization consists of execution of any class variable
initializers and static initializers of the class Test, in textual
order.
发生的事情是 jvm 在堆栈帧中为 x 分配内存。当您使用 this.x 时,返回 0,因为变量已经分配。
如果变量在初始化时是静态的,您将得到 1。
正在尝试编译这段代码
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
也可以使它工作。
如 JSL (§15.8.3)
When used as a primary expression, the keyword
this
denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed.
This
关键字,被调用后会在后台创建class的实例。当您调用 { System.out.println(this.x); }
时,变量 i
是用 Main
class 创建的。
我会尝试在编译层解释。
假设你有这样的方法:
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;
}
对应的Java字节码为ctor:
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
关键字加载 Main 对象
堆栈上的引用。之后可以访问字段 x,因此可以成功执行 System.out.println
调用。
JSL 8.6 应该解释你的编译时错误:
Instance initializers are permitted to refer to the current object via the keyword this (§15.8.3) ...
Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. See §8.3.3 for the precise rules governing forward reference to instance variables.
在§8.3.3中说:
Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. Specifically, it is a compile-time error if all of the following are true:
The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of C or an instance initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
这就是为什么写简单的名称 x
会给您带来错误。
Instance initializers are permitted to refer to the current object via the keyword this (§15.8.3), to use the keyword super (§15.11.2, §15.12), and to use any type variables in scope.
Initialization consists of execution of any class variable initializers and static initializers of the class Test, in textual order.
发生的事情是 jvm 在堆栈帧中为 x 分配内存。当您使用 this.x 时,返回 0,因为变量已经分配。 如果变量在初始化时是静态的,您将得到 1。