反射使用
概念
反射是计算机程序运行的时候,检查、自省、改变结构和行为的能力
使用
一般都是先获取 Class 类,然后根据要获取的操作信息,获取类的构造函数,方法,属性等,找到并调用相应的方法
用例
测试类父类 ReflectSuperBean
package com.yczuoxin.bean;
public class ReflectSuperBean {
public Boolean flag;
public ReflectSuperBean() {
System.out.println("调用父类无参构造方法");
}
public ReflectSuperBean(Boolean flag) {
this.flag = flag;
System.out.println("调用父类有参构造方法,入参 flag -> " + flag");
}
public void superMethod(Boolean flag) {
System.out.println("调用父类方法,入参 flag -> " + flag);
}
}
测试使用 ReflectBean
package com.yczuoxin.bean;
@BeanName(name = "反射 reflect")
public class ReflectBean extends ReflectSuperBean {
public String beanName;
@FieldName(fieldName = "info field")
private String info;
public ReflectBean() {
System.out.println("使用了无参构造函数");
}
public ReflectBean(String info) {
this.info = info;
System.out.println("使用了单参构造函数,入参 info -> " + info);
}
public ReflectBean(String beanName, String info) {
this.beanName = beanName;
this.info = info;
System.out.println("使用了双参构造函数");
}
private ReflectBean(Integer integer) {
System.out.println("使用了私有的构造参数,入参 integer -> " + integer);
}
public void methodWithoutParam() {
System.out.println("调用没有入参的方法");
}
@MethodName(methodName = "one param")
public void methodWithOneParam(String str) {
System.out.println("调用没有入参的方法,入参 str -> " + str);
}
private void privateMethod() {
System.out.println("调用私有方法");
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BeanName {
String name() default "tset bean";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldName {
String fieldName() default "test field";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodName {
String methodName() default "test method";
}
获取 Class
获取 Class 有三种方法
-
Class<ReflectBean> clazz = ReflectBean.class;
-
Class<?> clazz = Class.forName("com.yczuoxin.bean.ReflectBean");
-
ReflectBean bean = new ReflectBean(); Class<? extends ReflectBean> clazz = bean.getClass();
获取构造函数
获取构造函数的方法有三种
-
Class<ReflectBean> clazz = ReflectBean.class; ReflectBean bean = clazz.newInstance(); // 输出结果 // 调用父类无参构造方法 // 使用了无参构造函数
此方法能调用成功的前提是 Class 中有无参构造,然后根据无参构造函数构建出对象实例
-
Class<ReflectBean> clazz = ReflectBean.class; Constructor<ReflectBean> constructor = clazz.getConstructor(String.class); ReflectBean bean = constructor.newInstance("test class"); // 输出结果 // 调用父类无参构造方法 // 使用了单参构造函数,入参 info -> test class
此方法可以获取此类中修饰符为 public 的构造方法
-
Class<ReflectBean> clazz = ReflectBean.class; // 此处不能使用 getConstructor 获取私有构造器 Constructor<ReflectBean> priConstructor = clazz.getDeclaredConstructor(Integer.class); // 暴力获取权限 priConstructor.setAccessible(true); ReflectBean bean = priConstructor.newInstance(123); // 输出结果 // 调用父类无参构造方法 // 使用了私有的构造参数,入参 integer -> 123
此方法可以获取此类中定义的所有的构造函数
获取属性字段
获取属性字段的方法有两种
-
Class<ReflectBean> clazz = ReflectBean.class; Field flag = clazz.getField("flag"); System.out.println(flag.getName()); // 输出结果 // flag
此方法可以获取此类或者父类定义的修饰符为 public 的属性字段
-
Class<ReflectBean> clazz = ReflectBean.class; Field info = clazz.getDeclaredField("info"); // // 暴力获取权限 info.setAccessible(true); System.out.println(info.getName()); // 输出结果 // info
此方法只能获取此类定义的所有的属性字段
获取方法
获取方法的方法有两种
-
Class<ReflectBean> clazz = ReflectBean.class; ReflectBean bean = clazz.newInstance(); Method superMethod = clazz.getMethod("superMethod", Boolean.class); superMethod.invoke(bean, false); // 输出结果 // 调用父类无参构造方法 // 使用了无参构造函数 // 调用父类方法,入参 flag -> false
此方法可以获取此类或者父类定义的修饰符为 public 的方法
-
Class<ReflectBean> clazz = ReflectBean.class; ReflectBean bean = clazz.newInstance(); Method methodWithOneParam = clazz.getDeclaredMethod("privateMethod"); // 暴力获取权限 methodWithOneParam.setAccessible(true); methodWithOneParam.invoke(bean); // 输出结果 // 调用私有方法
此方法只能获取此类定义的所有的方法
获取注解
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<ReflectBean> clazz = ReflectBean.class;
ReflectBean bean = clazz.newInstance();
BeanName classAnnotation = clazz.getAnnotation(BeanName.class);
System.out.println(classAnnotation.name());
Field info = clazz.getDeclaredField("info");
info.setAccessible(true);
FieldName fieldAnnotation = info.getAnnotation(FieldName.class);
System.out.println(fieldAnnotation.fieldName());
Method method = clazz.getDeclaredMethod("methodWithOneParam", String.class);
MethodName methodAnnotation = method.getAnnotation(MethodName.class);
System.out.println(methodAnnotation.methodName());
}
}
// 输出内容
// 调用父类无参构造方法
// 使用了无参构造函数
// 反射 reflect
// info field
// one param
基本就是 getAnnotation() 方法去获取注解及其中的内容
内部类的特殊场景
Class 类中有三个针对内部类设置的方法
- getEnclosingClass();
- getEnclosingConstructor();
- getEnclosingMethod();
例子
public class Outer {
public Outer() {
// 构造方法中的匿名内部类
InnerClass innerClass = new InnerClass() {
@Override
public void fun() {
getEnclosing(this.getClass());
/**
* enclosingClass = class com.yczuoxin.demo.Outer
* enclosingConstructor = public com.yczuoxin.demo.Outer()
* enclosingMethod = null
*/
}
};
innerClass.fun();
}
// 匿名内部类
static InnerClass innerClass = new InnerClass() {
@Override
public void fun() {
getEnclosing(this.getClass());
/**
* enclosingClass = class com.yczuoxin.demo.Outer
* enclosingConstructor = null
* enclosingMethod = null
*/
};
};
public static void test() {
// 方法中的匿名内部类
InnerClass innerClass = new InnerClass() {
@Override
public void fun() {
getEnclosing(this.getClass());
/**
* enclosingClass = class com.yczuoxin.demo.Outer
* enclosingConstructor = null
* enclosingMethod = public static void com.yczuoxin.demo.Outer.test()
*/
}
};
innerClass.fun();
}
// 内部类
public static class InnerClass {
public InnerClass() {
}
public void fun() {
}
}
public static void main(String[] args) {
System.out.println("------内部类------");
getEnclosing(InnerClass.class);
System.out.println("------匿名内部类------");
innerClass.fun();
System.out.println("------方法中的匿名内部类------");
Outer.test();
System.out.println("------构造函数中的匿名内部类------");
new Outer();
}
/**
* getEnclosingClass:该类是在哪个类中定义的,比如直接定义的内部类或匿名内部类
* getEnclosingConstructor:该类是在哪个构造函数中定义的,比如构造方法中定义的匿名内部类
* getEnclosingMethod:该类是在哪个方法中定义的,比如方法中定义的匿名内部类
*
* @param clazz
*/
private static void getEnclosing(Class<?> clazz) {
Class<?> enclosingClass = clazz.getEnclosingClass();
Constructor<?> enclosingConstructor = clazz.getEnclosingConstructor();
Method enclosingMethod = clazz.getEnclosingMethod();
System.out.println("enclosingClass = " + enclosingClass);
System.out.println("enclosingConstructor = " + enclosingConstructor);
System.out.println("enclosingMethod = " + enclosingMethod);
}
}
总结
优点
- 在任何需要在运行时对类型做操作的场景下都可以考虑反射
- 可以提前让类加载器加载某个类
缺点
- 因为是运行时动态获取,所以可能会产生类型转换失败等问题而无法在编译期提前被编译器发现
- 反射是比较消耗性能的,因为对象实例化时,JVM 是有优化的,但是反射却不会优化
建议在可以不使用反射的情况下,尽量不使用反射