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 反射效率低,你知道原因在哪里么

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352