Java反射初级

前言:

反射作为一个高级技术,应该是必须掌握的,之前虽然一直都懂,也会用,但是真正做需求的时候总感觉容易忘记,而且很多细节和一些实用的用法不太懂,看了别人的博客,也写的不太详细,现在打算从初级到高级,写的尽量详细和易懂,也方便之后自己翻看.

 

注解类关键字:@interface 

注解的作用:一句话理解,注解就是给对象添加一个标记,不用对象自己去进行属性设置,利用注解对其进行赋值.

 每个注解,都需要添加该关键字才能作为一个注解.

元注解:

 @Target:java的原始注解,用于声明注解类型

ElementType:枚举类,其中包含了多种注解声明的字段:
TYPE,类,接口(包括注释类型)或枚举声明
FIELD,成员属性声明
METHOD,方法声明
PARAMETER,正式参数声明
CONSTRUCTOR,构造函数声明
LOCAL_VARIABLE,局部变量声明
ANNOTATION_TYPE,注解类型声明
PACKAGE,包装声明 
TYPE_PARAMETER,键入参数声明 
TYPE_USE;使用类型

 @Retention :注解策略,说明注解的保留时间,分3种策略

  RetentionPolicy.RUNTIME:注释将由编译器记录在类文件中,并由VM在运行时保留,因此可以反射读取,运行时调用。

  RetentionPolicy.CLASS:注释将由编译器记录在类文件中,但VM不需要在运行时保留,即在编写代码时执行

  RetentionPolicy.SOURCE:注释将被编译器丢弃。(这种情况没用过,具体如何定义后面再给出结论)

 

上面是最基本的介绍,去看看文档,大家应该都懂。其中常用的是方法,类,成员属性加上运行时注解。另外一些注解声明用到的较少,我自己也没怎么用过,有兴趣的可以自己写个demo去研究下。

 

反射

   关于反射的官方解释:

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

   我自己的理解:

      反射是用来修改和调用对象的方法的一种方式,简单点,从实用角度来说,就是让你对于某些API中无法修改和调用的对象,通过反射的方式去使用.

 接下来介绍下反射的简单用法:

  

        View view = new View();
        //反射修改属性
        view.show = 10;
        Field member = view.getClass().getDeclaredField("show");
        member.setAccessible(true);//设置可以方法私有属性
        member.set(view, 78);
        System.out.println(view.show);

        //反射调用方法
        Method method = view.getClass().getDeclaredMethod("modify");
        method.setAccessible(true);//设置可以方法私有方法
        method.invoke(view, null);

   上面是最基本的用法了,不过有一个方法需要注意,setAccessible(),这个方法的作用是让你能够拿到私有方法和私有属性并进行修改,如果方式和属性是private的,你直接set或invoke,这个时候是会抛异常的,如果设置的是private final,则说明这个属性,即使通过反射也是无法修改的,因为设置了final,是不允许对对象进行修改的.不过方法的话设置了private final依然可以调  用.

    然后说一下Medhod的一些其他用法,比如一个方法带有参数,如上面的modify()方法是不带参数的,如果是下面这样,该如何获取呢?

     public void modify(String msg);

   这个时候如果用view.getClass().getDeclareMethod("modify")会报异常 java.lang.NoSuchMethodException。

   说明找不到modify这个方法。

   要获取modify方法,就应该这样写:view.getClass().getDeclareMethod("modify",String.class);

注意后面的String.class,与方法中的类型要一致,如果是int则是int.class.

那么问题又来了,如果有2个方法呢?打开getDeclareMethod()看看源码。

 public Method getDeclaredMethod(String var1, Class... var2)

 第一个参数var1是方法名称,第二个参数则是方法带有的参数,它是一个可变参数类型,支持多个参数定义,所以后面你可以随便添加参数,但是获取该方法的时候也需要添加对应的参数.

 

 然后举个例子,android中通过反射的方式修改光标的值,代码如下:

       Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
       f.setAccessible(true);
       f.set(et, R.drawable.editext_cursor);

注解简单使用

     1.获取属性注解

       2种方式:一是获取所有带注解的成员,然后通过遍历的形式获取到每个成员上的注解数据,然后保存下来进行使用.

                      二是通过成员变量的名称,如"age",获取指定的成员的注解。

      View view = new View();
        try {
            Field[] fields=view.getClass().getDeclaredFields();//获取所有声明的属性,通过遍历,获取所有的注解的标记数据
            Field field = view.getClass().getDeclaredField("show");//获取指定的属性
            if (field.isAnnotationPresent(UIMonitor.class)) {
                UIMonitor monitor = field.getAnnotation(UIMonitor.class);
                if (monitor != null) {
                    System.out.println("life:" + monitor.life());
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

2.获取方法的注解

     获取方式和上面获取成员一样也是2种.

      

     try {
            Method method = view.getClass().getDeclaredMethod("modify");//获取指定方法
            int modifiers = method.getModifiers();
            System.out.println("modifiers:" + modifiers);
            if (method.isAnnotationPresent(Subscriber.class)) {//如果方法上存在该注解
                Subscriber subscriber = method.getAnnotation(Subscriber.class);//获取方法上的注解
                if (subscriber != null) {
                    System.out.println("priority" + subscriber.priority());
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        Method[] methods = view.getClass().getDeclaredMethods();//拿到所有的方法

        for (Method md : methods) {
            int modifiers = md.getModifiers();
            if (md.isAnnotationPresent(Subscriber.class)) {
                if ((modifiers & Modifier.PUBLIC) != 0) {//获取修改类型
            //获取方法中的参数,如modify(int res,String msg)有2个参数,则length=2;
                    Class<?>[] parameterTypes = md.getParameterTypes();
                    Class<?> p1 = parameterTypes[0];//获取第一个参数的参数类型
                    System.out.println("parameterTypes length:" + parameterTypes.length);
                    for (Class para : parameterTypes) {
                        System.out.println("parameterTypes:" + para.getName());
                    }
                    if (parameterTypes.length == 1) {
                        if (md.isAnnotationPresent(Subscriber.class)) {
                            Subscriber subscriber = md.getAnnotation(Subscriber.class);
                            Class<?> eventType = parameterTypes[0];
                        }
                    }
                } else {
                    throw new NullPointerException("no method");
                }

            }
        }

 

3.异常

(1)java.lang.reflect.InvocationTargetException

     利用反射调用方法中存在异常时(如空指针,数组越界等)会抛出该异常

(2)java.lang.InstantiationException

  利用反射实例化对象clazz.newInstance(),如果被实例化的对象中存在一个有参的构造方法,但是没有无参的构造方法,会抛出该异常

4.差异

getMethod():获取当前类及所有继承的父类的public修饰的方法。仅包括public

getDeclaredMethod():获取当前类的所有方法,包括public/private/protected/default修饰的方法

getField()和getDeclaredField()区别同上

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值