【发布时间】:2010-05-04 09:07:18
【问题描述】:
您好,我正在阅读 SCJP 关于内部类的书,发现了这个语句,它是这样的。
方法局部类只能引用标记为
final的局部变量
在解释中指定的原因是关于局部类对象的范围和生命周期以及堆上的局部变量,但我无法理解。我在这里错过任何关于final 的信息吗?
【问题讨论】:
标签: java
您好,我正在阅读 SCJP 关于内部类的书,发现了这个语句,它是这样的。
方法局部类只能引用标记为
final的局部变量
在解释中指定的原因是关于局部类对象的范围和生命周期以及堆上的局部变量,但我无法理解。我在这里错过任何关于final 的信息吗?
【问题讨论】:
标签: java
原因是,当方法局部类实例被创建时,它所引用的所有方法局部变量实际上都被编译器复制到了其中。这就是为什么只能访问final 变量的原因。 final 变量或引用是不可变的,因此它与方法本地对象中的副本保持同步。如果不是这样,在创建方法本地类之后,原始值/引用可能会被更改,从而让位于令人困惑的行为和细微的错误。
考虑JavaSpecialist newsletter no. 25中的这个例子:
public class Access1 {
public void f() {
final int i = 3;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(i);
}
};
}
}
编译器将内部类变成这样:
class Access1$1 implements Runnable {
Access1$1(Access1 access1) {
this$0 = access1;
}
public void run() {
System.out.println(3);
}
private final Access1 this$0;
}
由于
i的值是final,编译器可以将其“内联”到内部类中。
【讨论】:
在我看来,从方法本地类(例如匿名类)访问局部变量是一件有风险的事情。编译器允许这样做,但需要很好地理解正在发生的事情。
当内部类被实例化时,它使用的所有对局部变量的引用都被复制,并作为隐式构造函数参数传递(检查字节码)。实际上,编译器本可以允许将引用设为非最终引用,但这会造成混淆,因为如果方法在实例化后更改引用会发生什么,则不清楚。
但是,将引用设为 final 并不能消除所有问题。虽然引用是不可变的,但引用后面的对象可能仍然是可变的。在内部类的实例化直到其激活之间所做的任何对象突变都会被内部类看到,有时这不是程序员的意图。
【讨论】: