参考资料

  1. Java反射系列–方法大全
  2. Java反射(通俗易懂)
  3. 【小家java】Java反射机制中Class.getXXX()和Class.getDeclaredXXX()的使用区别和注意事项
  4. 【小家java】java8新特性之—反射获取方法参数名


目录

  • 一. 前期准备
  • 1.1 准备类与接口
  • 1.2 方法总结
  • 二. 获取类的字节码对象
  • 2.1 类.class
  • 2.2 类对象.getClass()
  • 2.3 Class.forName("类的全路径")
  • 2.4 cls.getSuperclass() 获取父类字节码对象
  • 三. 类相关信息的获取
  • 3.1 cls.getName() 获取全路径类名
  • 3.2 cls.getSimpleName() 获取类名
  • 3.3 cls.getPackageName() 获取包名
  • 3.4 cls.getInterfaces() 获取接口的字节码对象
  • 3.5 类注解
  • 3.5.1 类注解判断
  • 3.5.2 类注解获取
  • 四. 创建对象
  • 4.1 cls.getConstructor().newInstance() 无参构造
  • 4.2 cls.getConstructor(类型.class).newInstance(参数) 有参构造
  • 五. 获取Field属性
  • 5.1 cls.getFields()
  • 5.2 cls.getField("属性名")
  • 5.3 cls.getDeclaredFields()
  • 六. Field属性相关信息的获取
  • 6.1 field.getName()
  • 6.2 field.canAccess(类对象)
  • 6.3 field.setAccessible( )
  • 6.4 field.get(类对象) 属性值获取
  • 6.5 field.getType()
  • 6.6 field.getModifiers()
  • 6.7 Filed注解
  • 6.7.1 field.isAnnotationPresent(注解.class)
  • 6.7.2 field.getAnnotations()
  • 6.7.3 field.getAnnotation(注解.class)
  • 七. 获取Method方法
  • 7.1 cls.getMethods()
  • 7.2 cls.getMethod("方法名", 参数.class)
  • 7.3 cls.getDeclaredMethods()
  • 八. Method相关信息的获取
  • 8.1 method.invoke("类对象", "参数") 执行方法
  • 8.2 Method注解
  • 8.2.1 method.isAnnotationPresent(注解.class)
  • 8.2.2 method.getAnnotation(注解.class)
  • 8.3 method.getReturnType()
  • 8.4 method.getParameterCount()
  • 8.5 method.getParameterTypes()
  • 8.6 执行private方法
  • 8.7 获取Method参数的名称



一. 前期准备

1.1 准备类与接口

⏹接口

public interface Test25Interface {

    String address = "地球";

    String queryInfo();
}

⏹定义一个类,内含

  • public属性和方法
  • private属性和方法
import java.math.BigDecimal;
import java.util.List;

public class Test25Form {

    private BigDecimal id = BigDecimal.ONE;

    public List<String> hobbyList;

    protected BigDecimal getId() {
        return this.id;
    }

    private String printTest25FormInfo(String param) {
        System.out.println("printTest25FormInfo方法执行了,传入的参数为:" + param);
        return "传入的参数为:" + param;
    }

    public void setId(BigDecimal id) {
        this.id = id;
    }

    public List<String> getHobbyList() {
        return hobbyList;
    }

    public void setHobbyList(List<String> hobbyList) {
        this.hobbyList = hobbyList;
    }
}

⏹继承父类,并且实现接口

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

public class Test26Form extends Test25Form implements Test25Interface {

    @NotNull(message = "不能为空")
    private String name = "贾飞天";

    public Integer age = 18;

    protected BigDecimal money = new BigDecimal(18);

