486. Java 反射 - 构造器调用模式

0 阅读2分钟

486. Java 反射 - 构造器调用模式

在反射中,构造器的调用主要有两种方式:

  1. Class.newInstance() (已弃用 ❌)
  2. Constructor.newInstance()(推荐 ✅)

1. 为什么 Class.newInstance() 被弃用?

在 Java SE 9 之后,Class.newInstance() 就不推荐再用了,原因有三点:

  • 🚫 只能调用无参构造器 无法调用带参数的构造器。
  • 🚫 异常处理不合理 构造器抛出的任何异常(受检 / 非受检)都会被原样抛出,绕过了编译期检查。
  • 🚫 访问限制过于严格 只能调用 public 无参构造器,无法调用私有构造器。

👉 因此,我们现在应该统一使用 Constructor.newInstance()


2. 正确的调用方式:Constructor.newInstance()

调用模式:

Constructor<T> constructor = clazz.getConstructor(参数类型...);
T instance = constructor.newInstance(参数值...);

好处:

  • ✅ 可以调用任意构造器(带参 / 无参)
  • ✅ 异常会被包装在 InvocationTargetException 里,避免绕过编译器检查
  • ✅ 可以配合 setAccessible(true) 调用私有构造器

3. 示例:创建 String 实例

Class<String> stringClass = String.class;

// 获取无参构造器
String emptyString = stringClass.getConstructor().newInstance();

System.out.println("Empty? " + emptyString.isEmpty());

输出:

Empty? true

📌 注意:

  • 如果类没有无参构造器,会抛出 NoSuchMethodException
  • 如果类是抽象类 / 接口,会抛出 InstantiationException
  • 如果构造器不可见(如 private),会抛出 IllegalAccessException

4. 示例:调用 Record 构造器

Java 14 引入了 record,它们自带一个 canonical constructor(所有字段作为参数的构造器)。

public record Point(int x, int y) {
    public Point() {  // 自定义无参构造器
        this(0, 0);
    }
}

4.1 调用无参构造器

Class<Point> c = Point.class;
Constructor<Point> constructor = c.getConstructor();
Point p = constructor.newInstance();
System.out.println("p = " + p);

输出:

p = Point[x=0, y=0]

4.2 调用 canonical 构造器

Class<Point> c = Point.class;
Constructor<Point> constructor = c.getConstructor(int.class, int.class);
Point p = constructor.newInstance(1, 1);
System.out.println("p = " + p);

输出:

p = Point[x=1, y=1]

📌 说明:

  • 即使代码里没写,编译器也会自动生成 Point(int x, int y) 构造器。
  • 这就是 canonical constructor

5. 总结

  • ✅ 以后统一用 Constructor.newInstance(),不要用 Class.newInstance()
  • newInstance() 总是可能抛出以下异常:
    • InvocationTargetException(包装构造器抛出的异常)
    • InstantiationException(抽象类 / 接口无法实例化)
    • IllegalAccessException(构造器不可见时)
  • ✅ 结合 setAccessible(true) 可以突破访问限制(但模块化下可能失败)