在 Java 中,使用反射来创建对象、创建数组、调用方法

本文详细介绍了Java反射如何访问私有字段、修改对象状态、创建对象、调用不同类型的公开和私有方法,包括可变参数方法,并展示了完整源代码实例。通过反射,开发者可以在运行时动态操作对象,深入理解反射机制的应用。

  假设已有下面的类:

  • 示例类:Demo
package org.wangpai.reflectionuses.model;

/**
 * @since 2022-9-11
 */
public class Demo {
    private FieldA fieldA;

    private FieldB fieldB;

    /**
     * 无参构造器
     *
     * @since 2022-9-11
     */
    public Demo() {
        System.out.println("无参构造器 Demo() 被调用");
        System.out.println();
    }

    /**
     * 有参构造器
     *
     * @since 2022-9-11
     */
    public Demo(ParaA paraA, ParaB paraB) {
        System.out.println("构造器 Demo(ParaA paraA, ParaB paraB) 被调用");
        System.out.println("  > paraA:" + paraA);
        System.out.println("  > paraB:" + paraB);
        System.out.println();
        this.fieldA = new FieldA(paraA.toString());
        this.fieldB = new FieldB(paraB.toString());
    }

    /**
     * 公有静态方法
     *
     * @since 2022-9-11
     */
    public static void publicStaticFun(ParaA paraA, ParaB paraB) {
        System.out.println("公有静态方法 publicStaticFun(ParaA paraA, ParaB paraB) 被调用");
        System.out.println("  > paraA:" + paraA);
        System.out.println("  > paraB:" + paraB);
        System.out.println();
    }

    /**
     * 私有有静态方法
     *
     * @since 2022-9-11
     */
    private static void privateStaticFun(ParaA paraA, ParaB paraB) {
        System.out.println("私有有静态方法 privateStaticFun(ParaA paraA, ParaB paraB) 被调用");
        System.out.println("  > paraA:" + paraA);
        System.out.println("  > paraB:" + paraB);
        System.out.println();
    }

    /**
     * 公有非静态方法
     *
     * @since 2022-9-11
     */
    public void publicFun(ParaA paraA, ParaB paraB) {
        System.out.println("公有方法 publicFun(ParaA paraA, ParaB paraB) 被调用");
        System.out.println("  > paraA:" + paraA);
        System.out.println("  > paraB:" + paraB);
        System.out.println();
    }

    /**
     * 私有非静态方法
     *
     * @since 2022-9-11
     */
    private void privateFun(ParaA paraA, ParaB paraB) {
        System.out.println("私有方法 privateFun(ParaA paraA, ParaB paraB) 被调用");
        System.out.println("  > paraA:" + paraA);
        System.out.println("  > paraB:" + paraB);
        System.out.println();
    }

    /**
     * 公有非静态可变参方法(无前面的固定参数)
     *
     * @since 2022-9-11
     */
    public void publicVarParaFunWithNoFixedPara(ParaA... paraAS) {
        System.out.println("公有可变参方法(无前面的固定参数) publicVarParaFunWithNoFixedPara(ParaA... paraAS) 被调用");
        for (int order = 0; order < paraAS.length; ++order) {
            System.out.println(String.format("  > 可变参参数,第 %d 参数是:", order + 1) + paraAS[order]);
        }
        System.out.println();
    }

    /**
     * 公有非静态可变参方法(有前面的固定参数)
     *
     * @since 2022-9-11
     */
    public void publicVarParaFunWithFixedPara(ParaA paraA, ParaB... paraBs) {
        System.out.println("公有可变参方法(有前面的固定参数) publicVarParaFunWithFixedPara(ParaA paraA, ParaB... paraBs) 被调用");
        System.out.println("  > 固定参数 paraA:" + paraA);
        for (int order = 0; order < paraBs.length; ++order) {
            System.out.println(String.format("  > 可变参参数,第 %d 参数是:", order + 1) + paraBs[order]);
        }
        System.out.println();
    }

    /**
     * 私有非静态可变参方法(无前面的固定参数)
     *
     * @since 2022-9-11
     */
    private void privateVarParaFunWithNoFixedPara(ParaA... ParaAs) {
        System.out.println("私有可变参方法(无前面的固定参数) privateVarParaFunWithNoFixedPara(ParaA... ParaAs) 被调用");
        for (int order = 0; order < ParaAs.length; ++order) {
            System.out.println(String.format("  > 可变参参数,第 %d 参数:", order + 1) + ParaAs[order]);
        }
        System.out.println();
    }

