Java基础之反射

一、两个概念

  • 编译型语言:程序在执行之前需要将源代码编译成机器语言,再由机器运行机器码(二进制)。像C/C++、Delphi等都是属于编译型语言,编译型语言需要依赖编译器,通过编译器将源代码编译成与运行平台对应的机器码,运行时不需要重新解释运行,所以程序执行效率高,但因为编译器编译的机器码与运行平台相关,所以跨平台性差。
  • 解释性语言:相对编译型语言,不需要预先编译,以文本方式存储程序代码,在运行程序时,必须先由解释器解释再运行,每执行一次就要翻译一次,效率较低,像JavaScript,VBScript,Python,Ruby等都是解释型语言。

二、JAVA属于哪种语言?

  • 对于Java语言,Java程序首先通过编译器编译成.class文件,也就是字节码,并非是可以直接由机器直接运行的本地机器码,如果在windows平台上运行,则字节码通过windows平台上的JVM(JAVA虚拟机)进行解释执行。如果运行在linux平台上,字节码则通过linux平台上的java虚拟机进行解释执行。所以JAVA语言的跨平台特性是通过JVM的跨平台特性实现的,如果没有JVM,则不能进行跨平台。所以JAVA语言兼具解释性语言和编译型语言的特性,可以说是一种“半编译半解释”执行的语言。综合以上特性,编译型语言和解释型语言的分类就不太准确了。
  • 可以把JAVA语言划分到编译型语言中,因为编译的本质就是把一种相对高级的语言转换为另一个相对低级的语言,而由.java文件->.class文件的编译已经满足了这个特征。

三、为什么要使用反射?

  1. 我们知道,JAVA程序需要经过编译器编译成.class文件,再由JVM解释运行,这些Class对象承载了这个类的所有信息,包括父类、接口、构造函数、方法属性等,这些.class文件在运行之前会被ClassLoader加载到虚拟机中,当一个类被虚拟机加载之后,JVM就会在内存中自动产生一个Class对象。我们通过new的形式创建对象实际上就是通过这些Class“模板”来创建实例对象,而这个过程对我们来说是透明的,具体实现细节我们不得而知。
  2. 从以上分析可知,我们编写的JAVA类需要经过编译之后再由JVM解释执行,JVM解释执行的是我们编写的JAVA类生成的对应的.class文件,因为我们无法在程序运行的过程中修改.class文件,所以,我们编写的JAVA类,到了程序真正运行的时候是无法修改的,除非我们修改JAVA类,然后在重新编译运行。那么我们怎么在程序运行的修改类的相关信息呢?这时JVM相当于给我们提供了一个接口,让我们通过反射来修改已经编译好的JAVA类。是一种以开发效率换运行效率的手段。
  3. 场景:我们需要在程序运行的过程中动态生成一个类的实例,但是我们在程序运行之前无法获知这个JAVA类的任何信息,那也就是说我们在程序中无法定义这个类,也就无法通过new方法来生成相应的实例,也无法再编译的过程生成相应的.class文件,那我们还能去动态的加载这个类并为这个类生成相应的实例吗?
  4. 反射的工作原理就是借助和反射相关的四个核心类:java.lang.Class:Class类;java.lang.reflect.Constructor:构造器类;java.lang.reflect.Method:方法类;java.lang.reflect.Field:属性类。java.lang.Class是一个Java类,继承自Object类。Class类是一个java中的泛型类型。Class类是一般类和接口的进一步抽象,而Class类的每个实例则代表运行中的一个类。Class类的构造函数是私有的,无法通过new关键字来创建Class类的实例。只能通过JVM来调用它来构造一个Class实例。在程序运行时动态访问和修改任何类的行为和状态。

