精华内容
参与话题
问答
  • 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反射知识点小结

    万次阅读 2019-05-04 15:54:19
    参考代码:https://github.com/ZhangXiaoixiang/reflectdemo 说明:收集的网上资料,自己整理收集 视频教程(腾讯课堂): ...term_id=100354189&taid=2094161629319039&vid=t1426y3hmo...

    参考代码:https://github.com/ZhangXiaoixiang/reflectdemo

    springboot版本的(找java-base分支)https://gitee.com/zhang-xiao-xiang/all-demo-parent.git

    说明:收集的网上资料,自己整理收集

    视频教程(腾讯课堂): https://ke.qq.com/webcourse/index.html#cid=298879&term_id=100354189&taid=2094161629319039&vid=t1426y3hmoz
    视频教程(网易云课堂): https://study.163.com/course/courseLearn.htm?courseId=1005927016#/learn/video?lessonId=1053358058&courseId=1005927016

    1:通过反射获取类

    package com.example.java.base.reflect;
    
    import org.junit.Test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.Objects;
    
    /**
     * ReflectDemo01:反射获取类相关(基本操作)
     *
     * @author zhangxiaoxiang
     * @date 2020/11/3
     */
    public class ReflectTest {
        /**
         * 通过反射获取类(获取反射对象的3种方式 意区别,有的需要抛异常有的不需要)
         */
        public static void demo01() {
            try {
                Class<?> perClazz = Class.forName("com.example.java.base.reflect.Person");
                System.out.println("方式1===>通过Class.forName(\"全类名\")\t" + perClazz);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            Class<?> perClazz2 = Person.class;
            System.out.println("方式2===>类名.class\t" + perClazz2);
    
            Person per = new Person();
            Class<?> perClazz3 = per.getClass();
            System.out.println("方式3===>对象.getClass()\t" + perClazz3);
        }
    
    
        /**
         * 为了方便,这里定义一个方法获取反射,方便后面测试,这里使用的方式一为示例
         *
         * @return 获取到的反射对象
         */
        public static Class<?> getClassTest() {
            //Class入口
            Class<?> perClazz = null;
            try {
                perClazz = Class.forName("com.example.java.base.reflect.Person");
                return perClazz;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 获取方法(所有的都可以)
         */
        public static void demo02() throws IllegalAccessException, InstantiationException {
            //Class入口
            Class<?> perClazz = getClassTest();
            //获取所有的 公共的方法(1.本类 以及 父类、接口中的所有方法  2.符合访问修饰符规律),注意空指针
            System.out.println("===================================获取方法(所有的都可以)===========================");
            Method[] methods = Objects.requireNonNull(perClazz).getMethods();
            System.out.println("公共的方法==========");
            for (Method method : methods) {
                System.out.println(method);
            }
            System.out.println("所有的方法==========");
            //获取当前类的所有方法(1.只能是当前类   2.忽略访问修饰符限制)
            Method[] declaredMethods = perClazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                System.out.println(declaredMethod);
            }
            System.out.println("===================================获取所有的接口===========================");
    
            Class<?>[] interfaces = perClazz.getInterfaces();
            for (Class<?> inter : interfaces) {
                System.out.println(inter);
            }
            System.out.println("===================================获取所有的父类===========================");
            Class<?> superclass = perClazz.getSuperclass();
            System.out.println(superclass);
            System.out.println("===================================获取所有的构造方法===========================");
            Constructor<?>[] constructors = perClazz.getConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println("===================================获取所有的构造方法===========================");
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println("===================================获取所有的属性(公共和私有等)===========================");
            System.out.println("所有公共属性====");
            Field[] fields = perClazz.getFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            System.out.println("所有属性  (属性的 :公共属性 所有属性的区别 同“方法”)====");
            Field[] declaredFields = perClazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField);
            }
            System.out.println("============================获取当前反射所代表类(接口) 的对象(实例)===========================");
            Object instance = perClazz.newInstance();
            Person per = (Person) instance;
            per.interface2Method();
        }
    
        /**
         * 测试
         *
         * @throws InstantiationException 异常1
         * @throws IllegalAccessException 异常2
         */
        @Test
        public void test2() throws InstantiationException, IllegalAccessException {
            System.out.println("************************获取反射对象的3种方式************************");
            demo01();
            System.out.println("************************获取反射其他信息********************************");
            demo02();
        }
    }

    2获取到反射类实例后操作

    package com.example.java.base.reflect;
    
    import org.junit.Test;
    
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Objects;
    import java.util.Properties;
    
    /**
     * ReflectDemo02:反射获取类的实例,并操作
     *
     * @author zhangxiaoxiang
     * @date 2020/11/3
     */
    public class Reflect2Test {
        /**
         * 为了方便,这里定义一个方法获取反射,方便后面测试,这里使用的方式一为示例
         *
         * @return 获取到的反射对象
         */
        public static Class<?> getClassTest() {
            Class<?> perClazz = null;
            try {
                perClazz = Class.forName("com.example.java.base.reflect.Person");
                return perClazz;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 获取对象的实例,并操作对象
         *
         * @throws InstantiationException
         * @throws IllegalAccessException
         */
        public static void demo01() throws IllegalAccessException, InstantiationException, NoSuchFieldException,
                NoSuchMethodException, InvocationTargetException, IOException {
            Class<?> perClazz = getClassTest();
            Person per = (Person) Objects.requireNonNull(perClazz).newInstance();
            per.setId(1);
            per.setName("zs");
            per.setAge(23);
            per.setDesc("我是描述属性");
            System.out.println("===============================为对象赋值============================================");
            System.out.println(per.toString());
            System.out.println("===============================操作属性============================================");
            Field idField = perClazz.getDeclaredField("id");
            //访问的是private修饰的id,但是private是私有
            //修改属性的访问权限  使用反射时,如果 因为访问修饰符限制造成异常,可以通过  Field/Method/Constructor.setAccessible(true)
            idField.setAccessible(true);
            //per.setId(1);
            idField.set(per, 2);
            System.out.println("修改私有属性后:"+per.toString());
            Method method = perClazz.getDeclaredMethod("privateMethodTest", null);
            method.setAccessible(true);
            //方法的调用:invoke()
            method.invoke(per, null);
            //per.say(xxx);
            Method method2 = perClazz.getDeclaredMethod("privateMethodTest", String.class);
            method2.setAccessible(true);
            method2.invoke(per, "我是pram");
            System.out.println("===============================操作构造方法============================================");
            //		Constructor<?>[] constructors = perClazz.getConstructors() ;//公共
            //		for(Constructor constructor:constructors) {
            //			System.out.println(constructor);
            //		}
            
            //		Constructor<?>[] constructors = perClazz.getDeclaredConstructors() ;//本类
            //		for(Constructor constructor:constructors) {
            //			System.out.println(constructor);
            //		}
    
            //获取指定的构造方法
            //在反射中,根据类型 获取方法时:基本类型(int、char...)和包装类(Integer,Character)是不同的类型
            // Constructor<?> constructor = perClazz.getConstructor(Integer.class);
            Constructor<?> constructor = perClazz.getConstructor(int.class);
            System.out.println("获取指定的构造方法===>"+constructor);
    
            Constructor<?> constructor2 = perClazz.getDeclaredConstructor(String.class);
            System.out.println("获取指定的构造方法===>"+constructor2);
            //Person per = new Person() ;
    
    
            //根据获取的private构造方法,获取对象实例
            constructor2.setAccessible(true);
            Person per3 = (Person) constructor2.newInstance("zs");
            System.out.println("获取带有String的构造方法" + per3);
            //因为constructor是 有参构造方法(Integer还是int这里构造的就是什么值)
            Person instance2 = (Person) constructor.newInstance(23);
            System.out.println("获取带有Integer的构造方法" +instance2);
    
            Constructor<?> constructor3 = perClazz.getConstructor();
            //因为constructor3是 无参构造方法,因此不需要传值
            Person instance = (Person) constructor3.newInstance();
            System.out.println("无参构造"+instance);
            System.out.println("===============================动态加载 类名 和方法===============================");
            Properties prop = new Properties();
            prop.load(new FileReader("src/main/resources/class.txt"));
            String classname = prop.getProperty("classname");
            String methodname = prop.getProperty("methodname");
            Class<?> perClazz1 = null;
            try {
                perClazz1 = Class.forName(classname);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            method = perClazz1.getMethod(methodname);
            System.out.println("===>反射读取配置文件并调用");
            method.invoke(perClazz1.newInstance());
            System.out.println("===============================反射可以 越过泛型检查===============================");
            System.out.println("虽然可以通过反射 访问private等访问修饰符不允许访问的属性/方法,也可以忽略掉泛型的约束;" +
                    "但实际开发 不建议这样使用,因此可能造成程序的混乱。");
            ArrayList<Integer> list = new ArrayList<>();
            list.add(123);
            list.add(3);
            list.add(2);
    //		list.add("zs") ;
    
            Class<?> listClazz = list.getClass();
             method = listClazz.getMethod("add", Object.class);
            method.invoke(list, "zs...");
    
            System.out.println("反射可以 越过泛型检查===>"+list);
    
            System.out.println("===============================使用工具类复制===============================");
            Person person = new Person();
            PropertyUtil.setProperty(person, "name", "长草颜团子");
            PropertyUtil.setProperty(person, "age", 18);
            Student stu = new Student();
            PropertyUtil.setProperty(stu, "score", 98);
    
            System.out.println(person.toString());
            System.out.println(stu.getScore());
        }
        @Test
        public void test2() throws InstantiationException, IllegalAccessException, NoSuchMethodException,
                NoSuchFieldException, InvocationTargetException, IOException {
            System.out.println("************************反射获取类的实例,并操作************************");
            demo01();
        }
    
    
    }

     3项目结构示意图

     

     

    展开全文
  • Java反射

    千次阅读 多人点赞 2019-08-28 22:58:42
    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。 用途 ...

    反射

    概述

    定义

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

    用途

    通过反射,Java 代码可以发现有关已加载类的字段,方法和构造函数的信息,并可以在安全限制内对这些字段,方法和构造函数进行操作。

    很多人都认为反射在实际Java中开发应用中并不广泛,其实不然。当我们在使用 IDE(如 IDEA/Eclipse)时,当我们输入一个对象或者类并调用它的属性和方法时,一按 (“.”)点号,编译器就会自动列出她的属性或方法,这里就会用到反射。

    反射最重要的用途就是开发各种通用框架

    很多框架(比如 Spring)都是配置化的(比如Spring 通过 XML 配置模式装载 Bean),为了保证框架的通用性,他们可能根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

    对于框架开发人员来说,反射作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。

    Java反射框架提供一下功能:

    • 在运行时判定任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判定任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的方法

    反射的优缺点

    反射的优点

    使用反射机制,代码可以在运行时装配,提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需硬编码目标类

    反射的缺点

    性能问题:使用反射基本上是一种解释操作,JVM无法对这些代码进行优化,因此,反射操作的效率要比那些非反射操作低得多。反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,对性能要求高的程序中不建议使用

    安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行

    内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用——代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

    反射机制的相关类

    与Java反射相关的类如下:

    类名 用途
    Class类 代表类的实体,在运行的Java应用程序中表示类和接口
    Field类 代表类的成员变量(成员变量也称为类的属性)
    Method类 代表类的方法
    Constructor类 代表类的构造方法

    Class类

    Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。

    • 获得类相关的方法
    方法 用途
    asSubclass(Class clazz) 把传递的类的对象转换成代表其子类的对象
    Cast 把对象转换成代表类或是接口的对象
    getClassLoader() 获得类的加载器
    getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
    getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
    forName(String className) 根据类名返回类的对象
    getName() 获得类的完整路径名字
    newInstance() 创建类的实例
    getPackage() 获得类的包
    getSimpleName() 获得类的名字
    getSuperclass() 获得当前类继承的父类的名字
    getInterfaces() 获得当前类实现的类或是接口
    • 获得类中属性相关的方法
    方法 用途
    getField(String name) 获得某个公有的属性对象
    getFields() 获得所有公有的属性对象
    getDeclaredField(String name) 获得某个属性对象
    getDeclaredFields() 获得所有属性对象
    • 获得类中注解相关的方法
    方法 用途
    getAnnotation(Class annotationClass) 返回该类中与参数类型匹配的公有注解对象
    getAnnotations() 返回该类所有的公有注解对象
    getDeclaredAnnotation(Class annotationClass) 返回该类中与参数类型匹配的所有注解对象
    getDeclaredAnnotations() 返回该类所有的注解对象
    • 获得类中构造器相关的方法
    方法 用途
    getConstructor(Class…<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
    getConstructors() 获得该类的所有公有构造方法
    getDeclaredConstructor(Class…<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
    getDeclaredConstructors() 获得该类所有构造方法
    • 获得类中方法相关的方法
    方法 用途
    getMethod(String name, Class…<?> parameterTypes) 获得该类某个公有的方法
    getMethods() 获得该类所有公有的方法
    getDeclaredMethod(String name, Class…<?> parameterTypes) 获得该类某个方法
    getDeclaredMethods() 获得该类所有方法
    • 类中其他重要的方法
    方法 用途
    isAnnotation() 如果是注解类型则返回true
    isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定类型注解类型则返回true
    isAnonymousClass() 如果是匿名类则返回true
    isArray() 如果是一个数组类则返回true
    isEnum() 如果是枚举类则返回true
    isInstance(Object obj) 如果obj是该类的实例则返回true
    isInterface() 如果是接口类则返回true
    isLocalClass() 如果是局部类则返回true
    isMemberClass() 如果是内部类则返回true

    Field类

    Field代表类的成员变量(成员变量也称为类的属性)。

    方法 用途
    equals(Object obj) 属性与obj相等则返回true
    get(Object obj) 获得obj中对应的属性值
    set(Object obj, Object value) 设置obj中对应属性值

    Method类

    Method代表类的方法。

    方法 用途
    invoke(Object obj, Object… args) 传递object对象及参数调用该对象对应的方法

    Constructor类

    Constructor代表类的构造方法。

    方法 用途
    newInstance(Object… initargs) 根据传递的参数创建类的对象

    示例

    为了演示反射的使用,首先构造一个与书籍相关的model——Book.java,然后了解获取Class类对象的三种方法,最后通过反射方法示例创建对象、反射私有构造方法、反射私有属性、反射私有方法

    被反射类Book.java

    public class Book{
        private final static String TAG = "BookTag";
    
        private String name;
        private String author;
    
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", author='" + author + '\'' +
                    '}';
        }
    
        public Book() {
        }
    
        private Book(String name, String author) {
            this.name = name;
            this.author = author;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        private String declaredMethod(int index) {
            String string = null;
            switch (index) {
                case 0:
                    string = "I am declaredMethod 0 !";
                    break;
                case 1:
                    string = "I am declaredMethod 1 !";
                    break;
                default:
                    string = "I am declaredMethod -1 !";
            }
    
            return string;
        }
    }
    

    获取Class类对象的三种方法

    在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。
    在 Java API 中,获取 Class 类对象的三种方法
    方法一:使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。但可能抛出 ClassNotFoundException 异常
    Class<?> classBook = Class.forName(“com.jourwon.reflect.Book”);

    方法二:这种方法只适合在编译前就知道操作的 Class。直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高,这说明任何一个类都有一个隐含的静态成员变量 class
    Class clz = Book.class;

    方法三:使用类对象的 getClass() 方法。
    Book book1 = new Book();
    Class<? extends Book> book1Class = book1.getClass();

    @Test
    public void getClz() {
        try {
            // 方法一,使用 Class.forName 静态方法
            Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
    
            // 方法二:直接通过 类名.class 的方式得到
            Class clz = Book.class;
    
            // 方法三:使用类对象的 getClass() 方法。
            Book book1 = new Book();
            Class<? extends Book> book1Class = book1.getClass();
    
            System.out.println(classBook == clz);
            System.out.println(classBook == book1Class);
    
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    

    输出结果都是true,可以知道三种方法获取到的Class对象都是同一个对象

    true
    true
    

    反射常用类和方法测试

    public class ReflectClass {
    
        private static final Logger log = LoggerFactory.getLogger(ReflectClass.class);
    
        // 创建对象
        @Test
        public void reflectNewInstance() {
            try {
                Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
    
                Object objectBook = classBook.newInstance();
                Book book = (Book) objectBook;
                book.setName("Java高级特性-反射-创建对象");
                book.setAuthor("JourWon");
                log.info(book.toString());
    
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        // 反射私有的构造方法
        @Test
        public void reflectPrivateConstructor() {
            try {
                Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
                Constructor<?> declaredConstructorBook = classBook.getDeclaredConstructor(String.class, String.class);
                // 暴力反射
                declaredConstructorBook.setAccessible(true);
                Object objectBook = declaredConstructorBook.newInstance("Java高级特性-反射-反射私有的构造方法", "JourWon");
                Book book = (Book) objectBook;
                log.info(book.toString());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        // 反射私有属性
        @Test
        public void reflectPrivateField() {
            try {
                Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
                Object objectBook = classBook.newInstance();
                Field fieldTag = classBook.getDeclaredField("TAG");
                fieldTag.setAccessible(true);
                String tag = (String) fieldTag.get(objectBook);
                log.info(tag);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
    
        // 反射私有方法
        @Test
        public void reflectPrivateMethod() {
            try {
                Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
                Method methodBook = classBook.getDeclaredMethod("declaredMethod", int.class);
                methodBook.setAccessible(true);
                Object objectBook = classBook.newInstance();
                String string = (String) methodBook.invoke(objectBook, 0);
    
                ReflectClass.
    
                log.info(string);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
    
    }
    

    输出结果

    [main] INFO com.jourwon.reflect.ReflectClass - Book{name='Java高级特性-反射-创建对象', author='JourWon'}
    
    [main] INFO com.jourwon.reflect.ReflectClass - Book{name='Java高级特性-反射-反射私有的构造方法', author='JourWon'}
    
    [main] INFO com.jourwon.reflect.ReflectClass - BookTag
    
    [main] INFO com.jourwon.reflect.ReflectClass - I am declaredMethod 0 ! 
    

    通过反射获取私有属性,方法和构造方法时,需要进行暴力反射,设置setAccessible(true)。否则会报错说无法获取私有属性,方法和构造方法

    总结

    本文列举了反射机制使用过程中常用的、重要的一些类及其方法,更多信息和用法,反射原理和源码分析需要近一步的阅读反射相关资料。

    在阅读Class类文档时发现一个特点,以通过反射获得Method对象为例,一般会提供四种方法,getMethod(parameterTypes)、getMethods()、getDeclaredMethod(parameterTypes)和getDeclaredMethods()。getMethod(parameterTypes)用来获取某个公有的方法的对象,getMethods()获得该类所有公有的方法,getDeclaredMethod(parameterTypes)获得该类某个方法,getDeclaredMethods()获得该类所有方法。带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法。其他的Annotation、Field、Constructor也是如此。

    展开全文
  • Java 反射

    千次阅读 2016-01-04 07:39:46
    Java 反射标签: Java基础动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如众所周知的ECMAScript(JavaScript)便是一个动态语言。除此之外如...

    Java 反射

    标签 : Java基础


    动态语言

    动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如众所周知的ECMAScript(JavaScript)便是一个动态语言。除此之外如Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。(引自: 百度百科)

    var execString = "alert(Math.floor(Math.random()*10));";
    eval(execString);

    Class反射机制

    • 指的是可以于运行时加载,探知和使用编译期间完全未知的类.
    • 程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能调用他的任意一个方法和属性;
    • 加载完类之后, 在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象), 这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射.

    Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions(维度). The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.

    • 每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类.


    Class对象的获取

    • 对象的getClass()方法;
    • 类的.class(最安全/性能最好)属性;
    • 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用).


    从Class中获取信息

    Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,因此我们只是做简单的介绍,详细请参考JDK文档

    • 获取类内信息
    获取内容 方法签名
    构造器 Constructor<T> getConstructor(Class<?>... parameterTypes)
    包含的方法 Method getMethod(String name, Class<?>... parameterTypes)
    包含的属性 Field getField(String name)
    包含的Annotation <A extends Annotation> A getAnnotation(Class<A> annotationClass)
    内部类 Class<?>[] getDeclaredClasses()
    外部类 Class<?> getDeclaringClass()
    所实现的接口 Class<?>[] getInterfaces()
    修饰符 int getModifiers()
    所在包 Package getPackage()
    类名 String getName()
    简称 String getSimpleName()
    • 一些判断类本身信息的方法
    判断内容 方法签名
    注解类型? boolean isAnnotation()
    使用了该Annotation修饰? boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
    匿名类? boolean isAnonymousClass()
    数组? boolean isArray()
    枚举? boolean isEnum()
    原始类型? boolean isPrimitive()
    接口? boolean isInterface()
    obj是否是该Class的实例 boolean isInstance(Object obj)
    • 使用反射生成并操作对象:

      Method Constructor Field这些类都实现了java.lang.reflect.Member接口,程序可以通过Method对象来执行相应的方法,通过Constructor对象来调用对应的构造器创建实例,通过Filed对象直接访问和修改对象的成员变量值.


    创建对象

    通过反射来生成对象的方式有两种:
    • 使用Class对象的newInstance()方法来创建该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器).
    • 先使用Class对象获取指定的Constructor对象, 再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例).

    通过第一种方式来创建对象比较常见, 像Spring这种框架都需要根据配置文件(如applicationContext.xml)信息来创建Java对象,从配置文件中读取的只是某个类的全限定名字符串,程序需要根据该字符串来创建对应的实例,就必须使用默认的构造器来反射对象.
    下面我们就模拟Spring实现一个简单的对象池, 该对象池会根据文件读取key-value对, 然后创建这些对象, 并放入Map中.

    • 配置文件
    {
      "objects": [
        {
          "id": "id1",
          "class": "com.fq.domain.User"
        },
        {
          "id": "id2",
          "class": "com.fq.domain.Bean"
        }
      ]
    }
    • ObjectPool
    /**
     * Created by jifang on 15/12/31.
     */
    public class ObjectPool {
    
        private Map<String, Object> pool;
    
        private ObjectPool(Map<String, Object> pool) {
            this.pool = pool;
        }
    
        private static Object getInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            return Class.forName(className).newInstance();
        }
    
        private static JSONArray getObjects(String config) throws IOException {
            Reader reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream(config));
            return JSONObject.parseObject(CharStreams.toString(reader)).getJSONArray("objects");
        }
    
        // 根据指定的JSON配置文件来初始化对象池
        public static ObjectPool init(String config) {
            try {
                JSONArray objects = getObjects(config);
                ObjectPool pool = new ObjectPool(new HashMap<String, Object>());
                if (objects != null && objects.size() != 0) {
                    for (int i = 0; i < objects.size(); ++i) {
                        JSONObject object = objects.getJSONObject(i);
                        if (object == null || object.size() == 0) {
                            continue;
                        }
                        String id = object.getString("id");
                        String className = object.getString("class");
    
                        pool.putObject(id, getInstance(className));
                    }
                }
                return pool;
            } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    
        public Object getObject(String id) {
            return pool.get(id);
        }
    
        public void putObject(String id, Object object) {
            pool.put(id, object);
        }
    
        public void clear() {
            pool.clear();
        }
    }
    • Client
    public class Client {
    
        @Test
        public void client() {
            ObjectPool pool = ObjectPool.init("config.json");
            User user = (User) pool.getObject("id1");
            System.out.println(user);
    
            Bean bean = (Bean) pool.getObject("id2");
            System.out.println(bean);
        }
    }
    • User
    public class User {
    
        private int id;
        private String name;
        private String password;
    
        public int getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
    • Bean
    public class Bean {
        private Boolean usefull;
        private BigDecimal rate;
        private String name;
    
        public Boolean getUsefull() {
            return usefull;
        }
    
        public void setUsefull(Boolean usefull) {
            this.usefull = usefull;
        }
    
        public BigDecimal getRate() {
            return rate;
        }
    
        public void setRate(BigDecimal rate) {
            this.rate = rate;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Bean{" +
                    "usefull=" + usefull +
                    ", rate=" + rate +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    注意: 需要在pom.xml中添加如下依赖:

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.7</version>
    </dependency>
    
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.0</version>
    </dependency>

    调用方法

    当获取到某个类对应的Class对象之后, 就可以通过该Class对象的getMethod来获取一个Method数组或Method对象.每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法.

    @CallerSensitive
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        ...
    }

    下面我们对上面的对象池加强:可以看到Client获取到的对象的成员变量全都是默认值,既然我们已经使用了JSON这么优秀的工具,我们又学习了动态调用对象的方法,那么我们就通过配置文件来给对象设置值(在对象创建时), 新的配置文件形式如下:

    {
      "objects": [
        {
          "id": "id1",
          "class": "com.fq.domain.User",
          "fields": [
            {
              "name": "id",
              "value": 101
            },
            {
              "name": "name",
              "value": "feiqing"
            },
            {
              "name": "password",
              "value": "ICy5YqxZB1uWSwcVLSNLcA=="
            }
          ]
        },
        {
          "id": "id2",
          "class": "com.fq.domain.Bean",
          "fields": [
            {
              "name": "usefull",
              "value": true
            },
            {
              "name": "rate",
              "value": 3.14
            },
            {
              "name": "name",
              "value": "bean-name"
            }
          ]
        },
        {
          "id": "id3",
          "class": "com.fq.domain.ComplexBean",
          "fields": [
            {
              "name": "name",
              "value": "complex-bean-name"
            },
            {
              "name": "refBean",
              "ref": "id2"
            }
          ]
        }
      ]
    }

    其中fields代表该Bean所包含的属性, name为属性名称, value为属性值(属性类型为JSON支持的类型), ref代表引用一个对象(也就是属性类型为Object,但是一定要引用一个已经存在了的对象)

    /**
     * @author jifang
     * @since 15/12/31下午4:00
     */
    public class ObjectPool {
    
        private Map<String, Object> pool;
    
        private ObjectPool(Map<String, Object> pool) {
            this.pool = pool;
        }
    
        private static JSONArray getObjects(String config) throws IOException {
            Reader reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream(config));
            return JSONObject.parseObject(CharStreams.toString(reader)).getJSONArray("objects");
        }
    
        private static Object getInstance(String className, JSONArray fields)
                throws ClassNotFoundException, NoSuchMethodException,
                IllegalAccessException, InstantiationException, InvocationTargetException {
    
            // 配置的Class
            Class<?> clazz = Class.forName(className);
            // 目标Class的实例对象
            Object targetObject = clazz.newInstance();
            if (fields != null && fields.size() != 0) {
                for (int i = 0; i < fields.size(); ++i) {
                    JSONObject field = fields.getJSONObject(i);
                    // 需要设置的成员变量名
                    String fieldName = field.getString("name");
    
                    // 需要设置的成员变量的值
                    Object fieldValue;
                    if (field.containsKey("value")) {
                        fieldValue = field.get("value");
                    } else if (field.containsKey("ref")) {
                        String refBeanId = field.getString("ref");
                        fieldValue = OBJECTPOOL.getObject(refBeanId);
                    } else {
                        throw new RuntimeException("neither value nor ref");
                    }
    
                    String setterName = "set" +
                            fieldName.substring(0, 1).toUpperCase() +
                            fieldName.substring(1);
                    // 需要设置的成员变量的setter方法
                    Method setterMethod = clazz.getMethod(setterName, fieldValue.getClass());
                    // 调用setter方法将值设置进去
                    setterMethod.invoke(targetObject, fieldValue);
                }
            }
    
            return targetObject;
        }
    
        private static ObjectPool OBJECTPOOL;
    
        // 创建一个对象池的实例(保证是多线程安全的)
        private static void initSingletonPool() {
            if (OBJECTPOOL == null) {
                synchronized (ObjectPool.class) {
                    if (OBJECTPOOL == null) {
                        OBJECTPOOL = new ObjectPool(new ConcurrentHashMap<String, Object>());
                    }
                }
            }
        }
    
        // 根据指定的JSON配置文件来初始化对象池
        public static ObjectPool init(String config) {
            // 初始化pool
            initSingletonPool();
    
            try {
                JSONArray objects = getObjects(config);
                for (int i = 0; objects != null && i < objects.size(); ++i) {
                    JSONObject object = objects.getJSONObject(i);
                    if (object == null || object.size() == 0) {
                        continue;
                    }
                    String id = object.getString("id");
                    String className = object.getString("class");
    
                    // 初始化bean并放入池中
                    OBJECTPOOL.putObject(id, getInstance(className, object.getJSONArray("fields")));
                }
                return OBJECTPOOL;
            } catch (IOException | ClassNotFoundException |
                    InstantiationException | IllegalAccessException |
                    NoSuchMethodException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
    
        public Object getObject(String id) {
            return pool.get(id);
        }
    
        public void putObject(String id, Object object) {
            pool.put(id, object);
        }
    
        public void clear() {
            pool.clear();
        }
    }
    • Client
    public class Client {
    
        @Test
        public void client() {
            ObjectPool pool = ObjectPool.init("config.json");
            User user = (User) pool.getObject("id1");
            System.out.println(user);
    
            Bean bean = (Bean) pool.getObject("id2");
            System.out.println(bean);
    
            ComplexBean complexBean = (ComplexBean) pool.getObject("id3");
            System.out.println(complexBean);
        }
    }
    • ComplexBean
    public class ComplexBean {
    
        private String name;
    
        private Bean refBean;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Bean getRefBean() {
            return refBean;
        }
    
        public void setRefBean(Bean refBean) {
            this.refBean = refBean;
        }
    
        @Override
        public String toString() {
            return "ComplexBean{" +
                    "name='" + name + '\'' +
                    ", refBean=" + refBean +
                    '}';
        }
    }
    

    Spring框架就是通过这种方式将成员变量值以及依赖对象等都放在配置文件中进行管理的,从而实现了较好地解耦(不过Spring是通过XML作为配置文件).


    访问成员变量

    通过Class对象的的getField()方法可以获取该类所包含的全部或指定的成员变量Field,Filed提供了如下两组方法来读取和设置成员变量值.

    • getXxx(Object obj): 获取obj对象的该成员变量的值, 此处的Xxx对应8中基本类型,如果该成员变量的类型是引用类型, 则取消get后面的Xxx;
    • setXxx(Object obj, Xxx val): 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型, 如果该成员类型是引用类型, 则取消set后面的Xxx;

    注: getDeclaredXxx方法可以获取所有的成员变量,无论private/public;

    /**
     * @author jifang
     * @since 16/1/2下午1:00.
     */
    public class Client {
    
        @Test
        public void client() throws NoSuchFieldException, IllegalAccessException {
            User user = new User();
            Field idFiled = User.class.getDeclaredField("id");
            setAccessible(idFiled);
            idFiled.setInt(user, 46);
    
            Field nameFiled = User.class.getDeclaredField("name");
            setAccessible(nameFiled);
            nameFiled.set(user, "feiqing");
    
            Field passwordField = User.class.getDeclaredField("password");
            setAccessible(passwordField);
            passwordField.set(user, "ICy5YqxZB1uWSwcVLSNLcA==");
    
            System.out.println(user);
        }
    
        private void setAccessible(AccessibleObject object) {
            object.setAccessible(true);
        }
    }

    使用反射获取泛型信息

    为了通过反射操作泛型以迎合实际开发的需要, Java新增了java.lang.reflect.ParameterizedType java.lang.reflect.GenericArrayType java.lang.reflect.TypeVariable java.lang.reflect.WildcardType几种类型来代表不能归一到Class类型但是又和原始类型同样重要的类型.

    类型 含义
    ParameterizedType 一种参数化类型, 比如Collection<String>
    GenericArrayType 一种元素类型是参数化类型或者类型变量的数组类型
    TypeVariable 各种类型变量的公共接口
    WildcardType 一种通配符类型表达式, 如? ? extends Number ? super Integer

    其中, 我们可以使用ParameterizedType来获取泛型信息.

    public class Client {
    
        private Map<String, Object> objectMap;
    
        public void test(Map<String, User> map, String string) {
        }
    
        public Map<User, Bean> test() {
            return null;
        }
    
        /**
         * 测试属性类型
         *
         * @throws NoSuchFieldException
         */
        @Test
        public void testFieldType() throws NoSuchFieldException {
            Field field = Client.class.getDeclaredField("objectMap");
            Type gType = field.getGenericType();
            // 打印type与generic type的区别
            System.out.println(field.getType());
            System.out.println(gType);
            System.out.println("**************");
            if (gType instanceof ParameterizedType) {
                ParameterizedType pType = (ParameterizedType) gType;
                Type[] types = pType.getActualTypeArguments();
                for (Type type : types) {
                    System.out.println(type.toString());
                }
            }
        }
    
        /**
         * 测试参数类型
         *
         * @throws NoSuchMethodException
         */
        @Test
        public void testParamType() throws NoSuchMethodException {
            Method testMethod = Client.class.getMethod("test", Map.class, String.class);
            Type[] parameterTypes = testMethod.getGenericParameterTypes();
            for (Type type : parameterTypes) {
                System.out.println("type -> " + type);
                if (type instanceof ParameterizedType) {
                    Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
                    for (Type actualType : actualTypes) {
                        System.out.println("\tactual type -> " + actualType);
                    }
                }
            }
        }
    
        /**
         * 测试返回值类型
         *
         * @throws NoSuchMethodException
         */
        @Test
        public void testReturnType() throws NoSuchMethodException {
            Method testMethod = Client.class.getMethod("test");
            Type returnType = testMethod.getGenericReturnType();
            System.out.println("return type -> " + returnType);
    
            if (returnType instanceof ParameterizedType) {
                Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments();
                for (Type actualType : actualTypes) {
                    System.out.println("\tactual type -> " + actualType);
                }
            }
        }
    }

    使用反射获取注解

    使用反射获取注解信息的相关介绍, 请参看我的博客Java注解实践


    反射性能测试

    Method/Constructor/Field/Element都继承了AccessibleObject,AccessibleObject类中有一个setAccessible方法:

    public void setAccessible(boolean flag) throws SecurityException {
        ...
    }

    该方法有两个作用:
    1. 启用/禁用访问安全检查开关:值为true,则指示反射的对象在使用时取消Java语言访问检查;值为false,则指示应该实施Java语言的访问检查;
    2. 可以禁止安全检查, 提高反射的运行效率.

    /**
     * @author jifang
     * @since 15/12/31下午4:53.
     */
    public class TestReflect {
    
        @Before
        public void testNoneReflect() {
            User user = new User();
    
            long start = System.currentTimeMillis();
            for (long i = 0; i < Integer.MAX_VALUE; ++i) {
                user.getName();
            }
            long count = System.currentTimeMillis() - start;
            System.out.println("没有反射, 共消耗 <" + count + "> 毫秒");
        }
    
        @Test
        public void testNotAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            User user = new User();
            Method method = Class.forName("com.fq.domain.User").getMethod("getName");
    
            long start = System.currentTimeMillis();
            for (long i = 0; i < Integer.MAX_VALUE; ++i) {
                method.invoke(user, null);
            }
            long count = System.currentTimeMillis() - start;
            System.out.println("没有访问权限, 共消耗 <" + count + "> 毫秒");
        }
    
        @After
        public void testUseAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            User user = new User();
            Method method = Class.forName("com.fq.domain.User").getMethod("getName");
            method.setAccessible(true);
    
            long start = System.currentTimeMillis();
            for (long i = 0; i < Integer.MAX_VALUE; ++i) {
                method.invoke(user, null);
            }
            long count = System.currentTimeMillis() - start;
            System.out.println("有访问权限, 共消耗 <" + count + "> 毫秒");
        }
    }

    执行上面程序,在我的机器上可以看到使用反射会比直接调用慢3000毫秒,但是前提是该方法会执行20E+次(而且服务器的性能也肯定比我的机器要高),因此在我们的实际开发中,其实是不用担心反射机制带来的性能消耗的,而且禁用访问权限检查,也会有性能的提升.

    附-机器配置信息


    扩展阅读:
    为什么动态类型编程语言会如此流行?
    动态语言和静态语言的比较
    反射是否真的会让你的程序性能降低?
    展开全文
  • java 反射

    千次阅读 2016-07-28 16:50:30
    不管咋样,学java的,了解java反射非常有必要!什么是反射反射定义java官方文档给出了解释: Reflection is the ability of a program to query and modify its state during the execution. 意为:反射是在程序运行...
  • JAVA反射

    万次阅读 2018-04-14 16:45:16
    一.基础用法package cn.dicson; import java.lang.reflect.Method; public class MyReflect { public static void main(String[] args){ try { Class clazz = Class.forName("cn.dicson.NewOb...
  • java反射

    千次阅读 2018-01-23 18:02:05
    java反射 java反射以及复制一个bean的值到另一个bean中。 深入理解Java反射 https://www.cnblogs.com/luoxn28/p/5686794.html java 使用BeanInfo实现bean实体与map之间的互相转换 ...

空空如也

1 2 3 4 5 ... 20
收藏数 68,685
精华内容 27,474
关键字:

java反射

java 订阅