    /**
     * 私有非静态可变参方法(有前面的固定参数)
     *
     * @since 2022-9-11
     */
    private void privateVarParaFunWithFixedPara(ParaA paraA, ParaB... paraBs) {
        System.out.println("私有可变参方法 privateVarParaFunWithFixedPara(ParaA paraA, ParaB... paraBs) 被调用");
        System.out.println("  > 固定参数 paraA:" + paraA);
        for (int order = 0; order < paraBs.length; ++order) {
            System.out.println(String.format("  > 可变参参数,第 %d 参数:", order + 1) + paraBs[order]);
        }
        System.out.println();
    }

    /**
     * @since 2023-8-13
     */
    public void showFields() {
        System.out.println("公有方法 showFields() 被调用");
        System.out.println("  > fieldA:" + this.fieldA);
        System.out.println("  > fieldB:" + this.fieldB);
        System.out.println();
    }
}
  • 字段类:FieldA
package org.wangpai.reflectionuses.model;

/**
 * 参数类 FieldA
 *
 * @since 2023-8-13
 */
public class FieldA {
    private String info;

    /**
     * @since 2023-8-13
     */
    public FieldA() {
        super();
    }

    /**
     * @since 2023-8-13
     */
    public FieldA(String str) {
        super();
        this.info = str;
    }

    /**
     * @since 2023-8-13
     */
    @Override
    public String toString() {
        return this.info;
    }
}
  • 字段类:FieldB
package org.wangpai.reflectionuses.model;

/**
 * 参数类 FieldB
 *
 * @since 2023-8-13
 */
public class FieldB {
    private String info;

    /**
     * @since 2023-8-13
     */
    public FieldB() {
        super();
    }

    /**
     * @since 2023-8-13
     */
    public FieldB(String str) {
        super();
        this.info = str;
    }

    /**
     * @since 2023-8-13
     */
    @Override
    public String toString() {
        return this.info;
    }
}
  • 参数类:ParaA
package org.wangpai.reflectionuses.model;

/**
 * 参数类 ParaA
 *
 * @since 2022-9-11
 */
public class ParaA {
    private String info;

    public ParaA() {
        super();
    }

    public ParaA(String str) {
        super();
        this.info = str;
    }

    @Override
    public String toString() {
        return this.info;
    }
}
  • 参数类:ParaB
package org.wangpai.reflectionuses.model;

/**
 * 参数类 ParaB
 *
 * @since 2022-9-11
 */
public class ParaB {
    private String info;

    public ParaB() {
        super();
    }

    public ParaB(String str) {
        super();
        this.info = str;
    }

    @Override
    public String toString() {
        return this.info;
    }
}

反射访问字段

