前言:
反射作为一个高级技术,应该是必须掌握的,之前虽然一直都懂,也会用,但是真正做需求的时候总感觉容易忘记,而且很多细节和一些实用的用法不太懂,看了别人的博客,也写的不太详细,现在打算从初级到高级,写的尽量详细和易懂,也方便之后自己翻看.
注解类关键字:@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()区别同上