[Java] Java 反射机制

发布时间 2024-01-03 13:18:00作者: 千千寰宇

1 概述:反射机制

1.1 原理

  • 反射机制是指程序在运行时可以动态地获取类的信息,并且可以调用类的方法、访问类的属性等。
  • 在Java中,反射机制被广泛应用于框架、工具和其他一些需要动态加载和使用类的场景中。
    • 反射机制是Java语言的一个特性,它是通过反射API实现的。
    • 在Java中,每个类都有一个Class对象,它保存了该类的信息,包括类的名称、成员变量、方法和构造方法等。
    • 反射机制就是通过这个Class对象来获取类的信息,并且可以动态地调用类的方法、访问类的属性等。

1.2 作用

  • 1、反编译:.class–>.java
  • 2、通过反射机制访问/操纵java对象的属性、方法、构造方法等;
1)在运行时判断任意一个对象所属的类。
2)在运行时判断任意一个类所具有的成员变量和方法。
3)在运行时任意调用/操纵一个对象的方法
4)在运行时构造任意一个类的对象

1.3 核心API

反射机制的核心API

  • java.lang.Class
    • 静态方法
      • forName(className)
    • 实例方法
      • newInstance()
  • java.lang.reflect.Constructor : 用于描述获取到的单个成员构造器信息
  • java.lang.reflect.Field : 用于描述获取到的单个成员属性信息
    • getDeclaredFields
    • getFields
    • getDeclaredField
    • getField
    • set(object, fieldValue)
    • get(object)/getBoolean/Long/Short/Char/Byte/Double/Int(object)
    • getName()
    • getType()
    • getGenericType()
    • getModifiers()
    • getAnnotatedType
  • java.lang.reflect.Method : 用于描述获取到的单个成员方法信息
    • Method getMethod(String name,Class<?>... parameterTypes)
      • 获取该Class对象表示类中名字为name、且参数为parameterTypes的指定公共成员方法
    • Method[] getMethods()
      • 用于获取该Class对象表示类中所有公共成员方法
    • Object invoke(Object obj, Object... args)
      • 使用对象obj来调用此Method对象所表示的成员方法,实参传递args
    • int getModifiers()
      • 获取方法的访问修饰符
    • Class<?> getReturnType()
      • 获取方法的返回值类型
    • String getName()
      • 获取方法的名称
    • Class<?>[] getParameterTypes()
      • 获取方法所有参数的类型
    • Class<?>[] getExceptionTypes()
      • 获取方法的异常信息
  • java.lang.reflect.Modifier
  • java.lang.reflect.Parameter

2 案例实践

  • 假定我们有这么一个被试验的类:
package myjava.lang.reflect;  
  
public class Employee {  
    private String name;  
    private Integer salary;  
    
    public static String PREFIX = "EMP";
  
    public Employee() {  
  
    }  
  
    public Employee(String name, Integer salary) {  
        this.name = name;  
        this.salary = salary;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public Integer getSalary() {  
        return salary;  
    }  
  
    public void setSalary(Integer salary) {  
        this.salary = salary;  
    }  
  
    @Override  
    public String toString() {  
        return "Employee{" +  
                "name='" + name + '\'' +  
                ", salary=" + salary +  
                '}';  
    }  
}

2.1 获取【Class/实例对象】

import org.junit.Test;  
  
/**  
 * @description 类和对象的反射测试  
 * @reference-doc  
 *  [1] [Java反射设置类静态属性值 java实现反射的三种方式 - 51cto](https://blog.51cto.com/u_16213610/7297643)  
 */public class ClassAndInstanceReflectTest {  
    /** 获取 class **/  
    @Test  
    public void getClassTest() throws ClassNotFoundException {  
        //方式1 Class.forName  
        Class c1 = Class.forName("myjava.lang.reflect.Employee");  
        
        //方式2 java中每个类型都有 class 属性  
        Class c2 = Employee.class;  
        
        //方式3 java语言中任何一个java对象都有getClass 方法  
        Employee e = new Employee();  
        Class c3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)  
    }  
  
    /** 创建对象 **/  
    @Test  
    public void createObjectTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException {  
        Class c =Class.forName("myjava.lang.reflect.Employee");  
  
        //创建此Class 对象所表示的类的一个新实例  
        Object o = c.newInstance(); //调用了Employee的无参数构造方法.  
  
        System.out.println(o.toString());//Employee{name='null', salary=null}  
    }  
}

2.2 获取/操纵【属性】

package myjava.lang.reflect;  
  
import org.junit.Test;  
  
import java.lang.reflect.Field;  
import java.lang.reflect.Modifier;  
  
