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
}
}