• Java反射机制Reflection


    Java反射机制

    了解Java的反射机制,首先要从RTTI开始。

    文章的一开头就开始装逼了,RTTI是个什么东西。RTTI英文全称:Run-Time Type Information,望文生义,RTTI是运行时类型信息。

    什么是RTTI,有什么作用,编程时会用到吗?这个问题先放在这里,等会再解答。

     

    1,  .class文件

    学过一点点Java的人,都知道Java文件在编译之后变成了class文件。问题又来了,class文件是什么?肯定有人回答:Class文件就是一个编译后的文件,就像C/C++.c.cpp文件编译之后编程了.o,还需要解释吗?要,必须要。

    先来一张class文件的文件格式示意图。

      

     

    没看懂?必然看不懂,先简单介绍一下Class文件。

    Class文件从文件类型上是二进制文件,问题又来了,什么是二进制文件?二进制文件就是机器能读懂的语言,自从冯诺依曼的原型机出世,所有的计算机作为它的后代就只认识10这两个数字。二进制文件就是一堆01Class文件就是由一堆01堆起来的。但是,无规矩不成方圆,这些01是按照某种严格的规则放置在对应的位置上。JVM作为Java的运行环境,是规则的制定者和解读者(尽管是Java的开发者们制定的,我们可以简单地认为JVM做了这些事情,至少是开发者们让JVM这么干的)。JVM按照规则来解读01序列,然后告诉计算机如何去执行我们的程序所表达的意愿。

    但是,Class文件的结构不像XML那些描述型语言那样松散和自由,它的定义是非常严格的,条件也非常的苛刻。Class文件没有任何的分割符号,哪怕是一个空格一个回车(关于回车为什么叫做回车,点击这里)。

    在图中Class文件的几个部分,HeaderConstant pool...等,这些数据项无论是顺序还是数量都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何都被非常苛刻地限制,不允许改变。然后,你会看到Class attributesclass文件的最后,应该用过Java反编译器吧,有没有注意过Java反编译器反编译的class文件方法在上面,属性都在下面,就是这个原因。

     

    这篇博客讲解的很详细,http://blog.csdn.net/dc_726/article/details/7944154

    文件的开头是Header,这段十六进制码表明了JDK的版本号,所以你把JDK1.7class文件放在JDK1.6的环境下就不能运行,向下兼容。

     

    2, Class

    扯得有点远了。回到正题。

    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语言多么灵活。

     

    3,Class类与反射机制

    Class类的概念尽管很抽象,但是无疑,它是反射机制的起源,是Java语言中一个精巧美妙地设计。

    介绍完.class文件是怎么回事,又简单说明了.classClass之间的关系,然后要看一下Class类的官方描述。

    官方文档是最权威的,先来看一下Class类中的API是如何描述Class类的。下面是翻译后的中文文档的描述:

    Class类的实例表示正在运行的Java应用程序的类和接口。枚举是一种类,注释(注解)是一种接口。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。基本的Java类型(booleanbytecharshortintlongfloat 和 double)和关键字 void 也表示为 Class 对象。Class没有公用构造方法。Class对象是在加载类时由JVM以及通过调用类加载器中的defineClass方法自动构造的。

    看完这个描述,并结合上面提到的三个步骤,应该会有更好的理解。

     4,反射机制的定义和应用

    在深入到反射机制之前,先探析一下反射机制的定义和应用。反射机制定义:Java反射机制是在运行状态时,对于任意一个类,都能够直到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。

    我们不禁会想,得到这些属性和方法并且去改变它们有什么用?觉得反射机制仿佛很遥远,但是作为一个全职程序员,除了假期和双休日,你几乎每天都在和反射机制打交道,无论你使用的Eclipse系列的IDE(集成开发环境),还是使用的目前较为流行的IntelliJ IDEA,又或者是NetBeans,在你输入一个对象的名称,按下”.”时,总会弹出这个对象所拥有的所有方法列表供选择。这个功能就是反射机制实现的,反射机制得到了这个对象的方法和属性。

    很显然,是先有的反射机制,才有的自动智能提示,所以上面的例子只能勉强算作反射机制出现和应用的一个动机。

    在反射机制设计之初,睿智的设计者们或许就有了分布式的野心。编程人员希望Java能够提供在跨网络的远程平台上创建和运行对象的能力,这种能力的名字大家应该很熟悉,叫做远程方法调用RMI,这便是编程人员想要在运行时获取类的信息的又一个动机。RMI允许一个Java程序将对象分布到多台机器上,即在远程就可以调用某个对象,就像你通过浏览器去使用别人提供的网页服务一样。这就促成了现在的分布式系统的崛起。然而在JDK1.1版本中,Java反射机制就出现了。

     

    5,Java反射机制的类库支持及简介

    Class类和java.lang.reflect类库一起构成了对Java反射机制的支持。其中最常使用到的类是ConstructorFieldMethod,而这三个类都继承了一个接口java.lang.reflect.Member。下面列举介绍一下java.lang.reflect类库中的类:

    • AccessibleObjectFieldMethod,和Constructor对象的基类。提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。
    • Array:提供了动态创建和访问Java数组的方法。
    • Constructor:提供关于类的单个构造方法的信息以及对它的访问权限。
    • Field: 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
    • Method: 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
    • Modifier: 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。修饰符集被表示为整数,用不同的位位置 (bit position) 表示不同的修饰符。该类的字段均是int类型的变量。 
    • Proxy: 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
    • ReflectPermission:反射操作的 Permission 类。ReflectPermission 是一种指定权限,没有动作。当前定义的唯一名称是 suppressAccessChecks,它允许取消由反射对象在其使用点上执行的标准 Java 语言访问检查 对于 publicdefault(包)访问、protectedprivate 成员。

     

    当要使用反射机制去探查一个类的内部时,还可以调用getFields()getMethods()getConstructors()等很便利的方法。对于反射机制,和RTTI的区别就在于,RTTI是在编译时打开和检查.class文件,而反射机制是在运行时打开和检查.class文件。

     

    6,反射机制使用Demo

    1.Class类是反射机制的起源,我们得到Class类对象有三种方法:

    Class.forName()
    
    Object.getClass()
    
    类字面常量xx.class
    

      

    解释一下这三种方法,

    • 第一种方法是Class类自带的方法,
    • 第二种方法是所有的对象都能够使用的方法,因为getClass()方法是Object类的方法,所有的类都继承了Object,因此所有类的对象也都具有getClass()方法。
    • 第三种方法是类字面常量。Thinking in Java中建议使用类字面常量来生成对Class对象的引用,这样做即简单又安全,因为在编译时就会受到检查,因此不需要置于try语句块中,并且它根除了对forName()方法的调用,所以也更高效。可以想象一下JDBC的语法,在加载驱动的时候,使用的就是forName()方法,因此即使单独这一句程序,也要使用try语句块。

    类字面常量使得创建Class对象的引用时不会自动地初始化该对象,而是按照之前提到的加载,链接,初始化三个步骤,这三个步骤是个懒加载的过程,不使用的时候就不加载,这种机制是C/C++无法复制模拟的。

     

    其他DEMO

     

     

     

  • 相关阅读:
    跨域
    关于DEDECMS自定义模型当中添加自定义字段后在后台添加内容后不显示解决方案
    js复制文本
    dedecms 织梦显示时间格式
    基本特效:饿了么丝滑无缝过度搜索栏的实现
    2016-wing的年度总结
    这交互炸了(四) :一分钟让你拥有微信拖拽透明返回PhotoView
    这交互炸了(三):闪屏页是像云一样消失的
    这交互炸了(二):爱范儿是如何让详情页缩小为横向列表的
    这交互炸了:饿了么是怎么让Image变成详情页的
  • 原文地址:https://www.cnblogs.com/AaronCui/p/4911123.html
Copyright © 2020-2023  润新知