Java之反射相关

1.反射介绍

反射(Reflection)是Java的高级特性之一,反射允许运行的程序获取当前已加载入JVM中的属性信息,并且可以操作部分属性变化,final标记的属性无法通过反射修改,除了修改属性,反射也可以动态创建对象,并可以反射调用其相应的方法

android开发中,反射经常会配合DexClassLoader一起使用,实现动态数据的更新,当我们要使用某个类或者某个方法,但并不确定该版本有没有相应的方法时,可以通过反射去进行校验,这个在DexClassLoader加载的dex文件中使用的比较多

2.反射的方法

Java中可以操作的反射基本有下面几种
1.Class class类对象,这是反射的基础,反射都是基于类操作的
2.Field 类的成员变量
3.Method 类中定义的所有方法
4.Constructor 类的构造方法,通常用于反射实例化一个类对象使用

2.1 Class操作

class操作主要是用来获取相对应的class类型,以及后续的判定,也是反射的基础,没有类也就没法反射,主要有以下几种方式
1.Class.forName(String name) 传入类对应的全路径,需要捕获异常

Class<?> mClass =Class.forName("com.a.b.Test");

2.XXX.class 直接调用该类对应的.class属性

 Class<?> mClass = String.class

3.XXX.getClass() 调用该类实例的getClass方法获取,和2不同的是这个是通过实例获取,第2个是用的类获取

 Class<?> mClass = new String().getClass();

如果已经有一个类的实例,那么可以通过isInstance方法判断是否是某个类的实例

boolean res = String.class.isInstance(new String("1234"));

2.2 Field操作

顾名思义,就是成员变量的反射操作,可以通过反射获取并修改类相应的属性(final标记的除外)
与Filed相关的方法有
1.getField(String name) 获取该类指定的名称的共有的成员属性
2.getFields() 获取该类所有的共有的成员属性
3.getDeclaredField(String name) 获取该类中指定的名称的定义的成员属性
4.getDeclaredFields() 获取该类中所有的定义的成员属性
5.set(Object obj, Object value) 设置该属性的值

getFileldgetDeclaredField的区别是前者只能获取类中所定义的public属性,这个可以包含父类的public; 而后者是获取当前类中定义的所有属性,包含publicprivate以及protected的,但不包含父类的,如果想获取父类的则需要先通过getSuperclass获取父类的类,然后再调用一次相应的方法

 public static class TestParent {
        public String strParent;
        protected long longParent;
        private int intParent;
    }

    public static class TestChild extends TestParent {
        public String strChild;
        protected long longChild;
        private int intChild;
    }

    public static void main(String[] args) {
        TestChild testChild = new TestChild();
        Field[] fields = testChild.getClass().getFields();
        Field[] decFields = testChild.getClass().getDeclaredFields();

        testChild.getClass().getSuperclass()
        for (Field field : fields) {
            System.out.println("fields name ->>> "+field.getName());
        }

        for (Field decField : decFields) {
            System.out.println("decFields name ->>> "+decField.getName());
        }
    }

我这里定义了一个父类和子类,都有独立的不同属性,打印结果是

fields name ->>> strChild
fields name ->>> strParent
decFields name ->>> strChild
decFields name ->>> longChild
decFields name ->>> intChild

可以看出getField是可以获取父类的,而getDeclaredFields是获取当前类所有的,不包含父类

获取到Field属性后,就可以通过set(Object obj, Object value)方法给相应的属性设置值,这里的第一个参数是需要设置的类的实例,如果是静态的变量这里传null就可以,第二个参数是我们要设置的属性值
比如上面我们可以用下面的方法修改值

public static void main(String[] args) {
        TestChild testChild = new TestChild();
        try {
            Field field = testChild.getClass().getDeclaredField("strChild");
            field.set(testChild,"1234");
            System.out.println(testChild.strChild);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这里就会输出1234,也就是我们成功修改了相应实例中的属性

2.3 Method操作

这个是对方法的反射操作,相关的方法有
1.getMethod(String name, Class<?>... parameterTypes) 获取该类中指定名称的共有方法,注意这里需要传入参数,因为有方法的重载,会有同名的方法但参数不同
2.getMethods() 获取该类中所有的共有方法
3.getDeclaredMethod(String name, Class<?>... parameterTypes) 获取该类中指定名称的定义的方法
4.getDeclaredMethods() 获取该类中所有的定义的方法
5.invoke(Object obj, Object... args) 调用相应的方法

 public static class TestParent {
        public void methodParent(String str, int value) {}
        private void methodPrivateParent(){}
    }
    public static class TestChild extends TestParent {
        private void methodChild(String str,int value){
              System.out.println(str+":"+value);
		}
        private void methodPrivateChild(){}
    }
    public static void main(String[] args) {
        TestChild testChild = new TestChild();
        Method[] methods = testChild.getClass().getMethods();
        Method[] decMethods = testChild.getClass().getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("method ->>> "+method.getName());
        }
        for (Method decMethod : decMethods) {
            System.out.println("decMethod ->>> "+decMethod.getName());
        }
    }

打印结果是

method ->>> methodParent
method ->>> wait
method ->>> wait
method ->>> wait
method ->>> equals
method ->>> toString
method ->>> hashCode
method ->>> getClass
method ->>> notify
method ->>> notifyAll
decMethod ->>> methodPrivateChild
decMethod ->>> methodChild

会发现,getMethods()会把超类Object中的共有方法也打印出来,getDeclaredMethods()同样只打印自己类中的方法

当获取的Method对象后,就可以通过invoke(Object obj, Object... args)方法调用该方法,这里的第一个参数是该类的实例,同样如果是静态方法这里传null,后面的就是对应的参数属性了,需要按顺序填入,比如上面的方法我们可以这么反射

 public static void main(String[] args) {
        TestChild testChild = new TestChild();
        try {
            Method method = testChild.getClass().getDeclaredMethod("methodChild",String.class,int.class);
            method.setAccessible(true);
            method.invoke(testChild,"1234",666);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

注意这里的setAccessible方法,当反射的属性或方法是私有的时候,需要调用这个方法去设置访问权限

2.4 Constructor操作

这是对构造方法的反射操作,也是我们反射创建实例最常用的方式
1.getConstructor(Class<?>... parameterTypes) 获取该类指定参数类型的共有的构造方法
2.getConstructors() 获取该类所有的共有的构造方法
3.getDeclaredConstructor(Class<?>... parameterTypes) 获该类指定参数的定义的构造方法
4.getDeclaredConstructors() 获取该类所有的定义的构造方法
5.newInstance(Object ... initargs) 通过构造方法创建类的实例

构造方法和上面方法的操作类型,比如我们通过上面的构造方法创建实例再反射调用相应的方法,可以写成

  public static void main(String[] args) {
        try {
            Constructor<?> constructor = TestChild.class.getConstructor();
            TestChild testChild = (TestChild) constructor.newInstance();
            Method method = testChild.getClass().getDeclaredMethod("methodChild",String.class,int.class);
            method.setAccessible(true);
            method.invoke(testChild,"1234",666);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.反射的注意事项

  • 反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射
  • 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值