Java中反射的原理和基本方法示例

一、反射定义

反射(Reflection)是Java语言提供的一种基础功能,它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性。通过反射,我们可以在运行时获得程序或程序集中每一个内部属性、方法或构造器的详细信息,并能直接使用这些信息。

简单来说,反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

反射的核心方法:

// 反射的调用方法
Object result = method.invoke(Object obj, Object... args);

我们进入method.invoke的源代码发现,反射需要一个对象和多个Object类的参数,那么如何理解这个运行时呢?

以获取类的方法为例,我们可以通过Class类的getMethod方法获取某个类的方法,这个方法在运行时返回一个Method对象,这个对象包含了方法的所有信息,如方法名、参数类型、返回类型等。我们可以通过这个Method对象来调用这个方法。

Class clazz = Class.forName("java.util.ArrayList");
Method method = clazz.getMethod("add", Object.class);

在上面的代码中,我们首先通过Class.forName方法获取到了ArrayList类的Class对象,然后通过getMethod方法获取到了add方法的Method对象。这个过程都是在运行时完成的,我们在编写代码的时候并不需要知道具体要操作的类和方法,只需要知道类的名字和方法的名字及参数类型就可以了。

这就是反射的强大之处,它使我们的代码具有很高的灵活性,可以在运行时动态地操作类和对象。

二、反射的示例

下面是一个删除集合类中元素的方法:

public class CorrectRemoveElement {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int elementToRemove = 3;
        List<Integer> list = removeElement(arr, elementToRemove);
        for (int num : list) {
            System.out.println(num);
        }
    }
    public static List<Integer> removeElement(int[] arr, int elementToRemove) {

        List<Integer> list = new ArrayList<Integer>();
        for (int i : arr) {
            list.add(i);
        }
        Iterator<Integer> iter = list.iterator();
        while(iter.hasNext()){
            int item = iter.next();
            if(item % 2 == 0){
                iter.remove();
            }
        }
        return  list;
    }

}


正面的调用非常简单,使用new CorrectRemoveElement()构建这个对象实例,使用实例 instance.removeElement()即可

那么如果想用反射的形式,应该怎么修改这段代码呢?我们先给出答案

public static void main(String[] args) {
        try{
            Class clazz = Class.forName("remove.CorrectRemoveElement");
            Class<?>[] parameterTypes = new Class<?>[]{int[].class, int.class};
            Method method =clazz.getMethod("removeElement",parameterTypes);
            Constructor con = clazz.getConstructor();
            Object obj = con.newInstance();
            Object result = method.invoke(obj,new int[]{1,2,3,4,5},4);
            System.out.println(result);
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (NoSuchMethodException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

可以看到获取的顺序,从clazz的类对象,根据类路径获取类名;然后使用clazz.getMethod获取方法名;再通过 clazz.getConstructor();获取构造器,最后通过方法调用实例对象和参数,完成反射接口的调用,这其中要注意捕获反射调用的异常

三、反射的使用场景

动态加载和执行类:反射可以在运行时动态加载类,并执行其方法。例如,可以使用 Class.forName() 方法加载类,然后使用 getMethod() 和 invoke() 方法执行类的方法。

框架设计:许多 Java 框架(如 Spring、Hibernate)都使用了反射技术。例如,Spring 框架可以通过反射技术实现依赖注入,Hibernate 可以通过反射技术实现对象关系映射。

单元测试:在 Junit 等单元测试框架中,反射用于动态调用被测试类的方法。

插件化架构:在插件化架构中,主程序可以通过反射加载并运行插件,实现插件的热插拔。

反射是实现动态代理的基础,JDK动态代理和CGLIB动态代理都是反射的应用,但是它们的实现原理和使用场景有所不同。