使用匿名类,您实际上是在声明一个“无名”嵌套类。对于嵌套类,编译器生成一个带有构造函数的新的独立公共类,该构造函数将它使用的所有变量作为参数(对于“命名”嵌套类,这始终是原始/封闭类的实例)。这样做是因为运行时环境没有嵌套类的概念,因此需要从嵌套类(自动)转换为独立类。
以这段代码为例:
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 中的所有内容都是按值传递的,因此即使 EnclosureClass 中的引用变量 'shared' 发生更改,它指向的实例也不会被修改,以及指向它的所有其他引用变量(如匿名类中的一个:Enclosure$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);
}
}
这编译得很好,因为编译器将修改代码,以便新生成的类 Enclosure$1 将保存对 EnclosureClass 实例的引用(这只是一个表示,但应该让你继续):
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);
}
}
像这样,当 EnclosureClass 中的引用变量 'shared' 被重新分配时,并且这发生在调用 Thread#run() 之前,你会看到“other hello”打印了两次,因为现在 EnclosureClass$1#enclosing 变量将保留一个引用到声明它的类的对象,因此对该对象的任何属性的更改对于 EnclosureClass$1 的实例都是可见的。
有关该主题的更多信息,您可以查看这篇出色的博客文章(不是我写的): http: //kevinboone.net/java_inner.html