/** 属性的反射测试 **/  
public class FieldReflectTest {  
    /**  
     * 获取所有属性  
     * @throws ClassNotFoundException  
     * @reference-doc  
     *  [1] {@lik myjava.lang.reflect.Employee }     */    @Test  
    public void getAllFieldsTest() throws ClassNotFoundException {  
        /** 获取整个类 **/  
        Class c = Class.forName("myjava.lang.reflect.Employee");  
        //获取所有的属性  
        //1) getFields : 只获取 public 的属性字段: PREFIX  
        //Field[] fs = c.getFields();        //2) getDeclaredFields : 获取指定类中所有声明属性的字段数组(包括public、private和protected,但不包括父类的字段): name salary PREFIX  
        Field[] fs = c.getDeclaredFields();  
  
        //定义可变长的字符串,用来存储属性  
        StringBuffer sb = new StringBuffer();  
        //通过追加的方法,将每个属性拼接到此字符串中  
        //最外边的public定义  
        String modifier = Modifier.toString(c.getModifiers());//Modifier.toString(17) = "public final"  
        sb.append(modifier + " class " + c.getSimpleName() +"{\n");  
        //里边的每一个属性  
        for(Field field:fs){  
            sb.append("\t");//空格  
            sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如 "public static final" 等等  
            sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字 例如 "int"            sb.append(field.getName()+";\n");//属性的名字 + 回车 例如 "SIZE"        }  
  
        sb.append("}");  
  
        System.out.println(sb);  
        /**  
         public class Employee{            private String name;            private Integer salary;            public static String PREFIX;         }         */    }  
  
    /** 获取指定的属性 **/  
    @Test  
    public void getTargetFieldTest() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {  
        Employee employee = new Employee("jane", 8970);  
  
        /** 获取整个类 **/  
        Class c = Class.forName("myjava.lang.reflect.Employee");  
        //获取所有的属性  
        Field field1 = c.getField("PREFIX");//PREFIX √(获取成功) , name X(获取失败) , getName X(获取失败)  
        //1) getField("fieldName") : 只获取 public 的属性字段: PREFIX  
        System.out.println("PREFIX : " + field1.get(employee));//"EMP"  
  
        //2) getDeclaredFields : 获取指定类中指定声明属性的字段(支持public、private和protected,但不支持父类的字段): name salary PREFIX  
        Field field2 = c.getDeclaredField("name");  
        field2.setAccessible(true);//如不将private属性设置为true,在获取属性值(get)时将报 IllegalAccessException        field2.get(employee); //从对象中获取目标字段的值  
        System.out.println("name : " + field1.get(employee));//"name"  
        field2.set(employee, "jack");//设置目标字段的值  
        System.out.println(String.format("%s(type:%s) = %s" , field2.getName(), field2.getType().toString(), field2.get(employee)));//"name(type:class java.lang.String) = jack"  
    }  
}

2.3 获取/操纵【方法】

package myjava.lang.reflect;  
  
import org.junit.Test;  
  
import java.lang.reflect.Constructor;  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  
import java.lang.reflect.Parameter;  
  
public class MethodReflectTest {  
    @Test  
    public void aMethodTest() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {  
        Employee employee = new Employee("jane", 83500);  
  
        Class clazz = employee.getClass();  
        // 根据Class对象获取对应的有参构造方法  
        Constructor constructor = clazz.getConstructor(String.class, Integer.class);  
  
        // 使用有参构造方法构造对象,并记录  
        Object employee2 = constructor.newInstance("xiaoping.deng", 30000);  
  
        Method method = clazz.getMethod("getName");  
        //clazz.getMethods() / clazz.getDeclaredMethods()  
        //clazz.getDeclaredMethod(methodName) / clazz.getEnclosingMethod()  
        Object value = method.invoke(employee2);//Object invoke(Object object, Object... args)  
        System.out.println("value : " + value);//xiaoping.deng  
    }  
  
    @Test  
    public void aMethodTest2() throws NoSuchMethodException {  
        Employee employee = new Employee("jane", 83500);  
  
        Class clazz = employee.getClass();  
  
        //Method [] methods = clazz.getMethods();  
        Method method = clazz.getDeclaredMethod("setName", String.class);//查找 方法名为 setName,且入参类型为 String 的方法,查找失败时会报 NoSuchMethodException  
        Class<?> [] parameterTypes = method.getParameterTypes();;  
        Parameter [] parameters = method.getParameters();  
        for(int i=0, size = method.getParameterCount();i<size; i++){  
            Class<?> parameterType = parameterTypes[i];  
            Parameter parameter = parameters[i];  
            //method=setName | parameterType : java.lang.String | parameter # name=name ,type=class java.lang.String, index : 0  
            System.out.println(String.format("method=%s | parameterType : %s | parameter # name=%s ,type=%s, index : %s"  
                , method.getName(), parameterType.getCanonicalName(), parameter.getName(), parameter.getType().toString(), i)  
            );  
        }  
  
        Class<?> returnType = method.getReturnType();  
        System.out.println("returnType : " + returnType.getCanonicalName());//returnType : void  
    }  
}

X 参考文献