精华内容
下载资源
问答
  • Java反射技术详解

    万次阅读 多人点赞 2019-04-14 23:11:32
    相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有...

    前言

      相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有个开源框架jOOR相信很多人都用过,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架,当然这里所讲的反射技术,是学习Android插件化技术、Hook技术等必不可少的!

    一、基本反射技术

          1.1 根据一个字符串得到一个类

            getClass方法

     String name = "Huanglinqing";
     Class c1 = name.getClass();
     System.out.println(c1.getName());
    

         打印结果如下:

        

        Class.forName

        比如我们获取java.lang.String的类名 

       String name = "java.lang.String";
       Class c1 = null;
       try {
              c1 = Class.forName(name);
              System.out.println(c1.getName());
          } catch (ClassNotFoundException e) {
      }
    

        这里也通过捕获异常,因为我们传的这个字符串可能不合法,字符串合法命名是类的命名空间和类的名称组成

        打印结果如下:

        
       我们还可以通过c1.getSuperclass()获取到他的父类

       Type属性

        基本类型都有type属性,可以得到这个基本类型的类型,比如:

    Class c1 = Boolean.TYPE;
    Class c2 = Byte.TYPE;
    Class c3 = Float.TYPE;
    Class c4 = Double.TYPE;
    
     停停停!这些东西有啥子用,如此鸡肋! 别急,一切都是为后续做准备。

    二、获取类的成员

             当类中方法定义为私有的时候我们能调用?不能!当变量是私有的时候我们能获取吗?不能!但是反射可以,比如源码中有你需要用到的方法,但是那个方法是私有的,这个时候你就可以通过反射去执行这个私有方法,并且获取私有变量。

           获取类的构造函数

           为了便于测试,我们定义一个Test类,Test类如下:(省略get和set方法)

           Test类中我们定义是三个私有变量,生成两个公有的含参构造方法和一个私有的含参构造方法以及一个公有的无参构造方法。

    public class Test {
    
        private int age;
        private String name;
        private int testint;
    
        public Test(int age) {
            this.age = age;
        }
    
        public Test(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        private Test(String name) {
            this.name = name;
        }
    
        public Test() {
        }
    

          下面我们通过反射获取这些构造方法

           获取类的所有构造方法

     Test test = new Test();
     Class c4 = test.getClass();
     Constructor[] constructors ;
     constructors = c4.getDeclaredConstructors();
    

          通过getDeclaredConstructors可以返回类的所有构造方法,返回的是一个数组因为构造方法可能不止一个,通过getModifiers可以得到构造方法的类型,getParameterTypes可以得到构造方法的所有参数,返回的是一个Class数组,所以我们如果想获取所有构造方法以及每个构造方法的参数类型,可以有如下代码:

      for (int i = 0; i < constructors.length; i++) {
            System.out.print(Modifier.toString(constructors[i].getModifiers()) + "参数:");
            Class[] parametertypes = constructors[i].getParameterTypes();
            for (int j = 0; j < parametertypes.length; j++) {
                 System.out.print(parametertypes[j].getName() + " ");
           }
          System.out.println("");
      }
    

        运行结果如下所示:

        

        这样我们就得到了类中所有构造方法和构造方法中的参数,那么我们如何获取特定的构造方法呢?

        获取类中特定的构造方法

        我们可以通过getConstructors方法获取类中 所有的public类型的构造方法,代码和上面一样就不演示了。

        我们可以通过getDeclaredConstructor()方法传参获取特定参数类型的构造方法,这里注意是getDeclaredConstructor()不是  getDeclaredConstructors() ,所以返回的是一个Class对象而不是一个Class数组。

        获取无参构造方法直接不传参数,如下所示:

       try {
              constructors = c4.getDeclaredConstructor();
              System.out.print(Modifier.toString(constructors.getModifiers()) + );
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
          }
    

        这里要进行异常捕获,因为可能不存在对应的构造方法,打印结果如下:  

        

       如果我们想获取有两个参数分别为int和String类型的构造方法,代码如下:

      Class[] p = {int.class,String.class};
      try {
           constructors = c4.getDeclaredConstructor(p);
           System.out.print(Modifier.toString(constructors.getModifiers()) + "参数:");
           Class[] parametertypes = constructors.getParameterTypes();
           for (int j = 0; j < parametertypes.length; j++) {
                System.out.print(parametertypes[j].getName() + " ");
              }
           } catch (NoSuchMethodException e) {
                e.printStackTrace();
         }
    

      这里我们同样打印出构造方法的参数:


      

      调用构造方法

       从这里开始慢慢到了关键的一步,得到类的实例,我们主要借助于newInstance方法,为了方便演示我们将测试类的两个构造方法打印出来. 

       public Test(int age, String name) {
            this.age = age;
            this.name = name;
            System.out.println("hello" + name + "i am" + age);
        }
    
    
        private Test(String name) {
            this.name = name;
            System.out.println("My Name is" +
                    name);
        }
    

       我们先来调用public的方法,如下所示:

     Class[] p = {int.class,String.class};
     constructors = c4.getDeclaredConstructor(p);
     constructors.newInstance(24,"HuangLinqing");
    

     运行打印结果如下:


      

     那么调用私有构造方法呢,和上面一样,只是我们要设置constructors.setAccessible(true);代码如下:

      Class[] p = {String.class};
      constructors = c4.getDeclaredConstructor(p);
      constructors.setAccessible(true);
      constructors.newInstance("HuangLinqing");
    

      打印结果如下:


       

    调用类的私有方法

      如何调用类中的私有方法呢,我们先在测试类中编写一个测试的私有方法 如下:

      private void welcome(String tips){
            System.out.println(tips);
        }
    

      我们知道如果我们要正常的调用类的方法都是通过类.方法调用,所以我们调用私有方法也需要得到类的实例,而我们上面newInstace已经得到了类的实例,这样就好办了。

       Class[] p4 = {String.class};
       Method method = c4.getDeclaredMethod("welcome",p4);
       method.setAccessible(true);
    

       我们首先通过 getDeclaredMethod方法获取到这个私有方法,第一个参数是方法名,第二个参数是参数类型

       然后通过invoke方法执行,invoke需要两个参数一个是类的实例,一个是方法参数。

         Class[] p4 = {String.class};
         Method method = c4.getDeclaredMethod("welcome",p4);
         method.setAccessible(true);
         Object arg1s[] = {"欢迎关注代码男人技术公众号"};
         method.invoke(test,arg1s);
    

         test类的实例当不能new 获取的时候我们也可以通过反射获取,就是上面的newInstance方法。打印结果如下:


        

     获取类的私有字段并修改值

      看到这里你可能会说,有了set方法,什么私有不私有,test.set不就可以了,但是这里要注意我们是没有办法得到这个类的实例的,要不然都可以得到实例就没有反射一说了。我们在通过反射得到类的实例之后先获取字段:

    Field field = c4.getDeclaredField("name");
    field.setAccessible(true);
    field.set(o,"代码男人");
    

       o是我们上面通过反射构造方法获取的实例,  打印field.get(o).toString()的值如下:
      

       

       不过要注意的是我们修改了name的值只对当前的实例对象有效。

       Java的基本反射语法就是这样了,欢迎加入技术群一起探讨!

      最后反射封装类如下:

    package jnidemo.hlq.com.hookdemo;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * @author Huanglinqing
     * @date 2019/4/28
     */
    
    public class Reflex {
    
        /**
         * 获取无参构造函数
         * @param className
         * @return
         */
        public static Object createObject(String className) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            try {
                Class r = Class.forName(className);
                return createObject(r, pareTyples, pareVaules);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 获取无参构造方法
         * @param clazz
         * @return
         */
        public static Object createObject(Class clazz) {
            Class[] pareTyple = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return createObject(clazz, pareTyple, pareVaules);
        }
    
        /**
         * 获取一个参数的构造函数  已知className
         *
         * @param className
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object createObject(String className, Class pareTyple, Object pareVaule) {
    
            Class[] pareTyples = new Class[]{pareTyple};
            Object[] pareVaules = new Object[]{pareVaule};
    
            try {
                Class r = Class.forName(className);
                return createObject(r, pareTyples, pareVaules);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        /**
         * 获取单个参数的构造方法 已知类
         *
         * @param clazz
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) {
            Class[] pareTyples = new Class[]{pareTyple};
            Object[] pareVaules = new Object[]{pareVaule};
    
            return createObject(clazz, pareTyples, pareVaules);
        }
    
        /**
         * 获取多个参数的构造方法 已知className
         * @param className
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) {
            try {
                Class r = Class.forName(className);
                return createObject(r, pareTyples, pareVaules);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        /**
         * 获取构造方法
         *
         * @param clazz
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) {
            try {
                Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
                ctor.setAccessible(true);
                return ctor.newInstance(pareVaules);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        /**
         * 获取多个参数的方法
         * @param obj
         * @param methodName
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) {
            if (obj == null) {
                return null;
            }
    
            try {
                //调用一个private方法 //在指定类中获取指定的方法
                Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
                method.setAccessible(true);
                return method.invoke(obj, pareVaules);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 获取一个参数的方法
         * @param obj
         * @param methodName
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) {
            Class[] pareTyples = {pareTyple};
            Object[] pareVaules = {pareVaule};
    
            return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
        }
    
        /**
         * 获取无参方法
         * @param obj
         * @param methodName
         * @return
         */
        public static Object invokeInstanceMethod(Object obj, String methodName) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
        }
    
    
        /**
         * 无参静态方法
         * @param className
         * @param method_name
         * @return
         */
        public static Object invokeStaticMethod(String className, String method_name) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
        }
    
        /**
         * 获取一个参数的静态方法
         * @param className
         * @param method_name
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) {
            Class[] pareTyples = new Class[]{pareTyple};
            Object[] pareVaules = new Object[]{pareVaule};
    
            return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
        }
    
        /**
         * 获取多个参数的静态方法
         * @param className
         * @param method_name
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) {
            try {
                Class obj_class = Class.forName(className);
                return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 无参静态方法
         * @param method_name
         * @return
         */
        public static Object invokeStaticMethod(Class clazz, String method_name) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);
        }
    
        /**
         * 一个参数静态方法
         * @param clazz
         * @param method_name
         * @param classType
         * @param pareVaule
         * @return
         */
        public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) {
            Class[] classTypes = new Class[]{classType};
            Object[] pareVaules = new Object[]{pareVaule};
    
            return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);
        }
    
        /**
         * 多个参数的静态方法
         * @param clazz
         * @param method_name
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules) {
            try {
                Method method = clazz.getDeclaredMethod(method_name, pareTyples);
                method.setAccessible(true);
                return method.invoke(null, pareVaules);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        public static Object getFieldObject(String className, Object obj, String filedName) {
            try {
                Class obj_class = Class.forName(className);
                return getFieldObject(obj_class, obj, filedName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static Object getFieldObject(Class clazz, Object obj, String filedName) {
            try {
                Field field = clazz.getDeclaredField(filedName);
                field.setAccessible(true);
                return field.get(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule) {
            try {
                Field field = clazz.getDeclaredField(filedName);
                field.setAccessible(true);
                field.set(obj, filedVaule);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule) {
            try {
                Class obj_class = Class.forName(className);
                setFieldObject(obj_class, obj, filedName, filedVaule);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        public static Object getStaticFieldObject(String className, String filedName) {
            return getFieldObject(className, null, filedName);
        }
    
        public static Object getStaticFieldObject(Class clazz, String filedName) {
            return getFieldObject(clazz, null, filedName);
        }
    
        public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) {
            setFieldObject(classname, null, filedName, filedVaule);
        }
    
        public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) {
            setFieldObject(clazz, null, filedName, filedVaule);
        }
    }
    
     
    展开全文
  • java语言替代品by Carlos Raphael 卡洛斯·... Java反射的更快替代品 (A faster alternative to Java Reflection) In the article Specification Pattern, for the sake of sanity, I didn’t mention about an unde...

    java语言替代品

    by Carlos Raphael

    卡洛斯·拉斐尔(Carlos Raphael)

    Java反射的更快替代品 (A faster alternative to Java Reflection)

    In the article Specification Pattern, for the sake of sanity, I didn’t mention about an underlying component to nicely make that thing happen. Now, I’ll elaborate a little bit more around the JavaBeanUtil class, that I put in place to read the value for a given fieldName from a particular javaBeanObject, which in that occasion turned out to be FxTransaction.

    在“ 规范模式”一文中 ,出于理智的考虑,我没有提到使该事情发生的基础组件。 现在,我将详细介绍JavaBeanUtil类,以便从特定的javaBeanObject读取给定fieldName的值在这种情况下该值原来是FxTransaction

    You can easily argue I could’ve basically used Apache Commons BeanUtils or one of its alternatives to achieve the same result. But I was interested in getting my own hands dirty with something different that I knew would be way faster than any library built on top of the widely known Java Reflection.

    您可以轻松地说出我本来可以使用Apache Commons BeanUtils或其替代方法之一来达到相同的结果。 但是我感兴趣的是用一种我知道的东西比在众所周知的Java Reflection之上构建的任何库更快的方式弄污自己。

    The enabler of the technique used to avoid the very slow reflection is the invokedynamic bytecode instruction. Briefly, invokedynamic (or “indy”) was the greatest thing introduced in Java 7 in order to pave the way for implementing dynamic languages on top of the JVM through dynamic method invocation. It also later allowed lambda expression and method reference in Java 8 as well as string concatenation in Java 9 to benefit from it.

    避免非常缓慢的反射的技术的推动者是invokedynamic 字节码指令。 简而言之, invokedynamic (或“ indy”)是Java 7中引入的最重要的功能,它为通过动态方法调用在JVM之上实现动态语言铺平了道路。 后来,它还允许Java 8中的lambda表达式方法引用以及Java 9中的字符串连接从中受益。

    In a nutshell, the technique I’m about to better describe below leverages LambdaMetafactory and MethodHandle in order to dynamically create an implementation of Function. Its single method delegates a call to the actual target method with a code defined inside of lambda body.

    简而言之,我将在下面更好地描述的技术利用LambdaMetafactoryMethodHandle来动态创建Function的实现。 它的单个方法使用在lambda主体内部定义的代码将调用委派给实际的目标方法。

    The target method in question here is the actual getter method that has direct access to the field we want to read. Also, I should say if you are quite familiar with the nice things that came up within Java 8, you will find the below code snippets fairly easy to understand. Otherwise, it may be tricky at a glance.

    这里讨论的目标方法是直接访问我们要读取的字段的实际getter方法。 另外,我应该说,如果您对Java 8中的出色功能非常熟悉,您将发现以下代码片段相当容易理解。 否则,乍看之下可能很棘手。

    窥视自制JavaBeanUtil (A peek at the homemade JavaBeanUtil)

    The following method is the utility used to read a value from a JavaBean field. It takes the JavaBean object and a single fieldA or even nested field separated by periods, for example, nestedJavaBean.nestedJavaBean.fieldA

    以下方法是用于从JavaBean字段读取值的实用程序。 它接受JavaBean对象和单个fieldA或什至由句点分隔的嵌套字段,例如nestedJavaBean.nestedJavaBean.fieldA

    For optimal performance, I’m caching the dynamically created function that is the actual way of reading the content of a given fieldName. So inside the getCachedFunction method, as you can see above, there’s a fast path leveraging the ClassValue for caching and there’s the slow createAndCacheFunction path executed only if nothing has been cached so far.

    为了获得最佳性能,我将缓存动态创建的函数,这是读取给定fieldName内容的实际方法。 因此,如上所示,在getCachedFunction方法内部,有一条利用ClassValue进行缓存的快速路径,并且只有在到目前为止尚未缓存任何内容的情况下,才执行慢速的createAndCacheFunction路径。

    The slow path will basically delegate to the createFunctions method that is returning a list of functions to be reduced by chaining them using Function::andThen. When functions are chained, you can imagine some sort of nested calls like getNestedJavaBean().getNestedJavaBean().getFieldA(). Finally, after chaining we simply put the reduced function in the cache calling cacheAndGetFunction method.

    慢速路径基本上将委托给createFunctions方法,该方法将通过使用Function::andThen链接它们来减少要减少的Function::andThen 。 当函数链接在一起时,您可以想象像getNestedJavaBean().getNestedJavaBean().getFieldA()这样的嵌套调用。 最后,在链接之后,我们只需将简化后的函数放在调用cacheAndGetFunction方法的缓存中。

    Drilling a bit more into the slow path of function creation, we need to individually navigate through the field path variable by splitting it as per below:

    深入研究函数创建的慢速路径,我们需要按如下所示拆分字段来分别浏览字段path变量:

    The above createFunctions method delegates the individual fieldName and its class holder type to createFunction method, which will locate the needed getter based upon javaBeanClass.getDeclaredMethods(). Once it’s located, it maps to a Tuple object (facility from Vavr library), that contains the return type of the getter method and the dynamically created function in which will act as if it was the actual getter method itself.

    上面的createFunctions方法将各个fieldName及其类持有者类型委托给createFunction方法,该方法将基于javaBeanClass.getDeclaredMethods()定位所需的getter。 找到后,它映射到一个Tuple对象(来自Vavr库的工具),该对象包含getter方法的返回类型和动态创建的函数,该函数在其中的作用就好像它是实际的getter方法本身一样。

    This tuple mapping is done by createTupleWithReturnTypeAndGetter in conjunction with createCallSite method as follows:

    该元组映射由createTupleWithReturnTypeAndGetter结合createCallSite方法完成,如下所示:

    In the above two methods, I make use of a constant called LOOKUP, which is simply a reference to MethodHandles.Lookup. With that, I can create a direct method handle based on the previously located getter method. And finally, the created MethodHandle is passed to createCallSite method whereby the lambda body for the function is produced using the LambdaMetafactory. From there, ultimately, we can obtain the CallSite instance, which is the function holder.

    在以上两种方法中,我使用了一个名为LOOKUP的常量,该常量只是对MethodHandles.Lookup的引用。 这样,我可以基于先前定位的getter方法创建直接方法句柄 。 最后,将创建的MethodHandle传递给createCallSite方法,从而使用LambdaMetafactory生成函数的lambda主体。 最终,我们可以从那里获得CallSite实例,它是函数的所有者。

    Note that if I wanted to deal with setters I could use a similar approach by leveraging BiFunction instead of Function.

    请注意,如果我想处理二传手,则可以通过利用BiFunction而不是Function来使用类似的方法。

    基准测试 (Benchmark)

    In order to measure the gains of performance, I used the ever-awesome JMH (Java Microbenchmark Harness), which will likely be part of the JDK 12. As you may know, results are bound to the platform, so for reference I’ll be utilizing a single 1x6 i5-8600K 3.6GHz and Linux x86_64 as well as Oracle JDK 8u191 and GraalVM EE 1.0.0-rc9.

    为了衡量性能的提高,我使用了令人敬畏的JMH( Java Microbenchmark Harness ),它很可能是JDK 12的一部分。 如您所知,结果与平台有关,因此作为参考,我将使用单个1x6 i5-8600K 3.6GHz Linux x86_64 以及Oracle JDK 8u191GraalVM EE 1.0.0-rc9

    For comparison, I used Apache Commons BeanUtils, a well-known library for most Java developers, and one of its alternatives called Jodd BeanUtil which claims to be almost 20% faster.

    为了进行比较,我使用了Apache Commons BeanUtils (这是大多数Java开发人员所熟知的库)及其替代方案之一,称为Jodd BeanUtil ,它声称快了近20%

    Benchmark scenario is set as follows:

    基准情景设置如下:

    The benchmark is driven by how deep we are going to retrieve some value as per the four different levels specified above. For each fieldName, JMH will perform 5 iterations of 3 seconds each to warm things up and then 5 iterations of 1 second each to actually measure. Each scenario will then repeat 3 times to reasonably gather the metrics.

    基准是由我们根据上述四个不同级别检索某些值的深度决定的。 对于每个fieldName ,JMH将执行5次迭代(每次3秒)以预热事物,然后执行5次迭代(每秒钟1秒)以进行实际测量。 然后,每个方案将重复3次以合理收集指标。

    结果 (Results)

    Let’s start with the results gathered from the JDK 8u191 run:

    让我们从JDK 8u191运行收集的结果开始:

    The worst scenario using invokedynamic approach is much faster than the fastest scenario from the other two libraries. That’s a huge difference, and if you’re doubting the results, you can always download the source code and play around as you like.

    使用invokedynamic方法的最坏情况比其他两个库中最快的情况要快得多。 这是一个巨大的差异,如果您对结果有所怀疑,可以随时下载源代码并随意播放。

    Now, let’s see how the same benchmark performs with GraalVM EE 1.0.0-rc9

    现在,让我们看看相同的基准在GraalVM EE 1.0.0-rc9

    Full results can be viewed here with the nice JMH Visualizer.

    完整的结果可以在此处使用漂亮的JMH Visualizer查看。

    观察结果 (Observations)

    The huge difference is because JIT-compiler knows CallSite and MethodHandle very well and knows how to inline them quite well as opposed to the reflection approach. Also, you can see how promising GraalVM is. Its compiler does a truly awesome job being capable of a great performance enhancement for the reflection approach.

    巨大的差异是因为JIT编译器非常了解CallSiteMethodHandle并且知道如何很好地内联它们,而不是反射方法。 此外,您可以看到GraalVM的前景如何。 它的编译器做的确实很棒,能够为反射方法带来极大的性能增强。

    If you’re curious and want to play further, I encourage you to pull the source code from my Github. Bear in mind I’m not encouraging you to do your own homemade JavaBeanUtil and use in production. Rather my aim here is to simply showcase my experiment and the possibilities we can get from invokedynamic.

    如果您好奇并想进一步学习,建议您从我的Github中提取源代码。 请记住,我不鼓励您做自己的自制JavaBeanUtil并在生产中使用。 相反,我的目的只是简单地展示我的实验以及我们从invokedynamic可以得到的可能性。

    翻译自: https://www.freecodecamp.org/news/a-faster-alternative-to-java-reflection-db6b1e48c33e/

    java语言替代品

    展开全文
  • 文章目录什么是反射反射能干什么实现反射的几种方式反射的实现反射的缺点优点:缺点:需要考虑反射的慢吗有什么方法可以替代反射吗javassit生成代码BCELASM其他框架应该选哪个呢扩展字面常量不会触发初始化 ...

    什么是反射

    反射就是在程序运行期间,动态的获取一个类的Class对象,然后通过Class对象来解析类的结构,比如说得到这个类有什么方法,有什么属性。得到这些方法或属性后,可以调用指定对象上的这个方法,也可以给指定对象上的这个属性赋值。
    一个类都有什么东西:成员变量、方法、构造方法、注解等信息,利用反射技术把这些信息映射成一个对象,这正体现了在java中“万物皆对象”的理念,连java类都被作为了一个对象。
    在Java中用来表示运行时类型信息的对应类就是Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中

    public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type, AnnotatedElement {
        private static final int ANNOTATION= 0x00002000;
        private static final int ENUM      = 0x00004000;
        private static final int SYNTHETIC = 0x00001000;
    
        private static native void registerNatives();
        static {
            registerNatives();
        }
    
        /*
         * Private constructor. Only the Java Virtual Machine creates Class objects.(私有构造,只能由JVM创建该类)
         * This constructor is not used and prevents the default constructor being
         * generated.
         */
        private Class(ClassLoader loader) {
            // Initialize final field for classLoader.  The initialization value of non-null
            // prevents future JIT optimizations from assuming this final field is null.
            classLoader = loader;
        }
    

    反射能干什么

    一直以来反射技术都是Java中的闪亮点,这也是目前大部分框架(如Spring/Mybatis等)得以实现的支柱。在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。
    一般来说反射是用来做框架的,或者说可以做一些抽象度比较高的底层代码,反射在日常的开发中用到的不多,但是咱们还必须搞懂它,因为搞懂了反射以后,可以帮助理解框架的一些底层原理。当你看完下面的示例之后,你就体会到反射的作用了,会让人感觉很舒服,你会体会到在java中你基本上可以为所欲为了。另外一句话说:反射是框架设计的灵魂。
    使用场景,比如:

    1. 需要根据运行时候的配置来动态生成相应的对象,例如通过一个类的全限定类名字符串来生成类的对象
    2. 根据注解来动态确定程序执行逻辑
    3. 生成动态代理
    4. 突破一些sdk的API接口限制(访问一个类的私有字段,私有方法等)

    等其他使用场景。

    实现反射的几种方式

    实现反射有3中方式

    class.forName

    Class clazz=Class.forName(“com.zejian.Gum”),Class.forName()方法的调用将会返回一个对应类的Class对象,forName方法是Class类的一个static成员方法,记住所有的Class对象都源于这个Class类,因此Class类中定义的方法将适应所有Class对象

    实例对象的getClass方法

    通过一个实例对象获取一个类的Class对象,其中的getClass()是从顶级类Object继承而来的,它将返回表示该对象的实际类型的Class对象引用

    Gum gum = new Gum();
    Class clazz2=gum.getClass();
    System.out.println("new=clazz2:"+clazz2.getName());
    

    字面常量,类名.class

    Class clazz = Gum.class;这种方式相对前面两种方法更加简单,更安全。因为它在编译器就会受到编译器的检查同时由于无需调用forName方法效率也会更高,因为通过字面量的方法获取Class对象的引用不会自动初始化该类,不仅可以应用于普通的类,也可以应用用接口,数组以及基本数据类型,这点在反射技术应用传递参数时很有帮助。

    反射的实现

    import java.lang.reflect.Constructor;
    import java.lang.reflect.*;
    
    /*Class:代表一个字节码文件的对象,每当有类被加载进内存,JVM就会在堆上给
     *        该类创建一个代表该类的对象。每个类的Class对象是的。
     *Class类没有构造方法,获得类对应的Class方法有3种
     *1.:getClass()、2.类、接口.class 、3.Class.forName("类全名");
     *比较推荐使用第3种方式,使用前两种方式程序扩展性不好。
     *
     *Class类中定义了许多关于获取类中信息的方法:
     *1.获得该类的构造方法,属性,方法、实例的方法。包含特定情况的获得
     *2.获得该类的父类,实现的接口,该类的类加载器,类名、包名等。
     *3.判断该类的具体是接口、类、内部类等
     *4.方法中加Declared表示可以获得本类定义的任何方法和属性
     *
     *注意:关于获得到的方法、属性、构造器的具体操作被封装在import java.lang.reflect包里面
     *Method:里面最常用的方法invoke(对象,可变参数列表)--调用指定的方法
     *Field:get/set;获取和修改属性值
     *Constrcutor:使用newInstance(可变参数列表)--调用指定构造方法创建类的实例
     *注意:私有的要调用前先去掉访问权限限制setAccssible()
     * */
    public class ReflectionWithClass {
    
        public static void main(String[] args) throws Exception {
    
            //第一种方式获得Class对象,比较麻烦,要先创建对象,再使用对象调用方法
            HelloKitty ht = new HelloKitty();
            Class clazz = ht.getClass();
    
            //第二种方式获得Class对象。使用静态的属性创建
            Class clazz1 = HelloKitty.class;
    
            //第三种使用Class对象的静态方法获得Class对象
            Class clazz2 = Class.forName("HelloKitty");
    
            //获得该类的类加载器
            ClassLoader c = clazz2.getClassLoader();
            System.out.println(c.toString());
    
            Class clazz3 = String.class;
            System.out.println(clazz3.getClassLoader());
    
            //获得该类的实例
            Object obj = clazz2.newInstance();
            //获得该类的构造器---公开的,getDeclaredConstructors()--可以获得私有的
            Constructor[] con = clazz2.getDeclaredConstructors();
            for(Constructor cc:con){
                System.out.print(cc + " ");
            }
    
            //获得类的方法
            Method[] mm = clazz2.getDeclaredMethods();
            for(Method mmm:mm){
                System.out.print(mmm + " ");
            }
    
            System.out.println();
            //获取特定的方法
            Method m = clazz2.getMethod("walk",null);
            System.out.println(m.toString());
    
            Field[] f = clazz2.getDeclaredFields();
            for(Field ff:f){
                System.out.print(ff+ " ");
            }
    
            //调用指定的方法---先获取,在调用;注意私有方法先设置访问权限
            Method m1 = clazz2.getMethod("walk", null);
            System.out.println("hahahhha");
            m1.invoke(obj,null);
    
            //调用指定的构造方法创建类实例;先获取在调用
            Constructor cc = clazz2.getConstructor(int.class,String.class);
            Object o1 = cc.newInstance(12,"blue");
    
            //获取和修改对象的属性值
            Field ffs = clazz2.getDeclaredField("age");
            ffs.setAccessible(true);
            ffs.set(obj, 29);
            Object oo = ffs.get(obj);
            System.out.println(oo);
    
        }
    
    }
    
    class HelloKitty {
        private int age;
        public String color = "pink";
        public HelloKitty() {}
    
    
        public HelloKitty(int age) {
            this.age = age;
        }
    
        public HelloKitty(int age,String color) {
            this.age = age;
            this.color = color;
            System.out.println("okokok");
        }
    
        public void walk(){
            System.out.println("hhhhhhhhhhhhh");
        }
    
        public void talk(int i){
            System.out.println(i + "----------" + age);
        }
    }
    

    参考链接:
    https://blog.csdn.net/ju_362204801/article/details/90578678

    反射的缺点

    优点:

    优点就是反射的特性,可以在运行时获得一个编译时还不存在的类的信息,灵活性特别强

    缺点:

    但反射的使用原则是:能不用则尽量不用,为啥:

    1. 编译器将无法在编译期间为我们做类型检查的工作,就是说类型错误不能在编译时候被发现了
    2. 使用反射时候的代码,乱、难写、代码可读性不高,而且还容易出错
    3. 慢。为什么慢,大家提及最多的是:
      3.1 不使用反射时编译器会优化对象实例化的过程,而使用反射则完全不优化,那么实例化对象这块就会有比较大的差距。
      3.2 查找检查等操作上的差距

    需要考虑反射的慢吗

    https://blog.csdn.net/f641385712/article/details/81225239
    最终得到的结论就是,不需要考虑反射的慢。
    反射大概比直接调用慢50~100倍(JDK7以后对反射有优化,大概在5倍到20倍不等了),但是需要你在执行100万遍的时候才会有所感觉,如果你只是偶尔调用一下反射,请忘记反射带来的性能影响,如果你需要大量调用反射,请考虑缓存

    有什么方法可以替代反射吗

    由于反射的优点是其动态性,而缺点之一就是有性能损失,所以大家就想怎么保留其动态性而减少性能损失。目前比较流行的一种做法是使用编译时代码生成的方式取代反射!很多库现在都是使用这种方式,例如依赖注入库 dagger2,MyBatis等
    反射跟代码生成完全是两种不同的原理,反射是从现有的文件类中获取信息,而代码生成,则是动态的从无到有生成想要的代码。
    反射是动态的获取,在运行时根据逻辑获取想要的信息,代码生成是根据逻辑生成想要的代码,两者有异曲同工的意思。

    javassit生成代码

    Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。

    它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。

    参考链接:https://www.cnblogs.com/rickiyang/p/11336268.html
    或者看api文档http://www.javassist.org/html/

    BCEL

    使用 Apache Byte Code Engineering Library (BCEL)。与 Javassist 所支持的源代码接口不同,BCEL 在实际的 JVM 指令层次上进行操作。在希望对程序执行的每一步进行控制时,底层方法使 BCEL 很有用,但是当两者都可以胜任时,它也使 BCEL 的使用比 Javassist 要复杂得多。
    关于BCEL的博客、文档非常少,博主在找资料的时候,以下几个博客还行,但是找现有被使用到的应用场景,却没有,故博主也失去了对他的兴趣。
    https://blog.csdn.net/yczz/article/details/14497897
    https://blog.csdn.net/iteye_12751/article/details/82550534
    官网:https://sourceforge.net/projects/bcel/

    ASM

    ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
    具体的ASM实现,请参看以下博主搜到的连接
    https://zhuanlan.zhihu.com/p/94498015?utm_source=wechat_timeline
    https://blog.csdn.net/zhuoxiuwu/article/details/78619645
    https://www.jb51.net/article/103701.htm
    三个连接从不同的方面讲述了使用、底层等信息,请自行参考。

    其他框架

    也有其他框架,博主就不列举了,请有兴趣的同学一起共勉。

    应该选哪个呢

    那么这几个都是字节码操作框架,应该选哪个呢?—答案是ASM,ASM框架也是最多被使用。

    ASM字节码处理框架是用Java开发的而且使用基于访问者模式生成字节码及驱动类到字节码的转换。这允许开发人员避免直接处理方法字节码中的类常量池及偏移,因此为开发人员隐藏了字节码的复杂性并且相对于其他类似工具如BCEL, SERP, or Javassist提供了更好的性能。

    比对链接:https://www.jianshu.com/p/db1edeb013ff

    总之,记住有其他框架,了解最优的框架即可。

    扩展

    字面常量不会触发初始化

    使用字面常量的方式获取Class对象的引用不会触发类的初始化,先了解一下类加载过程

    在这里插入图片描述
    加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象

    链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用。

    初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。

    由此可知,我们获取字面常量的Class引用时,触发的应该是加载阶段,因为在这个阶段Class对象已创建完成,获取其引用并不困难,而无需触发类的最后阶段初始化。下面通过小例子来验证这个过程:

    class Initable {
      //编译期静态常量
      static final int staticFinal = 47;
      //非编期静态常量
      static final int staticFinal2 =
        ClassInitialization.rand.nextInt(1000);
      static {
        System.out.println("Initializing Initable");
      }
    }
    
    class Initable2 {
      //静态成员变量
      static int staticNonFinal = 147;
      static {
        System.out.println("Initializing Initable2");
      }
    }
    
    class Initable3 {
      //静态成员变量
      static int staticNonFinal = 74;
      static {
        System.out.println("Initializing Initable3");
      }
    }
    
    public class ClassInitialization {
      public static Random rand = new Random(47);
      public static void main(String[] args) throws Exception {
        //字面常量获取方式获取Class对象
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        //不触发类初始化
        System.out.println(Initable.staticFinal);
        //会触发类初始化
        System.out.println(Initable.staticFinal2);
        //会触发类初始化
        System.out.println(Initable2.staticNonFinal);
        //forName方法获取Class对象
        Class initable3 = Class.forName("Initable3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initable3.staticNonFinal);
      }
    }
    
    输出结果:
    After creating Initable ref
    47
    Initializing Initable
    258
    Initializing Initable2
    147
    Initializing Initable3
    After creating Initable3 ref
    74
    
    展开全文
  • java反射

    2021-04-15 15:25:47
    反射是Java中强大的技术,很多优秀的开源框架都是通过反射完成的,后来因为java反射影响性能,所以被运行时注解APT替代了,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架。 JAVA反射机制是在运行...

    一、简介

    反射是Java中强大的技术,很多优秀的开源框架都是通过反射完成的,后来因为java反射影响性能,所以被运行时注解APT替代了,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架。
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。反射就是把java类中的各种成分映射成一个个的Java对象。

    二、为什么使用反射

    Java中编译类型有两种:

    • 静态编译:在编译时确定类型,绑定对象即通过。
    • 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。

    在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。

    实现Java反射机制的类都位于java.lang.reflect包中:

    • Class类:代表一个类
    • Field类:代表类的成员变量(类的属性)
    • Method类:代表类的方法
    • Constructor类:代表类的构造方法
    • Array类:提供了动态创建数组,以及访问数组的元素的静态方法

    为了使用类而做的准备工作一般有以下3个步骤:

    • 加载:由类加载器完成,找到对应的字节码,创建一个Class对象
    • 链接:验证类中的字节码,为静态域分配空间
    • 初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块

    jdk动态代理网址推荐:
    https://www.nhooo.com/note/qagnkd.html
    https://my.oschina.net/u/4613350/blog/4810435

    展开全文
  • java反射的使用

    千次阅读 2018-11-14 18:49:27
    java反射的使用 Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性 灵活使用反射能让我们代码更加灵活,这里比如JDBC原生代码注册驱动,...
  • Java反射机制

    2013-04-23 13:57:24
    反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。下面具体介绍一下java反射机制。这里你将颠覆原来对java的理解。 Java反射...
  • Java反射机制(带应用)

    千次阅读 2016-08-07 21:45:17
    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能 称为java语言的反射机制...
  • java 反射

    千次阅读 2011-12-13 09:47:02
    通过反射查看类信息 Java程序许多对象在运行时都会出现两种类型:编译时类型和运行时类型,Person p=new Students();这行代码会生成一个p变量,该变量的编译类型为Person,运行时类型为Student;除此之外,还有更...
  • Java8替代传统反射动态获取成员变量值的一个示例 业务背景 新人注册发优惠券 要发送的券的信息以json的格式配置 如下所示 { "count":2 #发放2张 "days":"3,7" #有效期天数 一个有效期天数是3天 一个是7天 "price...
  • Java反射原理简析

    2020-04-30 23:45:40
    Java反射机制允许我们动态的调用某个对象的方法/构造函数,获取某个对象的属性等,而无需在编码时确定调用的对象。这种机制在我们常用的框架中也非常常见。 1.原理简介 类actionClass = Class.forName(“ MyClass...
  • java反射原理

    千次阅读 2012-10-01 23:18:02
    反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。下面具体介绍一下java反射机制。这里你将颠覆原来对java的理解。 Java反射机制...
  • Java 反射机制

    千次阅读 2011-08-29 15:12:21
    反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。下面具体介绍一下java反射机制。这里你将颠覆原来对java的理解。 Java反射机制...
  • java 反射的使用

    千次阅读 2012-08-10 13:31:04
     JAVA反射使用手记 本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。 在开始之前,我...
  • java反射常用方法

    千次阅读 2016-08-23 17:12:28
    JAVA反射使用手记  本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。  在开始之前...
  • 1、为什么要用反射? 通常写代码时,涉及调用关系,都是直接在另一个类中new一个对象,当...JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一
  • java反射注解

    2013-04-19 01:43:54
    Java反射机制是在运动状态中,对于任意一个类都能知道这个类的所有属性和方法。 对于任意一个对象都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。 ...
  • JAVA反射使用手记

    2012-02-28 19:55:14
    JAVA反射使用手记  本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。  在...
  • JAVA反射机制

    2012-07-04 16:34:04
    反射(Reflection)是java语言的一个特性,它允程序在运行时来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。这个能特定我们不常看到,但是在其他的比如C...
  • Java反射与注解

    2017-08-28 01:05:50
    JAVA反射 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。反射机制是什么 面试有可能会问到,这句话不管你能不能理解,...
  • Java反射机制允许程序在运行时判断分析任意一个类的结构,包括成员变量和方法,并调用任意一个对象的方法。Eclipse可以自动弹出对象的方法及属性,就是利用了反射的原理。 java动态代理可以在不改变被调用对象源码的...
  • java反射简短手记

    2012-07-25 16:52:42
    本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。  在开始之前,我先定义一个测试类...
  • java反射机制的实现原理 反射机制: 所谓的反射机制就是java语言在运行时拥有一项自观的能力。 通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。 下面具体介绍一下java的反射机制。这里你将颠覆原来...
  • JAVA反射机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:...
  • java反射注解的用途

    2014-05-04 23:51:44
    很多人都知道java反射机制和注解技术。反射(Reflection)就是加载类,并解剖出类的各个组成部分;而Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有...
  • java反射   一、反射的含义: Java类的一种自审机制,它是一种可以认识自身和动态改变自身的一种行为。 二、反射的使用: 1、通常在对文件操作时需要用到类的反射机制,通过反射可以获取一个未知类的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,383
精华内容 12,953
关键字:

java反射的替代

java 订阅