使用反射实现对象的工厂模式_基于类名字符串创建实例

1次阅读

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") 才能成功

工厂模式用反射本身不难,难的是类加载时机、访问权限、构造器可见性这些隐性约束。多数问题不是语法写错,而是没意识到反射操作发生在哪个 ClassLoader、哪个模块作用域里。

text=ZqhQzanResources