class获取类名用getname()(jvm内部名)、getsimplename()(简洁名)、getcanonicalname()(规范名,匿名类为null);父类用getsuperclass()(object或接口返回null);接口用getinterfaces();判断类型用isinterface()/isprimitive()/isarray();泛型信息需通过field/method等获取,class直接调用会因类型擦除返回null或object;类加载用class.forname()时需注意类加载器一致性,避免noclassdeffounderror。

怎么用 Class 获取类名、父类、接口这些基本信息
直接调用 getName()、getSuperclass()、getInterfaces() 就行,但要注意返回值可能为 null(比如 Object 没有父类)或空数组(没实现接口)。getSimpleName() 和 getCanonicalName() 容易混淆:前者只返回类名(如 "ArrayList"),后者带包名且对内部类/泛型更友好(如 "java.util.ArrayList"),但匿名类的 getCanonicalName() 返回 null。
常见错误是把 getName() 当作“用户友好的类名”用——它返回的是 JVM 内部表示名,数组会变成 "[Ljava.lang.String;",基本类型是 "int",不是 "java.lang.Integer"。
-
getName()用于序列化、类加载器查找,别用来显示 - 要打印给用户看,优先用
getSimpleName()(顶层类)或getTypeName()(Java 8+,对泛型更准确) -
getSuperclass()对Object返回null,记得判空;接口的getSuperclass()也返回null(接口没有父类)
isInterface()、isPrimitive()、isArray() 这些判断方法怎么选
它们不互斥:int[].class.isArray() 是 true,int[].class.isPrimitive() 是 false;int.class.isPrimitive() 是 true,int.class.isArray() 是 false。关键看你要区分什么场景:
- 做类型分发(比如 JSON 序列化):先用
isArray()分出数组,再用getComponentType()拿元素类型 - 检查是否为基本类型(如生成字节码):必须用
isPrimitive(),别用== int.class做硬比较——包装类(Integer.class)不满足,且容易漏掉boolean/char等 -
isInterface()对Comparable.class返回true,但对Runnable.class也返回true——它只认interface关键字声明的,不认函数式接口语义
获取泛型信息为什么经常拿到 Object 或 null
因为泛型擦除,运行时 List<string>.class</string> 就是 List.class,原始类型信息丢了。真正能拿到泛型参数的地方只有三处:字段、方法参数、方法返回值的声明位置——而且得通过 Field、Method、Constructor 这些对象去拿,不能从 Class 直接拿。
立即学习“Java免费学习笔记(深入)”;
比如 class A { List<string> list; }</string>,你得先用 A.class.getDeclaredField("list"),再调 getGenericType(),才能得到 ParameterizedType;如果直接用 List.class.getTypeParameters(),返回的是 [](声明时没写类型变量)。
- 字段/方法上用了泛型?用
getDeclaredField()+getGenericType() - 想确认某个类是否声明了类型变量(如
class Box<t> {}</t>)?查getTypeParameters(),不是getGenericSuperclass() -
getGenericSuperclass()返回的是带泛型的父类类型(如ArrayList<string></string>继承自AbstractList<string></string>),但仅当父类本身是参数化类型时才非null
用 forName() 加载类时 ClassLoader 不一致导致的 NoClassDefFoundError
Class.forName(String) 默认用当前线程上下文类加载器(Thread.currentThread().getContextClassLoader()),而 MyClass.class 字面量用的是定义该类的类加载器。两者不一致时,即使类存在,也可能因双亲委派失败而抛 NoClassDefFoundError(注意不是 ClassNotFoundException)。
典型场景:Web 应用里,业务代码在 WAR 包,工具类在 shared/lib,用 Class.forName("com.example.MyUtil") 可能找不到——因为上下文类加载器是 WebAppClassLoader,但 MyUtil 在 SharedClassLoader 加载路径下。
- 明确类加载器来源?用
Class.forName(name, false, loader)显式传入 - 不确定该用谁?优先用目标类所在类的
getClassLoader(),比如SomeKnownClass.class.getClassLoader() -
forName(name, false, loader)的第二个参数是initialize:设为false可跳过静态块执行,适合只查元数据不触发副作用的场景
泛型擦除和类加载器隔离是两个最常被忽略的点,尤其在框架开发或热部署场景下,错一个就卡死在 Class 对象生成阶段。











