Java反射

反射

反射简介

java的反射就是通过Class来在运行时获取类的完整结构信息 & 调用对象的方法。正常情况下,Java类在编译前,就已经被加载到JVM中;而反射机制使得程序运行时还可以动态地去操作类的变量、方法等信息。

反射的本质是当一个类被加载以后,Java虚拟机就会自动产生一个 Class对象。通过这个 Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。

优点是灵活在运行时才动态创建&获取,缺点则是效率低,容易破坏类结构(因为绕过了源码容易干扰类原有内部逻辑)。

效率低主要是由以下几个方面造成:

  • 反射调用过程中会产生大量的临时对象,这些对象会占用内存,可能会导致频繁 gc,从而影响性能。
  • 反射调用方法时会从方法数组中遍历查找,并且会检查可见性等操作会耗时。
  • 反射调用时编译器难以对动态调用的代码提前做优化。
  • 反射一般会涉及自动装箱/拆箱和类型转换,都会带来一定的资源开销

反射使用

大致分为以下几个方面:

  • 获取Constructor类对象
 <-- 1. 获取类的构造函数(传入构造函数的参数类型)->>
   // a. 获取指定的构造函数 (公共 / 继承)
   Constructor<T> getConstructor(Class<?>... parameterTypes)
   // b. 获取所有的构造函数(公共 / 继承) 
   Constructor<?>[] getConstructors(); 
   // c. 获取指定的构造函数 ( 不包括继承)
   Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
   // d. 获取所有的构造函数( 不包括继承)
   Constructor<?>[] getDeclaredConstructors(); 
 // 最终都是获得一个Constructor类对象
 
 // 特别注意:
   // 1. 不带 "Declared"的方法支持取出包括继承、公有(Public) & 不包括有(Private)的构造函数
   // 2. 带 "Declared"的方法是支持取出包括公共(Public)、保护(Protected)、默认(包)访问和私有(Private)的构造方法,但不包括继承的构造函数
   // 下面同理
  • 获取Method类对象
<--  2. 获取类的属性(传入属性名) -->
  // a. 获取指定的属性(公共 / 继承)
   Field getField(String name) ;
  // b. 获取所有的属性(公共 / 继承)
   Field[] getFields() ;
  // c. 获取指定的所有属性 (不包括继承)
   Field getDeclaredField(String name) ;
  // d. 获取所有的所有属性 (不包括继承)
   Field[] getDeclaredFields() ;
// 最终都是获得一个Field类对象
  • 获取Field类对象
<-- 3. 获取类的方法(传入方法名 & 参数类型)-->
  // a. 获取指定的方法(公共 / 继承)
    Method getMethod(String name, Class<?>... parameterTypes) ;
  // b. 获取所有的方法(公共 / 继承)
   Method[] getMethods() ;
  // c. 获取指定的方法 ( 不包括继承)
   Method getDeclaredMethod(String name, Class<?>... parameterTypes) ;
  // d. 获取所有的方法( 不包括继承)
   Method[] getDeclaredMethods() ;
// 最终都是获得一个Method类对象
  • 其他常用方法
getSuperclass(); 
// 返回父类

String getName(); 
// 作用:返回完整的类名(含包名,如java.lang.String ) 
 
Object newInstance(); 

总结来说分为带 "Declared"和不带 "Declared",不带Declared的方法只能获取公有属性以及从父类继承的,带Declared的则只能访问自己定义的属性(但是可以获取私有属性)。还有可以根据是否提供参数分类,带参数的方法一般是根据参数匹配符合的属性,不带参数的获取方法会返回所有符合的属性。

当获取到Constructor、Method、Field之后我们可以根据获取的对象做进一步的获取信息或操作

<-- 1. 通过Constructor 类对象获取类构造函数信息 -->
  String getName();// 获取构造器名
  Class getDeclaringClass();// 获取一个用于描述类中定义的构造器的Class对象
  int getModifiers();// 返回整型数值,用不同的位开关描述访问修饰符的使用状况
  Class[] getExceptionTypes();// 获取描述方法抛出的异常类型的Class对象数组
  Class[] getParameterTypes();// 获取一个用于描述参数类型的Class对象数组

<-- 2. 通过Field类对象获取类属性信息 -->
  String getName();// 返回属性的名称
  Class getDeclaringClass(); // 获取属性类型的Class类型对象
  Class getType();// 获取属性类型的Class类型对象
  int getModifiers(); // 返回整型数值,用不同的位开关描述访问修饰符的使用状况
  Object get(Object obj) ;// 返回指定对象上 此属性的值
  void set(Object obj, Object value) // 设置 指定对象上此属性的值为value
 
<-- 3. 通过Method 类对象获取类方法信息 -->
  String getName();// 获取方法名
  Class getDeclaringClass();// 获取方法的Class对象 
  int getModifiers();// 返回整型数值,用不同的位开关描述访问修饰符的使用状况
  Class[] getExceptionTypes();// 获取用于描述方法抛出的异常类型的Class对象数组
  Class[] getParameterTypes();// 获取一个用于描述参数类型的Class对象数组

<--额外:java.lang.reflect.Modifier类 -->
// 作用:获取访问修饰符

static String toString(int modifiers)   
// 获取对应modifiers位设置的修饰符的字符串表示

static boolean isXXX(int modifiers) 
// 检测方法名中对应的修饰符在modifiers中的值

数组与枚举的反射

//创建元素类型、元素长度指定的数组
public static Object newInstance(Class<?> componentType, int length)
//创建多维度的数组,dimensions可连续传递多个,分别代表不同维度
public static Object newInstance(Class<?> componentType, int... dimensions)
//获取指定数组的对应索引的值
public static native Object get(Object array, int index)
//赋值给指定数组的对应索引下的值
public static native void set(Object array, int index, Object value)
//获取数组长度
public static native int getLength(Object array)


//获取当前枚举类型Class的所有定义的枚举常量
public T[] getEnumConstants()

String[] strArr = new String[10];
int[][] twoDimArr = new int[3][2];
int[] oneDimArr = new int[10];
Class<? extends String[]> strArrCls = strArr.getClass();
Class<? extends int[][]> twoDimArrCls = twoDimArr.getClass();
Class<? extends int[]> oneDimArrCls = oneDimArr.getClass();

获取基本数据类型的Class

//基本类型的class即为对应的包装类型
Class<Integer> intCls = int.class;
Class<Byte> byteCls = byte.class;
Class<Character> charCls = char.class;
Class<Double> doubleCls = double.class;
//void也是一种特殊的类型,返回的也是对应的包装类Void
Class<Void> voidCls = void.class;

反射应用

反射是Android源码里面应用最多的地方,小到xml的解析,大到dex文件的加载,application的启动,均大量运用了反射。

  • 反射生成对象、执行方法、获取类信息
  • 工厂模式优化
  • 动态代理

Java 反射真的很慢吗?

详解Java反射操作

反射技术在Android中的应用

Java反射:这是一份全面 & 详细的 Java反射机制 学习指南

一篇文章带你学懂Java反射

大家都说 Java 反射效率低,你知道原因在哪里么

posted @ 2020-09-23 21:15  Robin132929  阅读(24)  评论(0编辑  收藏