ParaA paraA = new ParaA("这是 ParaA");
ParaB paraB = new ParaB("这是 ParaB");
Demo demo = new Demo(paraA, paraB);
Field fA = demo.getClass().getDeclaredField("fieldA");
fA.setAccessible(true); // 设置允许反射读写私有字段
FieldA fieldA = (FieldA) fA.get(demo);
System.out.println("fieldA:" + fieldA);
构造器 Demo(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA
  > paraB:这是 ParaB

fieldA:这是 ParaA

反射修改字段

ParaA paraA1 = new ParaA("这是 ParaA1");
ParaB paraB1 = new ParaB("这是 ParaB1");
Demo demo = new Demo(paraA1, paraB1);
FieldA fieldA2 = new FieldA("这是 FieldA2");
Field fA = demo.getClass().getDeclaredField("fieldA");
fA.setAccessible(true); // 设置允许反射读写私有字段
fA.set(demo, fieldA2);
demo.showFields();
构造器 Demo(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA1
  > paraB:这是 ParaB1

公有方法 showFields() 被调用
  > fieldA:这是 FieldA2
  > fieldB:这是 ParaB1

反射创建对象

ParaA paraA = new ParaA("这是 ParaA");
ParaB paraB = new ParaB("这是 ParaB");
Demo demo = Demo.class
        .getConstructor(ParaA.class, ParaB.class)
        .newInstance(paraA, paraB);
构造器 Demo(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA
  > paraB:这是 ParaB

反射调用方法

反射调用公有静态方法

ParaA paraA = new ParaA("这是 ParaA");
ParaB paraB = new ParaB("这是 ParaB");
Demo.class.getMethod("publicStaticFun", ParaA.class, ParaB.class)
        .invoke(null, paraA, paraB);
公有静态方法 publicStaticFun(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA
  > paraB:这是 ParaB

反射调用私有静态方法

ParaA paraA = new ParaA("这是 ParaA");
ParaB paraB = new ParaB("这是 ParaB");
var privateFun = Demo.class.getDeclaredMethod("privateStaticFun", ParaA.class, ParaB.class);
privateFun.setAccessible(true); // 如果此处的类本来就有权限调用用反射调用的方法,则此行代码可以省略
privateFun.invoke(null, paraA, paraB);
私有有静态方法 privateStaticFun(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA
  > paraB:这是 ParaB

反射调用公有非静态方法

  调用上述 Test 对象的可变参私有方法的办法是:

Demo demo = new Demo();
ParaA paraA = new ParaA("这是 ParaA");
ParaB paraB = new ParaB("这是 ParaB");
Demo.class.getMethod("publicFun", ParaA.class, ParaB.class)
        .invoke(demo, paraA, paraB);
无参构造器 Demo() 被调用

公有方法 publicFun(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA
  > paraB:这是 ParaB

反射调用私有非静态方法

Demo demo = new Demo();
ParaA paraA = new ParaA("这是 ParaA");
ParaB paraB = new ParaB("这是 ParaB");
var privateFun = Demo.class.getDeclaredMethod("privateFun", ParaA.class, ParaB.class);
privateFun.setAccessible(true); // 如果此处的类本来就有权限调用用反射调用的方法,则此行代码可以省略
privateFun.invoke(demo, paraA, paraB);
无参构造器 Demo() 被调用

私有方法 privateFun(ParaA paraA, ParaB paraB) 被调用
  > paraA:这是 ParaA
  > paraB:这是 ParaB

反射调用公有非静态可变参方法(无前面的固定参数)

Demo demo = new Demo();
ParaA paraAOne = new ParaA("这是第一个 ParaA");
ParaA paraATwo = new ParaA("这是第二个 ParaA");
ParaA[] paraAs = new ParaA[]{paraAOne, paraATwo};
/**
 * 虽然 paraAs 已经是数组,但此处还是要将可变参数组再封装到一个数组中。
 * 不过,如果固定参数存在时,可以不用再外加数组,改为直接将各个元素列出。
 * 此处因为固定参数不存在,所以需再套一层数组
 */
Demo.class.getMethod("publicVarParaFunWithNoFixedPara", ParaA[].class)
        .invoke(demo, new Object[]{paraAs});
无参构造器 Demo() 被调用

公有可变参方法(无前面的固定参数) publicVarParaFunWithNoFixedPara(ParaA... paraAS) 被调用
  > 可变参参数,第 1 参数是:这是第一个 ParaA
  > 可变参参数,第 2 参数是:这是第二个 ParaA

反射调用私有非静态可变参方法(有前面的固定参数)

Demo demo = new Demo();
ParaA paraA = new ParaA("这是 ParaA");
ParaB paraBOne = new ParaB("这是第一个 ParaB");
ParaB paraBTwo = new ParaB("这是第二个 ParaB");
ParaB[] paraBs = new ParaB[]{paraBOne, paraBTwo};
Demo.class.getDeclaredMethod("publicVarParaFunWithFixedPara", ParaA.class, ParaB[].class)
        .invoke(demo, paraA, paraBs); // 因为固定参数有 1 个及以上,所以此处可以不用数组来包装
无参构造器 Demo() 被调用

公有可变参方法(有前面的固定参数) publicVarParaFunWithFixedPara(ParaA paraA, ParaB... paraBs) 被调用
  > 固定参数 paraA:这是 ParaA
  > 可变参参数,第 1 参数是:这是第一个 ParaB
  > 可变参参数,第 2 参数是:这是第二个 ParaB

反射调用私有非静态可变参方法(无前面的固定参数)

Demo demo = new Demo();
ParaA paraAOne = new ParaA("这是第一个 ParaA");
ParaA paraATwo = new ParaA("这是第二个 ParaA");
ParaA[] paraAs = new ParaA[]{paraAOne, paraATwo};
var privateFun = Demo.class.getDeclaredMethod(
        "privateVarParaFunWithNoFixedPara", ParaA[].class);
privateFun.setAccessible(true);
/**
 * 虽然 paraAs 已经是数组,但此处还是要将可变参数组再封装到一个数组中。
 * 不过,如果固定参数存在时,可以不用再外加数组,改为直接将各个元素列出。
 * 此处因为固定参数不存在,所以需再套一层数组
 */
privateFun.invoke(demo, new Object[]{paraAs});
无参构造器 Demo() 被调用

私有可变参方法(无前面的固定参数) privateVarParaFunWithNoFixedPara(ParaA... ParaAs) 被调用
  > 可变参参数,第 1 参数:这是第一个 ParaA
  > 可变参参数,第 2 参数:这是第二个 ParaA

反射调用私有非静态可变参方法(有前面的固定参数)

Demo demo = new Demo();
ParaA paraA = new ParaA("这是 ParaA");
ParaB paraBOne = new ParaB("这是第一个 ParaB");
ParaB paraBTwo = new ParaB("这是第二个 ParaB");
ParaB[] paraBs = new ParaB[]{paraBOne, paraBTwo};
var privateFun = Demo.class.getDeclaredMethod(
        "privateVarParaFunWithFixedPara", ParaA.class, ParaB[].class);
privateFun.setAccessible(true);
// 因为固定参数有 1 个及以上,所以此处可以不用数组来包装
privateFun.invoke(demo, paraA, paraBs);
无参构造器 Demo() 被调用

私有可变参方法 privateVarParaFunWithFixedPara(ParaA paraA, ParaB... paraBs) 被调用
  > 固定参数 paraA:这是 ParaA
  > 可变参参数,第 1 参数:这是第一个 ParaB
  > 可变参参数,第 2 参数:这是第二个 ParaB

反射创建数组

int arrayLength = 10;
// 注意:这样创建之后,得到的是一个空有长度无内容的空数组,因此,后续使用该数组,还需要依次初始化该数组的各个元素
Demo[] targetArray = (Demo[]) Array.newInstance(Demo[].class.getComponentType(), arrayLength);

反射调用的方法自身可以抛出异常的情形

  如果反射调用的方法本身可以抛出异常,则当其抛出异常时,此异常会被反射“吞掉”,封装成 InvocationTargetException 异常重新抛出。可以使用该封装异常的方法 getTargetException 或 getCause 来获取被吞掉的异常。例如,

try {
    /**
     * 此处为某反射调用代码
     */
} catch (InvocationTargetException exception) {
    Throwable realException = exception.getTargetException();
    if (realException instanceof MyXXXException) {
        throw (MyXXXException) realException;
    } else {
        // TODO:可以选择再抛出 InvocationTargetException 异常,也可以抛出自己想抛出的异常。或者做其它事情。
    }
}

反射识别重载方法

反射获取一个类的每个方法的重载个数

/**
 * 获取一个类的每个方法的重载个数
 *
 * @return HashMap<方法名, 该方法的重载个数>
 * @since 2024-10-11
 */
public static Map<String, Integer> getMethodOverloadCount(Class<?> clazz) {
    Map<String, Integer> methodCountMap = new HashMap<>();
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
        String methodName = method.getName();
        methodCountMap.put(methodName, methodCountMap.getOrDefault(methodName, 0) + 1);
    }
    return methodCountMap;
}

反射获取一个类每个方法的方法名和方法签名

/**
 * 获取一个类每个方法的方法名和其所有的方法签名。对于重载方法,这会将每个重载方法都列出来
 *
 * @return HashMap<方法名, 该方法的所有重载方法的方法签名>
 * @since 2024-10-11
 */
public static Map<String, List<String>> getMethodSignatures(Class<?> clazz) {
    Map<String, List<String>> methodSignaturesMap = new HashMap<>();
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
        String methodName = method.getName();
        String methodSignature = generateMethodSignature(methodName, method.getParameterTypes());
        methodSignaturesMap.computeIfAbsent(methodName, k -> new ArrayList<>()).add(methodSignature);
    }
    return methodSignaturesMap;
}

/**
 * 生成一个重载方法的方法签名字符串
 *
 * @since 2024-10-11
 */
private static String generateMethodSignature(String methodName, Class<?>[] parameterTypes) {
    StringBuilder signature = new StringBuilder();
    signature.append(methodName).append("(");
    String params = Arrays.stream(parameterTypes)
            .map(Class::getSimpleName)
            .reduce((a, b) -> a + ", " + b)
            .orElse("");
    signature.append(params).append(")");
    return signature.toString();
}

反射打印一个类每个方法的方法名和方法签名

/**
 * 打印一个类每个方法的方法名和方法签名。对于重载方法,这会将每个重载方法都打印出来
 *
 * @since 2024-10-11
 */
public static void printMethodSignatures(Class<?> clazz) {
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        System.out.println("方法名: " + methodName + ", 方法签名: " + generateMethodSignature(methodName, parameterTypes));
    }
}

/**
 * 生成一个重载方法的方法签名字符串
 *
 * @since 2024-10-11
 */
private static String generateMethodSignature(String methodName, Class<?>[] parameterTypes) {
    StringBuilder signature = new StringBuilder();
    signature.append(methodName).append("(");
    boolean seen = false;
    String acc = null;
    for (Class<?> parameterType : parameterTypes) {
        String simpleName = parameterType.getSimpleName();
        if (!seen) {
            seen = true;
            acc = simpleName;
        } else {
            acc = acc + ", " + simpleName;
        }
    }
    String params = seen ? acc : "";
    signature.append(params).append(")");
    return signature.toString();
}

完整源代码

  已上传至 GitHub 中,可免费下载:https://github.com/wangpaiblog/20210715_reflection-sample

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值