四、反射能做什么?

  1. 通过反射我们可以得到一个类的所有信息,如访问一个类中的所有属性和方法,包括访问权限受限制的属性和方法(声明为private或protected的属性和方法),通过反射我们可以访问一个对象的任意一个属性和方法。换句话说,JVM可以加载一个运行时才得知名称的.class文件,然后获悉其完整结构,并生成其对象实体、或对其fields(变量)设置,或调用其methods(方法)。
  2. 反射有如下作用:①操作因访问权限限制的属性和方法;②实现自定义注解,如依赖注入(DI),注解处理,动态代理,单元测试等功能。比如Retrofit,Spring,Dagger。;③动态加载第三方jar包;④按需加载类。⑤实现序列化与反序列化,比如PO的ORM,Json解析等。⑥实现跨平台兼容,比如JDK中的SocketImpl的实现。

五、反射在native中的实现

  1. Class.forName的实现
    Class.forName可以通过包名寻找Class对象,比如Class.forName("java.lang.String")。
    在JDK的源码实现中,可以发现最终调用的是native方法forName0(),它在JVM中调用的实际是findClassFromClassLoader(),原理与ClassLoader的流程一样。
  2. getDeclaredFields的实现
    在JDK源码中,可以知道class.getDeclaredFields()方法实际调用的是native方法getDeclaredFields0(),它在JVM主要实现步骤如下:
    ① 根据Class结构体信息,获取field_count与fields[]字段,这个字段早已在load过程中被放入了
    ② 根据field_count的大小分配内存、创建数组
    ③ 将数组进行forEach循环,通过fields[]中的信息依次创建Object对象
    ④ 返回数组指针
    主要慢在如下方面:1.创建、计算、分配数组对象;2.对字段进行循环赋值。
  3. Method.invoke的实现
    以下为无同步、无异常的情况下调用的步骤:
    ①创建Frame
    ②如果对象flag为native,交给native_handler进行处理
    ③在frame中执行java代码
    ④弹出Frame
    ⑤返回执行结果的指针
    主要慢在如下方面:1.需要完全执行ByteCode而缺少JIT等优化;2.检查参数非常多,这些本来可以在编译器或者加载时完成。
  4. class.newInstance的实现
    ①检测权限、预分配空间大小等参数
    ②创建Object对象,并分配空间
    ③通过Method.invoke调用构造函数(<init>())
    ④返回Object指针
    主要慢在如下方面:1.参数检查不能优化或者遗漏;2.<init>()的查表;3.Method.invoke本身耗时。

六、反射为什么影响性能?

  1. 前面已经说到,JAVA程序在运行之前需要经过编译,然后通过ClassLoader加载到JVM中,而类加载分为:加载->验证->准备->解析->初始化五个阶段,这些都是在运行期之前完成的,反射慢就慢在把装载期做的事情搬到了运行期,也就是说在使用反射时,需要在运行程序之前把类的加载过程执行一遍。(解释正确与否不得而知)另一种说法是:编译器没法对反射相关的代码做优化。在Stackoverflow上认为反射比较慢的程序员主要有如下看法:1.验证等防御代码过于繁琐,这一步本来在link阶段,现在却在计算时进行验证;2.产生很多临时对象,造成GC与计算时间消耗;3.由于缺少上下文,丢失了很多运行时的优化,比如JIT(它可以看作JVM的重要评测标准之一).
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • 写在最前 很高兴前一段的文章能给大家带来帮助,相信大家都清楚基础知识对于程序员来说是解决问题的根基,笔者的系列文章...
    LightningDC阅读 1,296评论 0 11
  • 整体Retrofit内容如下: 1、Retrofit解析1之前哨站——理解RESTful 2、Retrofit解析...
    隔壁老李头阅读 4,554评论 2 12
  • 看到了很多作者在简书中点评得到这款app,其中心思想大抵是,我从得到中没有得到我想得到的,得到看上去并不是那么有用...
    Hui胡说八道阅读 1,432评论 4 51
  • 我记得你拥抱我。 春天的时候花开得艳,夏天的时候天是养眼的亮蓝,秋天总轻盈地穿过洁白的云层,冬天的尘埃也在太阳光下...
    还是叫猴兜吧阅读 279评论 0 0