    public Test26Form(@NotNull(message = "不能为空") String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    private Integer getNum(Integer num) {
        return 10 + num;
    }

    public Test26Form() {
        System.out.println();
    }

    @Override
    public String queryInfo() {
        System.out.println("queryInfo方法执行了");
        return "默认的返回消息!";
    }

    public void printInfo(String msg) {
        System.out.println("打印消息: " + msg + "了!");
    }

    @NotNull
    public BigDecimal printInfo(String msg1, String msg2) {
        System.out.println("打印消息: " + msg1 + "了!");
        System.out.println("打印消息: " + msg2 + "了!");
        return BigDecimal.ZERO;
    }

    @Override
    public String toString() {
        return "Test26Form{" +
                "name='" + name + '\'' +
                ", age=" + age +
                "} " + super.toString();
    }
}

1.2 方法总结

  • Class,Field,Method都可以获取标记在身的注解
  • get系列:可获取本类的public + 父类或接口的public(含静态方法)
  • getDeclared系列:可获取本类所有的访问权限的元素(含静态方法)
  • private的Method和Filed不能直接使用,必须要设置允许访问
  • 通过名称获取Filed或Method的时候,该名称所对应的Filed或Method必须存在,否则会报错

二. 获取类的字节码对象

2.1 类.class

Class<Test26Form> cls1 = Test26Form.class;
System.out.println(cls1);  // class com.example.jmw.form.Test26Form

2.2 类对象.getClass()

Test26Form form = new Test26Form();
Class<? extends Test26Form> cls2 = form.getClass();
System.out.println(cls2);  // class com.example.jmw.form.Test26Form

2.3 Class.forName(“类的全路径”)

Class cls3 = Class.forName("com.example.jmw.form.Test26Form");
System.out.println(cls3);  // class com.example.jmw.form.Test26Form

2.4 cls.getSuperclass() 获取父类字节码对象

Class<? super Test26Form> superclass = cls1.getSuperclass();
System.out.println(superclass);  // class com.example.jmw.form.Test25Form

三. 类相关信息的获取

3.1 cls.getName() 获取全路径类名

String fullName = cls1.getName();
System.out.println(fullName);  // com.example.jmw.form.Test26Form

3.2 cls.getSimpleName() 获取类名

String simpleName = cls1.getSimpleName();
System.out.println(simpleName);  // Test26Form

3.3 cls.getPackageName() 获取包名

String packageName = cls1.getPackageName();
System.out.println(packageName);  // com.example.jmw.form

3.4 cls.getInterfaces() 获取接口的字节码对象

  • 一个类可以实现多个接口,所以得到的是一个数组
  • 得到接口的字节码之后,可以获取接口中的属性和方法
Class<?>[] interfaces = cls1.getInterfaces();
System.out.println(Arrays.toString(interfaces));  
// [interface com.example.jmw.form.Test25Interface]

// 获取第一个接口的字节码对象
Class<?> anInterface = interfaces[0];
// 获取接口上的名叫address的属性
Field fieldInterface = anInterface.getField("address");
// 获取该属性所对应的属性值
Object address = fieldInterface.get(anInterface);
System.out.println(address);  // 地球

3.5 类注解

3.5.1 类注解判断

  • 判断该注解是否在类上存在
cls.isAnnotationPresent(注解.class);

3.5.2 类注解获取

  • 获取类上标记的所有的注解
cls.getAnnotations()
  • 获取类上标记的指定注解
cls.getAnnotation(注解.class)

四. 创建对象

4.1 cls.getConstructor().newInstance() 无参构造

Test26Form form1 = cls1.getConstructor().newInstance();
System.out.println(form1);

4.2 cls.getConstructor(类型.class).newInstance(参数) 有参构造

Test26Form form2 = cls1.getConstructor(String.class, Integer.class)
					   .newInstance("枫叶红", 10);
System.out.println(form2);

五. 获取Field属性

5.1 cls.getFields()

  • 获取当前类和父类上所有标记为public的属性名
Field[] fields = cls1.getFields();
Arrays.stream(fields).map(Field::getName).forEach(System.out::println);

5.2 cls.getField(“属性名”)

  • 获取当前类和父类上为public的age属性,如果该属性不存在会报错
Field ageField = cls1.getField("age");
System.out.println(ageField);
  • 获取本类或父类上为public的id属性,由于该属性不存在会报错,因此使用filter函数过滤
Optional<Field> idOptional = Arrays.stream(fields)
									.filter(filed -> "id".equals(filed.getName()))
									.findAny();
System.out.println(idOptional);

5.3 cls.getDeclaredFields()

