java 反射方法(java反射原理)

1.反射相关概念

  • 反射:是指程序可以访问、检测和修改它本身状态或行为的一种能力;
  • 反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能;
  • 反射让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法。以帮助开发者写出正确的代码;
  • 测试时可以利用反射 API 访问类的私有成员,以保证测试代码覆盖率;

2.反射的API

java 反射方法(java反射原理)(1)

  • Field 类:提供有关类的属性信息,以及对它的动态访问权限;它是一个封装反射类的属性的类;
  • constructor 类:提供有关类的构造方法的信息,以及对它的动态访问权限;它是一个封装反射类的构造方法的类;
  • method 类:提供关于类的方法的信息,包括抽象方法;它是用来封装反射类方法的一个类;
  • Class 类:表示正在运行的 java 应用程序中的类的实例;
  • Object 类:Object 是所有 Java 类的父类。所有对象都默认实现了 Object 类的方法;

3.反射的使用

  1. 获取Class对象
  • 第一种方法是通过类的全路径字符串获取 Class 对象;
  • 第二种方法有限制条件:需要导入类的包;
  • 第三种方法已经有了 Student 对象,不再需要反射;

// 1.通过字符串获取Class对象,这个字符串必须带上完整路径名Class studentClass = Class.forName("com.test.reflection.Student");// 2.通过类的class属性Class studentClass2 = Student.class;// 3.通过对象的getClass()函数Student studentObject = new Student();Class studentClass3 = studentObject.getClass();System.out.println("class1 = " studentClass "\n" "class2 = " studentClass2 "\n" "class3 = " studentClass3 "\n" "class1 == class2 ? " (studentClass == studentClass2) "\n" "class2 == class3 ? " (studentClass2 == studentClass3));class1 = class com.test.reflection.Studentclass2 = class com.test.reflection.Studentclass3 = class com.test.reflection.Studentclass1 == class2 ? trueclass2 == class3 ? true

  1. 获取成员变量
  • getDeclaredFields:用于获取所有声明的字段,包括公有字段和私有字段;
  • getFields:仅用来获取公有字段;

// 1.获取所有声明的字段Field[] declaredFieldList = studentClass.getDeclaredFields();for (Field declaredField : declaredFieldList) { System.out.println("declared Field: " declaredField);}// 2.获取所有公有的字段Field[] fieldList = studentClass.getFields();for (Field field : fieldList) { System.out.println("field: " field);}declared Field: private java.lang.String com.test.reflection.Student.studentNamedeclared Field: public int com.test.reflection.Student.studentAgefield: public int com.test.reflection.Student.studentAge

  1. 获取泛型类型
  • 获取成员变量的泛型类型

Field field=MyClass.class.getField("stringList");Type genericsFieldType=field.getGenericType();if(genericsFieldType instanceof ParameterizedType){ ParameterizedType parameterizedType=(ParameterizedType) genericsFieldType; Type[] fieldArgTypes=parameterizedType.getActualTypeArguments(); for (Type fieldArgType:fieldArgTypes){ Class fieldArgClass=(Class) fieldArgType; System.out.println("泛型字段的类型:" fieldArgClass); }}

  • 获取方法参数的泛型类型

Method method=MyClass.class.getMethod("setList",List.class);Type[] genericParameterTypes=method.getGenericParameterTypes();for (Type genericType:genericParameterTypes){ if(genericType instanceof ParameterizedType){ ParameterizedType parameterizedType=(ParameterizedType)genericType; Type[] types= parameterizedType.getActualTypeArguments(); for (Type type:types){ Class realType=(Class) type; System.out.println("方法参数的类型:" realType); } }}

  • 获取方法返回值的泛型类型

Method method=MyClass.class.getMethod("getStringList",null);System.out.println(method.getReturnType());Type retrunType=method.getGenericReturnType();System.out.println(retrunType);if(retrunType instanceof ParameterizedType){ ParameterizedType type=(ParameterizedType)retrunType; Type[] typeArguments=type.getActualTypeArguments(); for(Type typeArgument:typeArguments){ Class typeArgClass=(Class)typeArgument; System.out.println("泛型类型:" typeArgClass); }}

  1. 获取构造方法
  • getDeclaredConstructors:用于获取所有构造方法;
  • getConstructors:用于获取公有构造方法;

