对于匿名类,实际上是在声明“无名”嵌套类。对于嵌套类,编译器使用构造函数生成一个新的独立公共类,该构造函数将使用它用作参数的所有变量(对于“命名”嵌套类,这始终是原始/封闭类的实例)。这样做是因为运行时环境没有嵌套类的概念,因此需要从嵌套类到独立类的(自动)转换。
以下面的代码为例:
public class EnclosingClass {
public void someMethod() {
String shared = "hello";
new Thread() {
public void run() {
// this is not valid, won't compile
System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
那是行不通的,因为这是编译器在后台执行的操作:
public void someMethod() {
String shared = "hello";
new EnclosingClass$1(shared).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
原始的匿名类被编译器生成的一些独立类代替(代码不准确,但应该可以带给您一个好主意):
public class EnclosingClass$1 extends Thread {
String shared;
public EnclosingClass$1(String shared) {
this.shared = shared;
}
public void run() {
System.out.println(shared);
}
}
如您所见,独立类保留对共享对象的引用,请记住,java中的所有内容都是按值传递的,因此即使EnclosingClass中的引用变量'shared'被更改,它指向的实例也不会被修改,以及指向它的所有其他参考变量(例如匿名类中的Enclosing $ 1)将不会意识到这一点。这是编译器强迫您将“共享”变量声明为final的主要原因,这样,这种类型的行为就不会使其融入已经运行的代码中。
现在,这是在匿名类中使用实例变量时发生的事情(这是解决问题,将逻辑移至“实例”方法或类的构造函数时应采取的措施):
public class EnclosingClass {
String shared = "hello";
public void someMethod() {
new Thread() {
public void run() {
System.out.println(shared); // this is perfectly valid
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
这样编译就可以了,因为编译器将修改代码,因此新生成的类Enclosing $ 1将保存对实例化该实例的EnclosingClass实例的引用(这只是一种表示形式,但应该可以使您前进):
public void someMethod() {
new EnclosingClass$1(this).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
public class EnclosingClass$1 extends Thread {
EnclosingClass enclosing;
public EnclosingClass$1(EnclosingClass enclosing) {
this.enclosing = enclosing;
}
public void run() {
System.out.println(enclosing.shared);
}
}
像这样,当EnclosingClass中的参考变量“ shared”被重新分配,并且发生在调用Thread#run()之前,您会看到“ other hello”打印了两次,因为现在EnclosingClass $ 1#enclosing变量将保留一个参考。到声明该类的对象的对象,因此对该对象上任何属性的更改对于EnclosingClass $ 1的实例都是可见的。
有关该主题的更多信息,您可以查看以下出色的博客文章(不是我写的):http : //kevinboone.net/java_inner.html