先来看一看今天的效果:
代码效果:
效果不重要,重要的是代码:
注解
官方解释:
从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
百度百科
定义一个注解:
public @interface SZJ{
}
元注解:
元注解是用来标示当前注解是干什么用的,什么时候用,常用的有2个
@Target
- ElementType.ANNOTATION_TYPE 应用于注解类型。
- ElementType.CONSTRUCTOR 应用于构造函数。
- ElementType.FIELD 应用于字段或属性。
- ElementType.LOCAL_VARIABLE 应用于局部变量。
- ElementType.METHOD 应用于方法级注解。
- ElementType.PACKAGE 可以应用于包声明。
- ElementType.PARAMETER 可以应用于方法的参数。
- ElementType.TYPE 可以应用于类的任何元素。
@Retention
- RetentionPolicy.SOURCE 仅用于源码级别,会被编译器忽略[编译成class后,会被class丢弃该注解]
- RetentionPolicy.CLASS 在编译器由编译器保留,JVM会忽略
- RetentionPolicy.RUNTIME 由JVM保留,运行时可以使用
CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
SOURCE<CLASS<RUNTIME
举例:
@Retention注解解释:
RetentionPolicy.SOURCE 仅用于源码级别,会被编译器忽略[编译成class后,会被class丢弃该注解]
这个注解太偏源码,用到的地方比较少
RetentionPolicy.CLASS 在编译器由编译器保留,JVM会忽略
这个注解一般是APT技术用到的比较多,像一些ARouter,EventBus,Butterknifer等等框架都是用的这个注解.
反射
定义:
反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和
方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。
百度百科
使用场景:
不知道某个类是什么类,用来干什么的,就可以通过反射机制直接调用到他内部的属性,以及方法
反射与Class:
反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等,比如说有一个Person类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。
获得Class对象:
- 类名获取: 类名.class
- 对象名获取: 对象名.getClass()
- 全类名获取: Class.forName(“全类名”)
代码展示:
public void test() {
System.out.println("我是方法哦");
//类名获取
Class<?> intCls = int.class;
Class<?> strCls = String.class;
//对象名获取
UserBean userBean = new UserBean();
Class<? extends UserBean> userBeanCls = userBean.getClass();
//全类名获取
try {
Class<?> userBeanCls2 = Class.forName("com.example.annotation.bean.UserBean");
} catch (Exception e) {
e.printStackTrace();
}
}
反射的🌰:
public void test() {
System.out.println("我是方法哦");
//TODO 对象名获取 class
UserBean userBean = new UserBean();
Log.i("szjUserBean",userBean.toString());
try {
Class<? extends UserBean> userBeanCls = userBean.getClass();
Field nameField = userBeanCls.getDeclaredField("name");
//允许访问 private
nameField.setAccessible(true);
//修改状态
nameField.set(userBean,"SZJ");
Log.i("szjUserBean",userBean.toString());
//TODO 全类名获取 class
Class<?> userBeanCls2 = Class.forName("com.example.annotation.bean.UserBean");
Field nameField2 = userBeanCls2.getDeclaredField("name");
nameField2.setAccessible(true);
//修改状态
nameField2.set(userBean,"我是SZJ");
Log.i("szjUserBean",userBean.toString());
//TODO 反射调用方法
Method test = userBeanCls.getMethod("test",String.class,int.class);
String userTestRt = (String) test.invoke(userBean, "张三1", 22);
Log.i("szjUserBean",userTestRt);
Method test2 = userBeanCls.getDeclaredMethod("test2",String.class,int.class);
//允许访问private
test2.setAccessible(true);
String userTestRt2 = (String) test2.invoke(userBean, "张三2", 33);
Log.i("szjUserBean2",userTestRt2);
} catch (Exception e) {
e.printStackTrace();
Log.i("szjError", e.toString());
}
}
效果图:
- getDeclaredField(String) 获得自己的成员属性[不包括父类,包括private]
- getFields(String) 获得自己+父类的成员属性[不包括private]
- Field[] = getDeclaredFields() 获取到所有的属性
- getMethod(参数名,参数.class) 获得方法[不包括父类,包括private]
- getDeclaredMethod(参数名,参数.class) 获得自己+父类的成员方法[不包括private]
- Method[] = getDeclaredMethods() 获取所有的方法
- Field.setAccessible(boolean) 是否允许访问private属性
反射获取到注解:
- Field.isAnnotationPresent(Class) 属性上是否有该注解
- Field.getAnnotation(Class) 获取到属性上注解
- Annotation[] = Method.getAnnotations() 获取到方法上的所有注解
- Annotation.annotationType() 获取到方法上注解的注解类型
注解配合反射实现效果
定义注解:
//运行时存在
@Retention(RetentionPolicy.RUNTIME)
//作用来属性上
@Target(ElementType.FIELD)
public @interface szjFind {
//IdRes: 用来区分传入 int 类型 是否是id
@IdRes int value();
}
通过反射和注解给控件赋值:
//TODO findViewById
public void initFind(Activity activity) {
//获取 Class
Class<? extends Activity> cls = activity.getClass();
//getFields 获得自己+父类的成员 不包括 private
//getDeclaredFields 获得自己的成员(不包括父类,包括 private)
//获得此类所有的属性
Field[] fields = cls.getDeclaredFields();
//遍历这个类中的所有属性
for (Field field : fields) {
//判断属性是否被 szjFind 注解
if (field.isAnnotationPresent(szjFind.class)) {
//获得注解的实例
szjFind szjFind = field.getAnnotation(szjFind.class);
assert szjFind != null;
//获取 id
int id = szjFind.value();
//找到 Id
View view = activity.findViewById(id);
//允许操作 private 属性
field.setAccessible(true);
try {
//反射设置属性的值
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
在Activity中注册:
szjAnnotationUtil.getInstance().initFind(this);
代码总览图:
因为高度重复,其余代码请下载demo观看
注意:在实际开发项目中不建议采用这种方式,因为反射非常耗时,本篇只是以[获取控件ID/onClick/Intent]他来举例!
完整代码
原创不易,您的点赞就是对我最大的支持!