参考资料
- Java反射系列–方法大全
- Java反射(通俗易懂)
- 【小家java】Java反射机制中Class.getXXX()和Class.getDeclaredXXX()的使用区别和注意事项
- 【小家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()
- 获取本类(不包含父类)的所有修饰符的属性,包括
private
和protected
。
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