  • 获取本类(不包含父类)的所有修饰符的属性,包括privateprotected
Field[] declaredFields = cls1.getDeclaredFields();
Arrays.stream(declaredFields).map(Field::getName)
							 .forEach(System.out::println);  // name age money

六. Field属性相关信息的获取

  • 获取名称为name的私有Field属性对象
Optional<Field> namePrivateFieldOptional = Arrays.stream(declaredFields)
					.filter(filed -> "name".equals(filed.getName()))
					.findAny();

// 获取名称为name的Field属性
if (namePrivateFieldOptional.isPresent()) {
	Field namePrivateField = namePrivateFieldOptional.get();
}

6.1 field.getName()

  • 获取当前属性名
String filedName = namePrivateField.getName();
System.out.println(filedName);

6.2 field.canAccess(类对象)

  • 判断该属性是否可以访问
boolean accessAble1 = namePrivateField.canAccess(form1);
System.out.println(accessAble1);  // false

6.3 field.setAccessible( )

  • 置压制访问类型检查,这样才能访问到private修饰符的属性值
namePrivateField.setAccessible(true);

// 因为访问类型检查已经被压制,因此该属性可以访问
boolean accessAble2 = namePrivateField.canAccess(form1);
System.out.println(accessAble2);  // true

6.4 field.get(类对象) 属性值获取

  • 属性值获取
// 设置压制访问类型检查,这样才能访问到private修饰符的属性值
namePrivateField.setAccessible(true);

// 获取属性所对应的属性值,注意:参数为类对象,不能为类的字节码文件
Object filedValue = namePrivateField.get(form1);
System.out.println(filedValue);  // 枫叶红

6.5 field.getType()

  • 获取属性所对应的类型
Class<?> fieldType = namePrivateField.getType();
System.out.println(fieldType.getName());  // java.lang.String

6.6 field.getModifiers()

  • 获取该属性的修饰符,常与Modifier对象一同使用
import java.lang.reflect.Modifier;

// 获取该属性的修饰符
int modifiers = namePrivateField.getModifiers();

// 判断该属性是否为public
boolean isPublicResult = Modifier.isPublic(modifiers);
System.out.println(isPublicResult);  // false

// 判断该属性是否为Static
boolean isStaticResult = Modifier.isStatic(modifiers);
System.out.println(isStaticResult);  // false

// 判断该属性是否为Private
boolean isPrivateResult = Modifier.isPrivate(modifiers);
System.out.println(isPrivateResult);  // true

6.7 Filed注解

6.7.1 field.isAnnotationPresent(注解.class)

  • 判断该注解是否存在于Field上

6.7.2 field.getAnnotations()

  • 获取属性上标记的所有注解
Annotation[] annotations = namePrivateField.getAnnotations();
System.out.println(Arrays.toString(annotations));

6.7.3 field.getAnnotation(注解.class)

  • 获取属性上标记的指定类型的注解
import javax.validation.constraints.NotNull;

NotNull notNullAnnotation = namePrivateField.getAnnotation(NotNull.class);
// 获取注解上message值
System.out.println(notNullAnnotation.message());  // 不能为空

七. 获取Method方法

7.1 cls.getMethods()

  • 获取当前类和其父类上所有修饰符为public的Method
Method[] methods = cls1.getMethods();

7.2 cls.getMethod(“方法名”, 参数.class)

  • 通过方法名和参数获取当前类和其父类上指定的Method对象
// 通过指定方法名称和参数类型的方法来获取Method对象(注意: 如果方法名称不存在或参数类型不正确的话,会报错,不会返回null)
Method printInfoTwoParamMethod = cls1.getMethod("printInfo", String.class, String.class);

// 对象中的printInfo方法被重载,因此此处调用的是只有一个参数的printInfo方法
Method printInfoOneParam = cls1.getMethod("printInfo", String.class);

7.3 cls.getDeclaredMethods()

