Java中的内部类在编译后会生成独立的.class文件,其命名遵循特定的规则,其中最常见的就是外部类名称后接$符号再接内部类名称的形式,也就是我们看到的Outer$Inner.class文件。
内部类class文件的基础命名规则
内部类的.class文件命名核心格式为外部类全限定名$内部类名称.class,其中$符号是分隔外部类和内部类的关键标识,这是Java编译器约定俗成的规则,目的是区分内部类和独立的顶层类。
不同种类的内部类命名会有细微差异:
- 普通成员内部类:外部类名$内部类名.class
- 静态内部类:命名规则和普通成员内部类一致,同样是外部类名$内部类名.class
- 局部内部类:外部类名$数字+内部类名.class,数字表示该类在方法中的定义顺序,从1开始计数
- 匿名内部类:外部类名$数字.class,数字表示匿名内部类的定义顺序,从1开始计数
Outer$Inner文件的生成示例
我们编写一个包含普通成员内部类的Outer类,查看编译后的产物:
// 外部类Outer
public class Outer {
// 成员内部类Inner
public class Inner {
public void print() {
System.out.println("这是内部类的方法");
}
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.print();
}
}
使用javac命令编译Outer.java后,会生成两个class文件:
- Outer.class:对应外部类本身
- Outer$Inner.class:对应内部类Inner,这就是Outer$Inner文件的由来
其他内部类场景的命名验证
静态内部类场景
静态内部类的命名和成员内部类一致,修改上面的Inner为静态内部类:
public class Outer {
// 静态内部类Inner
public static class Inner {
public void print() {
System.out.println("这是静态内部类的方法");
}
}
}
编译后依然会生成Outer$Inner.class文件,和成员内部类的命名没有区别。
局部内部类场景
在方法内部定义局部内部类,查看命名规则:
public class Outer {
public void testMethod() {
// 局部内部类LocalInner
class LocalInner {
public void print() {
System.out.println("这是局部内部类的方法");
}
}
LocalInner localInner = new LocalInner();
localInner.print();
}
}
编译后会生成Outer$1LocalInner.class文件,其中1表示这是Outer类中第一个定义的局部内部类。
匿名内部类场景
定义匿名内部类的场景:
public class Outer {
public void testMethod() {
// 第一个匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("第一个匿名内部类");
}
};
// 第二个匿名内部类
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("第二个匿名内部类");
}
};
}
}
编译后会生成Outer$1.class和Outer$2.class两个文件,分别对应两个匿名内部类,数字顺序和定义顺序一致。
命名规则的实际意义
遵循这样的命名规则,一方面可以让编译产物和源码结构形成明确的对应关系,开发者看到class文件名就能快速定位到对应的内部类定义位置;另一方面也避免了内部类和顶层类的命名冲突,保证类加载器能够正确识别和加载不同层级的类文件。
内部类class文件命名Outer$InnerJava编译修改时间:2026-06-26 23:57:33