zoukankan      html  css  js  c++  java
  • java反射使用和源码解析

    1         反射

    1.1            什么是反射

    正射:指的是我们知道类的定义和类中的方法名称,直接先创建对象,然后通过对象去调用方法。例如:

    Apple apple = new Apple(); //直接初始化,「正射」

    apple.setPrice(4);

    反射:指的是一开始不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象,需要用JDK 提供的反射 API 进行反射调用。需要通过类的路径字符串创建对象,通过方法名称字符串调用方法。例如:

    Class clz = Class.forName("com.chenshuyi.reflect.Apple");//通过字符串获取类Class

    Constructor constructor = clz.getConstructor();//获取类的构造器

    Object object = constructor.newInstance();//创建对象

    Method method = clz.getMethod("setPrice", int.class);//通过方法名获取方法

    method.invoke(object, 4);//调用方法;

    第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple)。反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

    1.2            反射获取类、构造器、方法、属性

    (1)获取类

    使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

    Class clz = Class.forName("java.lang.String");

    (2)获取构造器

    通过 Constructor 对象的 newInstance() 方法

    Constructor constructor = clz.getConstructor();//获取类的构造器

    Object object = constructor.newInstance();//创建对象

    (1)   获取方法

    获取方法的 Method 对象

    Method setPriceMethod = clz.getMethod("setPrice", int.class);

    利用 invoke 方法调用方法

    setPriceMethod.invoke(appleObj, 14);

    (2)   获取属性

    过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

    Class clz = Apple.class;

    Field[] fields = clz.getFields();

    for (Field field : fields) {

        System.out.println(field.getName());

    }

    输出结果是:

    Price

    (5)获取包含私有属性

    而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

    Class clz = Apple.class;

    Field[] fields = clz.getDeclaredFields();

    for (Field field : fields) {

        System.out.println(field.getName());

    }

    输出结果是:

    name

    price

    1.3           反射的源码解析

    (1)Method的invoke方法

    public Object invoke(Object obj, Object... args)

            throws IllegalAccessException, IllegalArgumentException,

               InvocationTargetException

        {

            if (!override) {

                if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {

                    Class<?> caller = Reflection.getCallerClass();

                    checkAccess(caller, clazz, obj, modifiers);

                }

            }

            MethodAccessor ma = methodAccessor;             // read volatile

            if (ma == null) {

                ma = acquireMethodAccessor();

            }

            return ma.invoke(obj, args);

    }

    (2)acquireMethodAccessor 返回MethodAccessor对象

    private MethodAccessor acquireMethodAccessor() {

            // First check to see if one has been created yet, and take it

            // if so

            MethodAccessor tmp = null;

            if (root != null) tmp = root.getMethodAccessor();

            if (tmp != null) {

                methodAccessor = tmp;

            } else {

                // Otherwise fabricate one and propagate it up to the root

                tmp = reflectionFactory.newMethodAccessor(this);

                setMethodAccessor(tmp);

            }

            return tmp;

        }

    (3)反射工厂返回对象reflectionFactory.newMethodAccessor

    public MethodAccessor newMethodAccessor(Method var1) {
        checkInitted();
        if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;// 最终返回的是DelegatingMethodAccessorImpl对象
        }
    }

    其中DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
    是将NativeMethodAccessorImpl var2对象作为入参,传入DelegatingMethodAccessorImpl的构造函数。那么构造函数内部做了什么?很关键!

    DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {

            this.setDelegate(var1);对象设置在Delegate属性中,后面会用到

    }

    delegate 属性的定义是private MethodAccessorImpl delegate;

    (4)步骤(3)最终返回的是DelegatingMethodAccessorImpl对象,所以步骤(1)中return ma.invoke(obj, args);函数invoke是DelegatingMethodAccessorImpl的方法。看看DelegatingMethodAccessorImpl的invoke方法定义如下:

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {

            return this.delegate.invoke(var1, var2);//调用了属性delegateinvoke方法;也就是NativeMethodAccessorImpl对象的invoke方法;    }

    (5)NativeMethodAccessorImpl对象的invoke实现

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {//累计大于阈值,切换

            if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {

                MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());

                this.parent.setDelegate(var3);

            }

            return invoke0(this.method, var1, var2);

        }

    每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器numInvocations,看超过阈值 没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的 MethodAccessor的实现类,并且通过this.parent.setDelegate(var3);改变DelegatingMethodAccessorImpl所引用的MethodAccessor为 Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。

    注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的,不能在深入了:

    JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0

    (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)

    {

        return JVM_InvokeMethod(env, m, obj, args);

    }

    为什么要这样先用native后又切换为java的呢?

    就像注释里说的,实际的 MethodAccessor 实现有两个版本,一个是 Native 版本NativeMethodAccessorImpl,一个是 Java 版本MethodAccessorImpl。Java版本在第一次启动时,需要加载字节码实现Method.invoke() 和 Constructor.newInstance() ,比较耗时。所以Native 版本第一次启动比java版本快3-4倍,但是后面的调用java版本比Native快20倍。所以为了避免启动慢,第一次使用native版本快速启动。为了避免后续运行慢,在切换到java版本MethodAccessorImpl 对象去实现反射。

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    百度云盘下载地址:

    链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg

    提取码:mc8l

    微信公众号获取最新的软件和视频介绍

    QStockView

  • 相关阅读:
    监控 Linux 性能的 18 个命令行工具
    VS2015中无法查找或打开 PDB 文件
    C1853 编译器错误:fatal error C1853: 'pjtname.pch' precompiled header file is from a previous
    malloc用法
    C语言中i++和++i的区别
    vs未定义的标识符“round”ceil()和floor()
    error C2065: “uint8_t”: 未声明的标识符
    strtol 函数用法
    C++ “string”: 未声明的标识符
    C++ 中c_str()函数
  • 原文地址:https://www.cnblogs.com/bclshuai/p/10617640.html
Copyright © 2011-2022 走看看