我正在 Java 中使用前向引用,想知道为什么 Java 允许使用ClassName
(在静态变量中)或在实例变量的情况下使用this
引用进行正向引用?在 JVM 级别发生的后台进程是什么?例如:
静态前向参考-
class StaticForwardReferences {
static {
sf1 = 10; // (1)
int b = sf1 = 20; // (2)
int c = StaticForwardReferences.sf1; // (3) Works fine
// Above statement allows the allocation of value of 'sf1'
// to variable 'c' just because it is accessed with class name
// instead of direct name
// whereas below statement throws illegal forward reference
// error at compile time
System.out.println(sf1); // (4) Illegal forward reference
}
static int sf1 = sf2 = 30;
static int sf2;
public static void main(String[] args) {}
}
当我们通过在上面的 (1) 和 (2) 步骤中通过赋值变量在声明之前分配变量来进行前向引用时,是否有任何类型的临时存储来存储值,如果我们打印变量c
的值,它会显示最近分配给sf1
的值。
非静态前向参考-
class NonStaticForwardReferences {
{
nsf1 = 10;
System.out.println(this.nsf1); // 10
nsf1 = sf1;
// System.out.println(nsf1); Illegal forward reference
int b = nsf1 = 20;
int c = this.nsf1;
System.out.println(c); // 20
// why variable 'c' is initialized to 20 when used with 'this' reference
// instead of showing illegal forward reference, how it works in the background?
}
int nsf1 = nsf2 = 30;
int nsf2;
static int sf1 = 5;
public static void main(String[] args) {}
}
请对上述两个案例在幕后发生的后台过程有所了解。提前感谢!:)
JLS 第 8.3.3 节说明了对静态变量("类变量")的前向引用成为编译器错误的条件,尽管它没有说明原因。
使用类变量,其声明在使用后以文本形式显示有时会受到限制,即使这些类变量在作用域中也是如此 (§6.3)。具体而言,如果满足以下所有条件,则为编译时错误:
类或接口 C 中类变量的声明在使用类变量后以文本方式显示;
用法是 C 的类变量初始值设定项或 C 的静态初始值设定项中的简单名称;
用途不在作业的左侧;
C 是最里面的类或接口,包含使用。
(斜体强调我的)
实例变量转发引用错误以类似的条件出现:
使用实例变量,其声明在使用后以文本方式显示有时会受到限制,即使这些实例变量在范围内也是如此。具体而言,如果满足以下所有条件,则为编译时错误:
类或接口 C 中实例变量的声明在使用实例变量后以文本方式显示;
用法是 C 的实例变量初始值设定项或 C 的实例初始值设定项中的简单名称;
用途不在作业的左侧;
C 是最里面的类或接口,包含使用。
(斜体强调我的)
在这两种情况下,使用简单名称都是这里的关键条件。 这意味着使用不带任何限定符(如this
或类名)的变量。 这就是为什么this.nsf1
在NonStaticForwardReferences
工作的原因。 如果删除this
,则会发生错误。 这也是为什么sf1
有错误而StaticForwardReferences.sf1;
没有错误的原因。
Java 要求字段的声明必须在任何初始值设定项中使用之前发生 表达式(如果字段用于初始值设定项中赋值的右侧) 表达。这实质上意味着字段的声明必须发生在 字段的值在初始值设定项表达式中读取。
使用简单名称进行前向引用时,静态初始值设定项块中的代码为 还须遵守上文讨论的先声明后宣读规则。
与在实例初始值设定项表达式中一样,关键字"this"和"super"可用于 引用实例初始值设定项块中的当前对象。
禁止前向引用并不意味着在初始化时创建变量。 "后台进程"只是一次分配所有类和对象,并赋予(静态)变量默认值。 (也就是说"零":在实践中,它可能通过calloc
或memset
来实现。
前向引用规则旨在防止看到这些第一个值,特别是对于final
应该只有一个值的变量。 但是它们的存在是为了捕捉人为错误,并且本身并不完美:只有对于常量变量,初始化才会发生得如此之早,以至于默认值是不可观察的。 显式指定this
或类名是演示此效果的最简单方法。