匿名 Java 类是在运行时创建的还是由编译器提前创建的?
根据 Java 文档,
They are like local classes except that they do not have a name
,所以我猜测它们是提前创建的。 如果您可以引用您的来源或知道如何测试这样的事情,请告诉我!
它们是由编译器创建的。您只需编译一些代码并查看磁盘上的内容即可看到它们。您最终会得到类似
Foo$1.class
的内容,其中 Foo
是包含匿名方法的类。
例如:
public class Test {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override public void run() {
System.out.println("Hi");
}
};
}
}
> javac Test.java
> dir Test*.class
Test.class
Test$1.class
> javap -c Test$1
Compiled from "Test.java"
final class Test$1 implements java.lang.Runnable {
Test$1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void run();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hi
5: invokevirtual #4 // Method java/io/PrintStream.println[...]
8: return
}
对于JVM而言,它们只是普通的类。编译器添加的各种语言功能(例如相关的编码实例)是通过额外的方法、构造函数参数和字段来实现的,这些方法、构造函数参数和字段通过具有您无法引用的名称而有效地对您隐藏。
这并不是说生成的类没有名称,实际上 - 只是强烈建议不要使用手动代码的名称。从 JLS 3.8 开始:
“Java 字母”包括大写和小写 ASCII 拉丁字母 A-Z (\u0041-\u005a) 和 a-z (\u0061-\u007a),并且由于历史原因,还包括 ASCII 下划线(_ 或 \u005f)和美元符号($ 或 \u0024)。 $ 符号应该仅在机械生成的源代码中使用,或者很少用于访问遗留系统上预先存在的名称。
所以理论上你可以在源代码中访问
Test$1
- 但javac
似乎以我尚未理解的方式阻止你这样做。 (我认为它使用类文件中的元数据来检查它是否是从匿名内部类编译的。)从语言的角度来看,这使得它“或多或少”是匿名的。
匿名类的字节码是在编译时创建的。只需检查您编译的类文件即可确认。匿名类以其封闭类命名,后面带有
$
符号和递增的数字。