class.forname() 创建实例前须处理 classnotfoundexception,需用全限定名、注意模块可见性及 classloader 一致性;newinstance() 已废弃,应改用 getdeclaredconstructor().newinstance() 并缓存 constructor 提升性能。

Java 里用 Class.forName() 创建实例前必须处理 ClassNotFoundException
反射创建对象失败,八成卡在类找不到这一步。不是代码写错了,而是 Class.forName() 在运行时查不到类——比如类名拼错、没加包路径、或类在另一个 ClassLoader 里。
-
Class.forName("User")一定报错,得写全限定名:Class.forName("com.example.User") - 如果类在模块化环境(Java 9+)或 OSGi 中,还要确认该类对当前模块可见
- Spring Boot 的 devtools 有时会用两个 ClassLoader,导致
Class.forName()找到类,但后续newInstance()报ClassCastException——这时得用当前线程上下文 ClassLoader:Thread.currentThread().getContextClassLoader().loadClass("...")
newInstance() 已废弃,改用 getDeclaredConstructor().newInstance()
Java 9 开始 Class.newInstance() 被标记为废弃,不仅因为不支持带参构造,更关键的是它绕过访问控制检查,容易掩盖权限问题。
- 无参构造:用
User.class.getDeclaredConstructor().newInstance(),比旧方法更明确,也兼容 private 构造器(需先调setAccessible(true)) - 带参构造:必须显式指定参数类型,比如
User.class.getDeclaredConstructor(String.class, int.class).newInstance("Alice", 25);类型不匹配会直接抛NoSuchMethodException - 注意:
getDeclaredConstructor()不会自动查找父类构造器,只查本类声明的,继承来的无参构造器不会被返回
工厂方法里缓存 Constructor 比反复反射快得多
每次创建对象都走一遍 Class.forName() + getDeclaredConstructor(),性能损耗明显,尤其在高频调用场景(如 RPC 反序列化、JSON 映射)。
- 把
Constructor实例缓存起来,比如用ConcurrentHashMap<string constructor>></string>,key 是全限定类名 - 缓存前记得调一次
setAccessible(true),避免每次 newInstance 都触发安全检查 - 注意泛型擦除:
List<string></string>和List<integer></integer>运行时都是List,不能靠泛型参数做缓存 key
Python 的 getattr(__import__(), class_name) 容易漏掉模块导入路径
Python 反射创建实例看似简单,但 __import__() 行为反直觉,尤其多层包结构下极易出错。
-
__import__("models.user")返回的是models模块,不是models.user;要拿到子模块得链式取:getattr(__import__("models.user"), "user")或更稳妥地用importlib.import_module("models.user") - 类名大小写敏感,且必须是模块内顶层定义的类,嵌套类(如
User.Meta)无法直接通过字符串加载 - 如果类在
__init__.py里被重新导出(如from .user import User),那import_module("models")后再getattr(..., "User")才能成功










