先来看一看今天的效果:
代码效果:
官方解释:
从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
百度百科
定义一个注解:
public @interface SZJ{
}
元注解:
元注解是用来标示当前注解是干什么用的,什么时候用,常用的有2个
@Target
@Retention
CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
SOURCE
RetentionPolicy.SOURCE 仅用于源码级别,会被编译器忽略[编译成class后,会被class丢弃该注解]
RetentionPolicy.CLASS 在编译器由编译器保留,JVM会忽略
这个注解一般是APT技术用到的比较多,像一些ARouter,EventBus,Butterknifer等等框架都是用的这个注解.
定义:
反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和
方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。
百度百科
使用场景:
不知道某个类是什么类,用来干什么的,就可以通过反射机制直接调用到他内部的属性,以及方法
反射与Class:
反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等,比如说有一个Person类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。
获得Class对象:
代码展示:
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());
}
}
效果图:
反射获取到注解:
定义注解:
//运行时存在
@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]他来举例!
完整代码
原创不易,您的点赞就是对我最大的支持!