了解Java的反射机制,首先要从RTTI开始。
文章的一开头就开始装逼了,RTTI是个什么东西。RTTI英文全称:Run-Time Type Information,望文生义,RTTI是运行时类型信息。
什么是RTTI,有什么作用,编程时会用到吗?这个问题先放在这里,等会再解答。
学过一点点Java的人,都知道Java文件在编译之后变成了class文件。问题又来了,class文件是什么?肯定有人回答:Class文件就是一个编译后的文件,就像C/C++的.c和.cpp文件编译之后编程了.o,还需要解释吗?要,必须要。
先来一张class文件的文件格式示意图。
没看懂?必然看不懂,先简单介绍一下Class文件。
Class文件从文件类型上是二进制文件,问题又来了,什么是二进制文件?二进制文件就是机器能读懂的语言,自从冯诺依曼的原型机出世,所有的计算机作为它的后代就只认识1和0这两个数字。二进制文件就是一堆0和1。Class文件就是由一堆0和1堆起来的。但是,无规矩不成方圆,这些0和1是按照某种严格的规则放置在对应的位置上。JVM作为Java的运行环境,是规则的制定者和解读者(尽管是Java的开发者们制定的,我们可以简单地认为JVM做了这些事情,至少是开发者们让JVM这么干的)。JVM按照规则来解读0和1序列,然后告诉计算机如何去执行我们的程序所表达的意愿。
但是,Class文件的结构不像XML那些描述型语言那样松散和自由,它的定义是非常严格的,条件也非常的苛刻。Class文件没有任何的分割符号,哪怕是一个空格一个回车(关于回车为什么叫做回车,点击这里)。
在图中Class文件的几个部分,Header,Constant pool...等,这些数据项无论是顺序还是数量都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何都被非常苛刻地限制,不允许改变。然后,你会看到Class attributes在class文件的最后,应该用过Java反编译器吧,有没有注意过Java反编译器反编译的class文件方法在上面,属性都在下面,就是这个原因。
这篇博客讲解的很详细,http://blog.csdn.net/dc_726/article/details/7944154。
文件的开头是Header,,这段十六进制码表明了JDK的版本号,所以你把JDK1.7的class文件放在JDK1.6的环境下就不能运行,向下兼容。
扯得有点远了。回到正题。
Java被编译后,生成了.class文件,JVM此时就要去解读.class文件。当程序主动去使用某个类时,如果这个类还没有被加载到内存中,JVM会通过三个步骤对类进行初始化:
1.加载:由类加载器执行,该步骤查找字节码,并从这些字节码中创建一个Class对象
2.链接:在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
3.初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块。
如以上三步,一个.class文件被融入到程序中供其调用。第一个步骤中,查找字节码并生成一个Class对象,在Java中,如Thinking in Java中提到的,一切皆是对象。被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是java.lang.Class。
这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类(所谓的动态是按照动态语言的定义——在程序运行时改变其结构:心得函数可以被引进,已有的函数可以被删除等在结构上的变化)。 由此,可以看出反射机制使得Java语言多么灵活。
Class类的概念尽管很抽象,但是无疑,它是反射机制的起源,是Java语言中一个精巧美妙地设计。
介绍完.class文件是怎么回事,又简单说明了.class和Class之间的关系,然后要看一下Class类的官方描述。
官方文档是最权威的,先来看一下Class类中的API是如何描述Class类的。下面是翻译后的中文文档的描述:
Class类的实例表示正在运行的Java应用程序的类和接口。枚举是一种类,注释(注解)是一种接口。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。基本的Java类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。Class没有公用构造方法。Class对象是在加载类时由JVM以及通过调用类加载器中的defineClass方法自动构造的。
看完这个描述,并结合上面提到的三个步骤,应该会有更好的理解。
在深入到反射机制之前,先探析一下反射机制的定义和应用。反射机制定义:Java反射机制是在运行状态时,对于任意一个类,都能够直到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
我们不禁会想,得到这些属性和方法并且去改变它们有什么用?觉得反射机制仿佛很遥远,但是作为一个全职程序员,除了假期和双休日,你几乎每天都在和反射机制打交道,无论你使用的Eclipse系列的IDE(集成开发环境),还是使用的目前较为流行的IntelliJ IDEA,又或者是NetBeans,在你输入一个对象的名称,按下”.”时,总会弹出这个对象所拥有的所有方法列表供选择。这个功能就是反射机制实现的,反射机制得到了这个对象的方法和属性。
很显然,是先有的反射机制,才有的自动智能提示,所以上面的例子只能勉强算作反射机制出现和应用的一个动机。
在反射机制设计之初,睿智的设计者们或许就有了分布式的野心。编程人员希望Java能够提供在跨网络的远程平台上创建和运行对象的能力,这种能力的名字大家应该很熟悉,叫做远程方法调用RMI,这便是编程人员想要在运行时获取类的信息的又一个动机。RMI允许一个Java程序将对象分布到多台机器上,即在远程就可以调用某个对象,就像你通过浏览器去使用别人提供的网页服务一样。这就促成了现在的分布式系统的崛起。然而在JDK的1.1版本中,Java反射机制就出现了。
Class类和java.lang.reflect类库一起构成了对Java反射机制的支持。其中最常使用到的类是Constructor,Field,Method,而这三个类都继承了一个接口java.lang.reflect.Member。下面列举介绍一下java.lang.reflect类库中的类:
当要使用反射机制去探查一个类的内部时,还可以调用getFields(),getMethods()和getConstructors()等很便利的方法。对于反射机制,和RTTI的区别就在于,RTTI是在编译时打开和检查.class文件,而反射机制是在运行时打开和检查.class文件。
1.Class类是反射机制的起源,我们得到Class类对象有三种方法:
Class.forName() Object.getClass() 类字面常量xx.class
解释一下这三种方法,
类字面常量使得创建Class对象的引用时不会自动地初始化该对象,而是按照之前提到的加载,链接,初始化三个步骤,这三个步骤是个懒加载的过程,不使用的时候就不加载,这种机制是C/C++无法复制模拟的。
原文:http://www.cnblogs.com/AaronCui/p/4911123.html