获取内部类Class对象需区分静态与成员内部类:静态类用“Outer.StaticInner”,成员类用“Outer$MemberInner”;实例化时静态类直接new,成员类需传入外部类实例。

要获取内部类的 Class 对象,关键在于区分成员内部类(非静态)和静态内部类——它们的类名表示方式不同,反射时必须使用**虚拟机规范的二进制名称(binary name)**,而非源码中的点分形式。
静态内部类:用点号连接,和普通类一样
静态内部类不依赖外部类实例,其二进制名称就是外层类名加 $ 再加内部类名,但通过 Class.forName() 加载时,要用点号(.)代替 $,因为 JVM 类加载器认的是点分命名约定(如 java.lang.String)。
例如:
public class Outer {
public static class StaticInner {}
}
正确写法:
-
Class.forName("Outer.StaticInner")✅(推荐,语义清晰) -
Outer.StaticInner.class✅(编译期已知,最直接) -
Class.forName("Outer$StaticInner")❌(这是 JVM 内部表示,forName不接受含$的字符串)
成员内部类(非静态):必须带外部类实例,类名含 $
成员内部类隐式持有外部类引用,它的二进制名称是 Outer$Inner。注意:Class.forName() 必须传入这个带 $ 的完整名称,不能用点号。
例如:
public class Outer {
public class MemberInner {}
}
正确写法:
-
Class.forName("Outer$MemberInner")✅(唯一可用的字符串方式) -
Outer.MemberInner.class❌(编译错误!无法在静态上下文中引用非静态内部类) -
new Outer().new MemberInner().getClass()✅(运行时获取,但需要先有外部类实例)
通过外部类 Class 获取内部类 Class(通用且安全)
避免硬编码字符串,更可靠的方式是调用 getDeclaredClasses():
-
Outer.class.getDeclaredClasses()返回所有声明的内部类(含静态与非静态)数组 - 遍历并用
getSimpleName()或getName()过滤目标类 - 对成员内部类,仍需注意:它不能直接
.class引用,但可通过此 API 反射拿到
示例:
Class<?>[] innerClasses = Outer.class.getDeclaredClasses();
for (Class<?> c : innerClasses) {
if ("MemberInner".equals(c.getSimpleName())) {
// 得到 Outer$MemberInner 的 Class 对象
System.out.println(c.getName()); // 输出:Outer$MemberInner
}
}
创建内部类实例:静态 vs 成员的关键区别
拿到 Class 对象后,实例化方式完全不同:
-
静态内部类:直接调用
clazz.getDeclaredConstructor().newInstance() - 成员内部类:必须传入外部类实例作为构造器第一个参数(即使无显式构造器,编译器也会合成一个)
例如:
// 静态内部类
Object staticInst = Class.forName("Outer$StaticInner").getDeclaredConstructor().newInstance();
// 成员内部类
Constructor<?> ctor = Class.forName("Outer$MemberInner").getDeclaredConstructor(Outer.class);
Object memberInst = ctor.newInstance(new Outer());

















