当我使用this
关键字访问类中的非静态变量时,Java 不会给出任何错误。但是当我不使用它时,Java会给出一个错误。为什么我必须使用this
?
我知道通常什么时候应该使用this
,但此示例与正常用法有很大不同。
例:
class Foo {
// int a = b; // gives error. why ?
int a = this.b; // no error. why ?
int b;
int c = b;
int var1 = this.var2; // very interesting
int var2 = this.var1; // very interesting
}
完整的描述在 Java 语言规范的第 8.3.3 节中:">字段初始化期间的前向引用">
前向引用(指当时尚未声明的变量)仅在以下情况都为真时才是一个错误:
类或接口 C 中实例变量的声明在使用实例变量后以文本方式显示;
用法是 C 的实例变量初始值设定项或 C 的实例初始值设定项中的简单名称;
用途不在作业的左侧;
C 是最里面的类或接口,包含使用。
请参阅粗体文本:"用途是一个简单的名称"。简单名称是没有进一步限定的变量名称。在您的代码中,b
是一个简单的名称,但this.b
不是。
但是为什么?
原因是,正如 JLS 示例中的草书文本所述:
"上述限制旨在在编译时捕获 循环或其他格式错误的初始化。">
换句话说,他们允许this.b
,因为他们认为合格的参考让你更有可能仔细考虑你正在做的事情,但简单地使用b
可能意味着你犯了一个错误。
这就是Java语言设计者的基本原理。据我所知,这在实践中是否属实,从未被研究过。
初始化顺序
为了扩展上述内容,参考Dukeling对该问题的评论,使用合格的参考this.b
可能不会给你你想要的结果。
我将此讨论限制为实例变量,因为 OP 仅引用它们。 实例变量的赋值顺序在 JLS 12.5 创建新类实例中描述。 您需要考虑到首先调用超类构造函数,并且初始化代码(赋值和初始化块)按文本顺序执行。
所以给定
int a = this.b;
int b = 2;
你最终会得到a
为零(执行a
的初始值设定项时b
的值),b
为2。
如果超类构造函数调用在子类中被重写的方法,并且该方法为b
赋值,则可以获得更奇怪的结果。
因此,一般来说,相信编译器并重新排序您的字段或在循环初始化的情况下修复潜在问题是一个好主意。
如果您需要使用this.b
来解决编译器错误,那么您可能正在编写的代码很难由您之后的人维护。
首先声明变量,然后分配变量。该类与此相同:
class Foo {
int a;
int b;
int c = b;
int var1;
int var2;
public Foo() {
a = b;
var1 = var2;
var2 = var1;
}
}
你不能做int a = b;
的原因是b
在创建对象时还没有定义,而是对象本身(即this
) 与其所有成员变量一起存在。
以下是每种方法的说明:
int a = b; // Error: b has not been defined yet
int a = this.b; // No error: 'this' has been defined ('this' is always defined in a class)
int b;
int c = b; // No error: b has been defined on the line before
您提出了 3 个案例:
int a = b; int b;
这会产生错误,因为编译器将在内存中查找b
并且它不存在。 但是当你使用this
关键字时,它会显式指定b
是在类的作用域中定义的,所有类引用都将被查找,最后它会找到它。- 第二种情况非常简单,正如我所描述的,
b
是在c
之前的作用域中定义的,并且在内存中查找b
时不会成为问题。 -
int var1 = this.var2;
int var2 = this.var1;
在这种情况下没有错误,因为在每种情况下,变量都是在类中定义的,赋值使用this
,它将在类中查找分配的变量,而不仅仅是它后面的上下文。
对于 Java 中的任何类,this
是一个默认的引用变量(当没有给出特定的引用时),用户可以给出它,或者编译器将在非静态块内提供。例如
public class ThisKeywordForwardReference {
public ThisKeywordForwardReference() {
super();
System.out.println(b);
}
int a;
int b;
public ThisKeywordForwardReference(int a, int b) {
super();
this.a = a;
this.b = b;
}
}
您说int a = b; // gives error. why ?
会产生编译时错误,因为b
是在a
之后声明的,这是 Java 中的一个Illegal Forward Reference
,并被视为编译时错误。
但在methods
Forward Reference
的情况下,它变得合法
int a = test();
int b;
int test() {
return 0;
}
但是在我的代码中,带有参数的构造函数是在a
和b
之前声明的,但不给出任何编译时错误,因为编译器System.out.println(b);
将被System.out.println(this.b);
替换。
关键字this
仅表示当前类引用或访问方法、构造函数或属性的引用。
A a1 = new A(); // Here this is nothing but a1
a1.test(); // Here this is again a1
当我们说a = this.b;
时,它指定b
是当前类属性,但是当我们说a = b;
因为它不在非静态块内时,this
将不存在,并且会查找之前声明的属性不存在。
请查看 Java 语言规范: https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2.3
这就是原因,海事组织:The usage is via a simple name.
因此,在这种情况下,您必须使用this
.