zoukankan      html  css  js  c++  java
  • 反射的基本使用以及原理(Class获取方式)

    1、什么是反射技术?

      动态获取指定类以及类中的内容(成员),并运行其内容。

      应用程序已经运行,无法在其中进行new对象的建立,就无法使用对象。这时可以根据配置文件的类全名去找对应的字节码文件,并加载进内存,并创建该类对象实例。这就需要使用反射技术完成。反射技术最重要的就是Class字节码对象。其次有Constructor、Method、Field等类。

      其实,反射机制的非常重要的一个类就是Class字节码对象,获取方式有三种:

    class Parent {
        protected String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    class Children extends Parent {
        private static final Integer age = new Integer(25);
    
        public Children(String name) {
            this.name = name;
        }
    
        public static Integer getAge() {
            return age;
        }
    }

    测试获取Class对象的三种方式:

        public static void testClazz() throws ClassNotFoundException {
            Children children = new Children("zhangsan");
            Class<? extends Children> class1 = children.getClass();// 获取class对象的第一种方式
            System.out.println(class1);
            Class class2 = Children.class;// 获取class对象的第二种方式
            System.out.println(class2);
            Class class3 = Class.forName("cn.xm.exam.test.Children");// 获取class对象的第三种方式(要写全路径)
            System.out.println(class3);
        }

    结果:

    class cn.xm.exam.test.Children
    class cn.xm.exam.test.Children
    class cn.xm.exam.test.Children

    总结:获取Class对象的三种方式:

      Class.forName("类的路径")

      类名.Class

      实例.getClass()

    2. Class字节码对象的作用一: newInstance和获取Constructor对象

    修改Children类:

    class Children extends Parent {
        private static final Integer age = new Integer(25);
    
        public Children() {
            System.out.println("无参构造方法");
        }
    
        private Children(String name) {
            System.out.println("有参构造方法");
            this.name = name;
        }
    
        public static Integer getAge() {
            return age;
        }
    }

    2.1  clazz.newInstance();// 其内部是调用无参的Constructor对象进行创建对象

            Class clazz = Children.class;// 获取class对象的第二种方式
            Object newInstance = clazz.newInstance();// 其内部是调用无参构造方法进行创建
            System.out.println(newInstance);

    结果:

    无参构造方法
    cn.xm.exam.test.Children@4614ac54

    如果没有无参构造方法或者无参构造方法的修饰符是private,调用此方法会报错java.lang.IllegalAccessException

    2.2 获取Constructor对象

        public static void test2() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
                NoSuchMethodException, SecurityException {
            // 1.获取所有的构造
            Class clazz = Children.class;// 获取class对象的第二种方式
            Constructor[] constructors = clazz.getConstructors();// 获取所有public声明的构造方法
            blConstructor(constructors);
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();// 获取所有publicprivateprotecteddefault声明的构造方法
            blConstructor(declaredConstructors);
    
            // 2.根据参数类型获取构造方法
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            blConstructor(new Constructor[] { constructor });
            Constructor declaredConstructor = clazz.getConstructor(null);// 获取无参构造方法,修饰符为只能为public
            blConstructor(new Constructor[] { declaredConstructor });
        }
    
        public static void blConstructor(Constructor[] constructors) {
            System.out.println("=============");
            for (Constructor constructor : constructors) {
                System.out.println("name ->" + constructor.getName());
                Class[] parameterTypes = constructor.getParameterTypes();// 获取构造方法的参数类型数组
                for (Class clazz11 : parameterTypes) {
                    System.out.println("clazz11->" + clazz11);
                }
                System.out.println("isAccessible ->" + constructor.isAccessible());// isAccessible返回的是是否是private声明的
            }
        }

    结果:

    =============
    name ->cn.xm.exam.test.Children
    isAccessible ->false
    =============
    name ->cn.xm.exam.test.Children
    clazz11->class java.lang.String
    isAccessible ->false
    name ->cn.xm.exam.test.Children
    isAccessible ->false
    =============
    name ->cn.xm.exam.test.Children
    clazz11->class java.lang.String
    isAccessible ->false
    =============
    name ->cn.xm.exam.test.Children
    isAccessible ->false

    总结:    getConstructors()返回所有声明为public的构造方法

        getDeclaredConstructors()获取所有的方法,不管修饰符

        getDeclaredConstructor(String.class);//获取单参数,且类型为String的构造方法,修饰符可以为publicprivateprotecteddefault

        getConstructor(null);// 获取无参构造方法,修饰符为只能为public

          带Declared的方法可以获取到private修饰的构造方法,isAccessible方法返回的是此对象的可访问标志的值(默认返回的是false)。

    2.3 Constructor对象创建实例

      Constructor可以创建对象,也可以获取构造方法的参数类型等。

        public static void test2() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
                NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
            Class clazz = Children.class;// 获取class对象的第二种方式
            // 2.根据参数类型获取构造方法
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            Constructor declaredConstructor = clazz.getConstructor(null);// 获取无参构造方法,修饰符为只能为public
    
            System.out.println("=====================");
            Object newInstance1 = declaredConstructor.newInstance();
    
            System.out.println("=====================");
            Object newInstance = constructor.newInstance("111");
        }

    结果:(由于带参数的构造是private修饰的,所以不能直接new)

    =====================
    无参构造方法
    =====================
    Exception in thread "main" java.lang.IllegalAccessException: Class cn.xm.exam.test.Test can not access a member of class cn.xm.exam.test.Children with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:110)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:262)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:254)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:517)
    at cn.xm.exam.test.Test.test2(Test.java:31)
    at cn.xm.exam.test.Test.main(Test.java:8)

      

    解决办法:(调用创建对象之前设置可见性为true)

        public static void test2() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
                NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
            Class clazz = Children.class;// 获取class对象的第二种方式
            // 2.根据参数类型获取构造方法
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            Constructor declaredConstructor = clazz.getConstructor(null);// 获取无参构造方法,修饰符为只能为public
    
            System.out.println("=====================");
            Object newInstance1 = declaredConstructor.newInstance();
    
            System.out.println("=====================");
            constructor.setAccessible(true);// 暴力可见
            Object newInstance = constructor.newInstance("111");
        }

    结果:

    =====================
    无参构造方法
    =====================
    有参构造方法

    3. Method对象

    修改Children类:

    class Children extends Parent {
        private static final Integer age = new Integer(25);
    
        public Children() {
            System.out.println("无参构造方法");
        }
    
        private Children(String name) {
            System.out.println("有参构造方法");
            this.name = name;
        }
    
        public static Integer getAge() {
            return age;
        }
    
        private void method1() {
            System.out.println("method1......");
        }
    
        private void method1(String arg) {
            System.out.println("method1......" + arg);
        }
    
        public void method2() {
            System.out.println("method1......");
        }
    }

    3.1 Method对象的获取方式

      此对象也是有四种获取方式,带Declared的可以获取任意修饰符的。

            Class clazz = Children.class;// 获取class对象的第二种方式
            // 2.根据参数类型获取构造方法
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            constructor.setAccessible(true);// 暴力可见
            Object newInstance = constructor.newInstance("111");
            Method[] methods = clazz.getMethods();
            Method[] declaredMethods = clazz.getDeclaredMethods();
            Method method = clazz.getMethod("method2", null);
            Method declaredMethod = clazz.getDeclaredMethod("method1", String.class);

    3.1 Method对象的作用

      Method对象可以获取方法的名字、返回类型、参数类型、异常类型、注解等信息,另外可以通过此实例直接调用方法。调用静态方法传入第一个参数传入null即可。

        public static void test2() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
                NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
            Class clazz = Children.class;// 获取class对象的第二种方式
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            constructor.setAccessible(true);// 暴力可见
            Object newInstance = constructor.newInstance("111");
            // 2.根据参数类型获取方法
            Method method = clazz.getMethod("getAge", null);
            Method declaredMethod = clazz.getDeclaredMethod("method1", String.class);
            // 2.1获取方法名字、返回类型、参数类型
            System.out.println(
                    method.getName() + "	" + method.getReturnType() + "	" + Arrays.toString(method.getParameterTypes()));
            // 2.2 .2执行private修饰的方法(第一个参数是对象,第二个是参数)
            declaredMethod.setAccessible(true);// 设置暴力可见
            declaredMethod.invoke(newInstance, "111");
            // 2.2 .2静态方法的执行
            System.out.println(method.invoke(null));
        }

    结果:

    有参构造方法
    getAge class java.lang.Integer []
    method1......111
    25

    4. Field对象

      Field相当于成员属性、字段。

    修改Children类:

    class Children extends Parent {
        private static final Integer age = new Integer(25);
        private String sex;
        public int score;
    
        public Children() {
            System.out.println("无参构造方法");
        }
    
        private Children(String name) {
            System.out.println("有参构造方法");
            this.name = name;
        }
    
        public static Integer getAge() {
            return age;
        }
    }

    4.1 Field的获取方式

            Class clazz = Children.class;// 获取class对象的第二种方式
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            constructor.setAccessible(true);// 暴力可见
            Object newInstance = constructor.newInstance("111");
            // 获取Field对象
            Field[] fields = clazz.getFields();
            System.out.println(Arrays.toString(fields));
            Field[] declaredFields = clazz.getDeclaredFields();
            System.out.println(Arrays.toString(declaredFields));
            Field field = clazz.getField("score");
            System.out.println(field);
            Field declaredField = clazz.getDeclaredField("age");
            System.out.println(declaredField);

    结果:

    有参构造方法
    [public int cn.xm.exam.test.Children.score]
    [private static final java.lang.Integer cn.xm.exam.test.Children.age, private java.lang.String cn.xm.exam.test.Children.sex, public int cn.xm.exam.test.Children.score]
    public int cn.xm.exam.test.Children.score
    private static final java.lang.Integer cn.xm.exam.test.Children.age

    4.2 Field的作用

      Field实例可以获取字段的类型、值,也可以修改字段的值。

            Class clazz = Children.class;// 获取class对象的第二种方式
            Constructor constructor = clazz.getDeclaredConstructor(String.class);// 获取参数类型为string修饰符为private的构造方法(因为是private修饰,所以只能用此方法获取)
            constructor.setAccessible(true);// 暴力可见
            Object newInstance = constructor.newInstance("111");
            // 获取Field对象
            Field field = clazz.getField("score");
            System.out.println(field.getType() + "	" + field.get(newInstance));
            // 修改成员属性的值
            field.set(newInstance, 80);
            System.out.println(field.getType() + "	" + field.get(newInstance));
            // 获取静态成员属性
            Field declaredField = clazz.getDeclaredField("age");
            declaredField.setAccessible(true);// 暴力可见
            System.out.println(declaredField.getType() + "	" + declaredField.get(null));

    结果:

    有参构造方法
    int 0
    int 80
    class java.lang.Integer 25

    field提供了获取字段基本类型的值与引用类型的值get方法,也提供了修改基本类型与引用类型值的set方法:(第一个参数是实例对象,如果是静态属性传入null就可以)

          

    总结:

    1.三种方法可以获取到Class类:

      Class.forName("类的路径")

      类名.Class

      实例.getClass()

    2.五种创建对象的方法:

      通过new语句实例化一个对象

      通过反射机制创建对象,class.newInstance()

      通过反射机制创建对象,constructor.newInstance()

      通过clone()方法创建一个对象

      通过反序列化方式创建对象

    3.获取构造方法对象Constructor、方法对象Method、字段对象Field都有四种方法。(两个返回数组、两个返回单个实例)

      直接调用getXXX方法例如  getConstructors 返回的是public修饰的方法,而且包括从父类继承的方法

      调用getDeclaredXXX方法获取的不管修饰符,但是不包括从父类继承的方法。

      也可以根据方法名称与类型或者字段名称来获取对应的单个实例。

  • 相关阅读:
    Zoundry Raven测试
    asp.net 异步加载?
    网页数据表格自动填充序号
    1.JSP
    C#基础之CLR的执行模型(二)
    C#基础之CLR的执行模型(一)
    java提供类与cglib包实现动态代理
    CSS初窥...
    Go的牛逼之处
    Go灵魂级选手之流程控制
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/7551550.html
Copyright © 2011-2022 走看看