getClass().getName() 返回带包名的全限定类名,如 java.lang.String;需判空调用,优先用 instanceof 判断类型,字符串匹配应使用 getSimpleName()。

getClass().getName() 返回的是带包名的全限定类名
Java 中 getClass().getName() 不是返回简单类名(比如 String),而是返回完整路径,例如 java.lang.String。这在日志、反射或调试时容易误判类型归属。
常见错误现象:用 if (obj.getClass().getName().equals("ArrayList")) 判断类型,结果永远为 false —— 因为实际返回的是 java.util.ArrayList。
- 判断类型优先用
instanceof或getClass() == Xxx.class - 若真需字符串匹配,用
getSimpleName()获取不带包名的类名 - 注意内部类会返回类似
com.example.Outer$Inner的格式,$ 符号是合法字符,不是错误
getClass() 在 null 上调用会抛出 NullPointerException
getClass() 是实例方法,必须在非 null 对象上调用。直接对可能为 null 的变量调用 obj.getClass().getName() 是高危操作。
使用场景:通用工具方法中打印对象类型、封装 JSON 时记录类型元信息等,都可能遇到未判空就取类名的情况。
立即学习“Java免费学习笔记(深入)”;
- 安全写法是先判空:
obj == null ? "null" : obj.getClass().getName() - 不要依赖
Objects.toString(obj.getClass(), "null")—— 这会报空指针,因为getClass()先执行 - 如果用 Lombok 的
@ToString,默认不包含 class 名;手动加includeFieldNames = true也解决不了 null 问题
getClass().getName() 和 Class.forName() 不是镜像操作
getClass().getName() 输出的结果,不能直接喂给 Class.forName() —— 看似能 round-trip,但数组、泛型擦除、原始类型等场景下会失效。
典型反例:一个 int[] 数组调用 getClass().getName() 返回 [I,这不是合法类名,Class.forName("[I") 虽然能成功(JVM 内部支持),但换成 List<string>[]</string> 就变成 [Ljava.util.List;,可读性差且难维护。
- 需要动态加载类时,优先用已知的类字面量,如
MyService.class - 若必须从字符串还原,确认来源可控(比如配置文件里写死的
com.example.Foo),避开 JVM 自动生成的编码名 - 注意
getName()对基本类型数组和引用类型数组返回格式不同:int[].class.getName()→[I,String[].class.getName()→[Ljava.lang.String;
性能和兼容性:getClass().getName() 没有缓存,但开销极小
每次调用 getClass().getName() 都会访问 Class 对象的 name 字段(JDK 8+ 已是 final 字段),不触发类加载或反射解析,所以无需手动缓存结果。
不过在高频日志场景(如每毫秒打一条含类名的日志),字符串拼接本身比取名更耗资源;而跨 JDK 版本时,getName() 行为稳定,无兼容性风险。
- 没必要为性能提前优化,除非压测证明它是瓶颈(通常不是)
- 避免在
toString()中无条件拼接getClass().getName()—— 可能引发无限递归(如自定义类里 toString 又调用了自身字段的 toString) - Android 上 Dalvik/ART 对
getName()行为一致,无需额外适配
最常被忽略的一点:类名字符串本身是不可变的,但它的“含义”可能随类加载器变化。同一个类名(如 com.example.Service)被不同 ClassLoader 加载后,getClass() 返回的对象不相等,getName() 却完全一样 —— 这时候光看字符串会误判类型一致性。