// 1.获取所有声明的构造方法Constructor[] declaredConstructorList = studentClass.getDeclaredConstructors();for (Constructor declaredConstructor : declaredConstructorList) { System.out.println("declared Constructor: " declaredConstructor);}// 2.获取所有公有的构造方法Constructor[] constructorList = studentClass.getConstructors();for (Constructor constructor : constructorList) { System.out.println("constructor: " constructor);}declared Constructor: public com.test.reflection.Student()declared Constructor: private com.test.reflection.Student(java.lang.String)constructor: public com.test.reflection.Student()

  1. 获取非构造方法
  • getDeclaredMethods :获取所有声明的非构造方法;
  • getMethods:仅获取公有非构造方法;

// 1.获取所有声明的函数Method[] declaredMethodList = studentClass.getDeclaredMethods();for (Method declaredMethod : declaredMethodList) { System.out.println("declared Method: " declaredMethod);}// 2.获取所有公有的函数Method[] methodList = studentClass.getMethods();for (Method method : methodList) { System.out.println("method: " method);}declared Method: public void com.test.reflection.Student.setStudentAge(int)declared Method: private java.lang.String com.test.reflection.Student.show(java.lang.String)method: public void com.test.reflection.Student.setStudentAge(int)method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionmethod: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionmethod: public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionmethod: public boolean java.lang.Object.equals(java.lang.Object)method: public java.lang.String java.lang.Object.toString()method: public native int java.lang.Object.hashCode()method: public final native java.lang.Class java.lang.Object.getClass()method: public final native void java.lang.Object.notify()method: public final native void java.lang.Object.notifyAll()

  1. 示例

// 1.通过字符串获取Class对象,这个字符串必须带上完整路径名Class studentClass = Class.forName("com.test.reflection.Student");// 2.获取声明的构造方法,传入所需参数的类名,如果有多个参数,用','连接即可Constructor studentConstructor = studentClass.getDeclaredConstructor(String.class);// 如果是私有的构造方法,需要调用下面这一行代码使其可使用,公有的构造方法则不需要下面这一行代码studentConstructor.setAccessible(true);// 使用构造方法的newInstance方法创建对象,传入构造方法所需参数,如果有多个参数,用','连接即可Object student = studentConstructor.newInstance("NameA");// 3.获取声明的字段,传入字段名Field studentAgeField = studentClass.getDeclaredField("studentAge");// 如果是私有的字段,需要调用下面这一行代码使其可使用,公有的字段则不需要下面这一行代码// studentAgeField.setAccessible(true);// 使用字段的set方法设置字段值,传入此对象以及参数值studentAgeField.set(student,10);// 4.获取声明的函数,传入所需参数的类名,如果有多个参数,用','连接即可Method studentShowMethod = studentClass.getDeclaredMethod("show",String.class);// 如果是私有的函数,需要调用下面这一行代码使其可使用,公有的函数则不需要下面这一行代码studentShowMethod.setAccessible(true);// 使用函数的invoke方法调用此函数,传入此对象以及函数所需参数,如果有多个参数,用','连接即可。函数会返回一个Object对象,使用强制类型转换转成实际类型即可Object result = studentShowMethod.invoke(student,"message");

4.反射机制原理

  1. 总体流程
  • 准备阶段:编译期装载所有的类,将每个类的元信息保存至Class类对象中,每一个类对应一个Class对象;
  • 获取Class对象:调用x.class/x.getClass()/Class.forName() 获取x的Class对象clz(这些方法的底层都是native方法)
  • 进行实际反射操作:通过clz对象获取Field/Method/Constructor对象进行进一步操作;
  1. 通过Class获取Field/Method/Constructor
  • Class类中包含的ReflectionData,用于保存反射操作的基本类型;

java 反射方法(java反射原理)(2)

  • 以Field的获取为例进行探究,先看看getDeclaredField;

java 反射方法(java反射原理)(3)

  • 内部调用了privateGetDeclaredFields方法;

java 反射方法(java反射原理)(4)

  • 获取到的Method如何调用Method对象通过MethodAcessor的invoke调用方法
  • ;通过反射工厂生成MethodAcessor对象;
  • 生成NativeMethodAcessorImpl,最终由DelegatingMethodAccessorImpl代理 ;
  • 调用时先进入的是DelegatingMethodAccessorImpl的invoke方法;DelegatingMethodAcessorImpl是代理对象,实质上最终调用的是NativeMethodAcessorImpl的invoke方法;
  • 所有的方法反射都是先走NativeMethodAccessorImpl,默认调了15次之后,才生成一个GeneratedMethodAccessorXXX类,生成好之后就会走这个生成的类的invoke方法了;
,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页