美文网首页
java 反射进阶

java 反射进阶

作者: 写代码的向日葵 | 来源:发表于2019-07-26 13:00 被阅读0次

    1.java反射基础

    2.java反射进阶


    上一节介绍了反射基础,本节介绍更深入的反射概念。

    1.获取不到Class

    当Class.forName()中路径获取不到对应的Class时,会抛出异常。

    2. 获取不到Field

    1. 确实不存在这个Field
    2. 修饰符导致的权限问题
      以上两种情况会抛出NoSuchFieldException异常

    反射获取属性的修饰符

    方法 本Class SuperClass
    getField public public
    getDeclaredField public procted private no
    getFields public public
    getDeclaredFields public procted private no
      1. getField只能获取对象中public修饰符的属性,并且能获取父类Class的public属性。
      2. getDeclaredField能获取对象中各种修饰符的属性,但无法获取父类的任何属性。
    

    如何才能获取父类的属性呢?class对象会提供getSuperClass的方法来获取父类的对象,然后再通过父类调用
    getDeclaredField来获取其属性。

      Class c1=Class.forName("com.zhang.Reflect");
      Class superClass=c1.getSuperClass();
      Field field=superClass.getDeclaredField("name");
    

    3. 获取不到Method

    和Field的情况类似,当方法名或参数数目类型没对上时,就会出NoSuchMethodException异常。获取方法体如下表

    获取方法体

    方法 本Class SuperClass
    getMethod public public
    getDeclaredMethod public procted private no
    getMethods public public
    getDeclaredMethods public procted private no

    4. 获取不到Constructor

    方法 本Class SuperClass
    getConstructor public public
    getDeclaredConstructor public procted private no

    \color{red}{需要注意的是,构造方法无法调用父类的任何方法}
    反射创建一个对象,可以使用Class.newInstance()和Constructor.newInstance()两种方法,不同之处在在于Class.newInstance()的使用受到限制,对应的Class中必须存在一个无参数的构造方法,并且必须要有访问权限,而constructor.newInstance()适用任何类型的构造方法,无论是否有参数都可以调用,只需要使用setAccessible()控制访问验证即可。所以一般建议使用constructor.newInstance().

    5.反射静态方法

    调用静态方法直接用Class.method()的形式就可以调用。

    public  class TestMethod{
            static void test(){
                  System.out.println("test");
             }
    }
    
    public class Test{
               public static void main(String[] args) {
                try {
                        Class clz=Class.forName("TestMethod");
                        Method method=clz.getDeclaredMethod("test");
                        method.invoke(null);
                 }catch (ClassNotFoundException e) {
                      e.printStackTrace();
                  } catch (NoSuchMethodException e) {
                      e.printStackTrace();
                  } catch (IllegalAccessException e) {
                      e.printStackTrace();
                  } catch (IllegalArgumentException e) {
                      e.printStackTrace();
                  } catch (InvocationTargetException e) {
                      e.printStackTrace();
            }
        }
    }
    

    关键在于Method.invoke的第一个参数,static方法因为因为属于类本身,所以不需要填写对象,填写nuLL即可。

    6.反射泛型参数方法

    在说这个之前,我门需要看下invoke方法的代码

      @CallerSensitive
        public Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException,
               InvocationTargetException
        {
            if (!override) {
                if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                    Class<?> caller = Reflection.getCallerClass();
                    checkAccess(caller, clazz, obj, modifiers);
                }
            }
            MethodAccessor ma = methodAccessor;             // read volatile
            if (ma == null) {
                ma = acquireMethodAccessor();
            }
            return ma.invoke(obj, args);
        }
    

    第一个Object参数对应的Class对象实例,后面的参数是可变参数,可以接受多个参数。

    public class TestMethod<T> {
        public void test(T t){
            System.out.println(t);
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            try {
                Class clzT=TestMethod.class;
                Method method=clzT.getDeclaredMethod("test", Object.class);
                method.setAccessible(true);
                method.invoke(new TestMethod<Integer>(), 1);
            }catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    其中有一个泛型基础——类型擦除。
    当一个方法中有泛型参数时,编译器会自动类型向上转型,T向上转型是Object,所以实际上在TestMethod类中是test(Object t)。getDeclaredMethod需要用Object.class作为参数。

    相关文章

      网友评论

          本文标题:java 反射进阶

          本文链接:https://www.haomeiwen.com/subject/rhsrrctx.html