zoukankan      html  css  js  c++  java
  • JVM的反射实现

    java的反射机制

    java的反射机制是在运行状态中,对于任意一个类(Class)都能知道他的属性(Field)和方法(Method),对于任意一个对象都能够调用它的方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。它允许正在运行的java程序观测甚至是修改程序的动态行为。

    在java中实现反射最重要的一步也就是第一步是获得Class对象,得到该对象后可通过该对象调用相应的方法获取该类中的属性方法,以及调用该类中的方法。

    Java中反射有如下几种实现方式:

    1、通过Class.forName()方法加载字符串,就可以得到该字符串做代表的Class对象。

    如:Class<?> clazz = Class.forName("java.lang.String")就可以得到String类的Class对象。值得注意的是,字符串必须是类的全名,即包名+类名。

    下边的代码是Struts配置文件struts.xml中的一个action的配置。

    <action name="registe" class="cn.com.test.struts2.RegisteAction">
       <result>/registeResult.jsp</result>
       <result name="input">/registe2.jsp</result>
    </action>

    服务器就是通过class属性给出的类的全命名class="cn.com.test.struts2.RegisteAction"的字符串来得到对象的,就是反射机制RegisteAction的。然后再去调用这个类中execute()方法。

    2、通过类名调用class属性得到该类的Class对象。

    Class<?> clazz = String.class也可以得到String类的Class对象。

    3、调用实例的getClass()方法。

    如:Date date = new Date();
           Class<?> clazz = date.getClass();
    4、如果是基本类型的包装类,则可以通过调用包装类的Type属性来获得该包装类的Class对象。
    如:Class<?> clazz = Integer.TYPE;

    我们可以通过Class对象枚举该类中的所有方法,还可以通过Method.setAccessible(位于java.lang.reflect包,该方法继承自AccessibleObject)绕过java语言的访问权限,在私有方法所在类之外的地方调用该方法。

    反射在java中的应用

    1、java集成开发环境,每当我们敲入点号时,IDE便会根据点号前的内容,动态展示可以访问的字段和方法。

    2、java调试器,它能够在调试过程中枚举某一对象所有字段的值。

    3、web开发中,我们经常接触到各种配置的通用框架。为保证框架的可扩展性,他往往借助java的反射机制。例如Spring框架的依赖反转(IOC)便是依赖于反射机制。

    反射调用的实现Mehod.invoke

     1 public Object invoke(Object obj, Object... args)
     2         throws IllegalAccessException, IllegalArgumentException,
     3            InvocationTargetException
     4     {
     5         if (!override) {
     6             if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
     7                 Class<?> caller = Reflection.getCallerClass();
     8                 checkAccess(caller, clazz, obj, modifiers);
     9             }
    10         }
    11         MethodAccessor ma = methodAccessor;             // read volatile
    12         if (ma == null) {
    13             ma = acquireMethodAccessor();
    14         }
    15         return ma.invoke(obj, args);
    16     }

    你会发现,它实际上是委派给 MethodAccessor 来处理。MethodAccessor  是一个接口,它有两个实现:一个通过本地方法来实现反射调用,另一个使用了委派模式(这里我们称委派实现)。

    每个 Method 实例的第一次反射调用都会生成一个委派实现,他所委派的具体实现便是一个本地实现。当进入JVM内部后,我们便拥有了Mehod 实例所指向方法的具体地址。这时反射调用无非就是将传入的参数准备好,然后调用进入目标方法。

    反射调用先是调用了Method.invoke, 然后进入委派实现(DelegatingMethodAccessorImpl),再进入本地实现(NativeMethodAccessorImpl),最后到达目标方法。

    调用了Method.invoke之后,先进行访问权限检查,再获取MethodAccessor对象,并调用MethodAccessor.invoke方法。MethodAccessor被同名Method所共享,由ReflectionFactory创建。创建机制采用一种名为inflation的方式:如果该方法的调用<=15,会创建本地实现,他的实现就是直接调用native方法实现反射,如果该方法的累计调用次数>15,会创建由字节码组装而成的MethodAccessorImpl。(是否采用inflation机制和15这个数字可以在jvm参数中调整)。

    性能

    通过JNI(java本地接口)调用native方法初始化更快,但是对优化有阻碍作用。随着调用次数的增多,使用动态实现可以直接以java调用的方式来实现反射,发挥了java即时编译的优化作用。

    java反射调用效率慢的原因:

    1、接口的通用性,java的invoke方法是传object和object[]数组的。基本参数类型需要装箱和拆箱,产生大量额外的对象和内存开销,频繁促发GC。

    2、编译器难以对动态调用的代码提前做优化比如方法内联。

    3、反射需要按名检索类和方法,有一定的时间开销。

  • 相关阅读:
    Study Plan The TwentySecond Day
    Study Plan The Nineteenth Day
    Study Plan The TwentySeventh Day
    Study Plan The Twentieth Day
    Study Plan The TwentyFirst Day
    python实现进程的三种方式及其区别
    yum makecache
    JSONPath 表达式的使用
    oracle执行cmd的实现方法
    php daodb插入、更新与删除数据
  • 原文地址:https://www.cnblogs.com/cathyqq/p/9545450.html
Copyright © 2011-2022 走看看