  • 获取本类所有的访问权限的方法(private方法也会被获取)
Method[] declaredMethods = cls1.getDeclaredMethods();

八. Method相关信息的获取

8.1 method.invoke(“类对象”, “参数”) 执行方法

⏹执行无参方法

// 从方法List中过滤出方法名为queryInfo的方法
Optional<Method> methodQueryInfo = Arrays.stream(methods)
				.filter(method -> "queryInfo".equals(method.getName()))
				.findAny();
if (methodQueryInfo.isPresent()) {
    // 获取类中的方法
    Method method = methodQueryInfo.get();
    // 👉👉👉无传参,执行该方法,并且获取到返回值
    Object invokeResult = method.invoke(form1);
    System.out.println(invokeResult);
}

⏹执行一个参数的方法

// 对象中的printInfo方法被重载,因此此处调用的是只有一个参数的printInfo方法
Method method = cls1.getMethod("printInfo", String.class);
method.invoke(form1, "贾飞天");

⏹执行多个参数的方法

// 通过指定方法名称和参数类型的方法来获取Method对象(注意: 如果方法名称不存在或参数类型不正确的话,会报错,不会返回null)
Method method = cls1.getMethod("printInfo", String.class, String.class);
method.invoke(form1, "msg1", "msg2");

8.2 Method注解

8.2.1 method.isAnnotationPresent(注解.class)

  • 判断该注解是否存在于Method上

8.2.2 method.getAnnotation(注解.class)

  • 获取方法上标记的注解
import javax.validation.constraints.NotNull;
import java.lang.reflect.Method;

// 获取方法上标记的NotNull注解,从而获取注解上标记的值
NotNull methodAnnotation = printInfoTwoParamMethod.getAnnotation(NotNull.class);
System.out.println(methodAnnotation.message());

8.3 method.getReturnType()

  • 获取该方法的返回值类型
Class<?> returnType = printInfoTwoParamMethod.getReturnType();
System.out.println(returnType.getName());  // java.math.BigDecimal

8.4 method.getParameterCount()

  • 获取方法中参数的数量
int parameterCount = printInfoTwoParamMethod.getParameterCount();
System.out.println(parameterCount);  // 2

8.5 method.getParameterTypes()

  • 获取方法中参数的类型
Class<?>[] parameterTypes = printInfoTwoParamMethod.getParameterTypes();
Arrays.stream(parameterTypes).map(Class::getName).forEach(System.out::println);
// java.lang.String 
// java.lang.String

8.6 执行private方法

  • method.canAccess():判断该方法是否可访问
  • method.setAccessible(): 控制该方法是否可访问
// 获取本类所有的访问权限的方法(private方法也会被获取)
Method[] declaredMethods = cls1.getDeclaredMethods();
// 将private方法getNum从方法中过滤出来
Optional<Method> declaredMethod = Arrays.stream(declaredMethods)
				.filter(method -> "getNum".equals(method.getName()))
				.findAny();
if (declaredMethod.isPresent()) {
    // 获取到该类上的private方法
    Method method = declaredMethod.get();
    // 检查该方法是否可以访问
    if (!method.canAccess(form1)){
        // 忽略检查,使private方法可以访问
        method.setAccessible(true);
    }

    // 执行私有方法
    Object invokeResult = method.invoke(form1, 10);
    System.out.println("通过反射执行之后得到的值为:" + invokeResult);  // 20
}

8.7 获取Method参数的名称

  • 需要借助Spring的LocalVariableTableParameterNameDiscoverer对象
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

// 获取名称为printInfo,并且参数类型为String的Method
Method printInfoOneParam = cls.getMethod("printInfo", String.class);

// 创建LocalVariableTableParameterNameDiscoverer对象
LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

// 解析Method对象,得到参数名称的数组
String[] parameterNames = nameDiscoverer.getParameterNames(printInfoOneParam);
Arrays.stream(parameterNames).forEach(System.out::println);  // msg