元数据
元数据是指用来描述数据的数据(“data about data”),简单的说,就是描述代码间关系,代码本身,资源数据等的数据。一般是结构化数据(如存储在数据库里的数据,规定了字段的长度、类型等)。元数据是指从信息资源中抽取出来的用于说明其特征、内容的结构化的数据。
比如,关于一本书(信息资源),我们在图书馆系统中检索可以得到题名,版本、出版数据、相关说明,包括检索点等,这些就是元数据,用于描述资源的。
Java注解
JDK5.0引入了Annotation的概念来描述元数据,在java中,元数据是以标签的形式存在,元数据并不会影响程序代码的编译和执行。JDK5.0出来后,java语言中有四种类型,class(类)、enum(枚举)、interface(接口)和@interface(注解),他们在java中处于同一级别。元数据在java中,既是由注解来表示的。注解可以在编译、类加载和运行时被读取。
Java的注解本质上是一个接口,继承了接口Annotation的接口,这里的继承用的是extends。
注解既然和接口类似,就会包含成员:
- final静态属性,必须初始化
- 公共抽象方法,可设置默认值
@interface隐含继承Annotation,但是自定义注解不能直接继承Annotation。直接继承的是接口,非注解。
注解的作用
- 编写文档(通过代码里面标识的元数据编写,例如@param,@return)
- 代码分析(可以替代配置文件)
- 编译检查(通常配合lint使用,如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。)
Java中自带的注解
(基本)
- @Override(限定重写父类的方法)
- @SuppressWarning(用于抑制编译器产生警告信息)
- @Deprecated(表示"已过时/不建议使用/在后期的API中可能被删除")
- @SafeVarargs(java7新增,和堆污染有关,具体不是很了解)
(用于标识注解的注解) - @Retention(决定注解存活和读取时间,它包含一个RetationPolicy的value成员变量,用于指定它所修饰的注解的读取和存活时间)
一般的值有:- Retationpolicy.CLASS:在类加载的时候解读,执行的时候,jvm就会抛弃掉。
- Retationpolicy.ROURCE:存在于源码中,在编译的时候解读,之后就抛弃,不进入类加载和运行环节。
- Retationnpolicy.RUNTIME:运行时解读,可以在运行时通过反射获取注解,执行操作。
- @Target (指定修饰的元素,包含一个value值)
value可选:- ElementType.ANNOTATION_TYPE: 指定该Annotation只能修饰Annotation。
- ElementType.CONSTRUCTOR: 指定只能修饰构造器。
- ElementType.FIELD: 指定只能成员变量。
- ElementType.LOCAL_VARIABLE: 指定只能修饰局部变量。
- ElementType.METHOD: 指定只能修饰方法。
- ElementType.PACKAGE: 指定只能修饰包定义。
- ElementType.PARAMETER: 指定只能修饰参数。
- ElementType.TYPE: 指定可以修饰类,接口,枚举定义。
- @Document(可被javadoc提取成文档)
- @Inherited 被他修饰的Annotation具有继承性
自定义注解
最简单的例子,Android中代替findviewById()对组件进行初始化。只需要在成员变量组件前加上注解就行,能有效的减少代码量,可读性更强。
首先定义注解,运行时读取,作用于成员变量
写法和接口雷同
用法是@FindView(R.id.XXX)这里传入的Id就是这个value的返回值,可以在获取到这个注解实例后调用这个value() 方法获取值
可以不止一个方法,多一个方法,就可以多传一个参数,如果有默认值,也可以不传
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FindView{
/**
* id 注解,这里没有默认值,不过可以自由添加,
* 例如:public String tableName() default "className";
* @return
*/
int value();
}
从上面注解的定义来,类似于接口,所以注解本身不具备解析和其他操作的功能,这里我们想要的是利用这个注解携带的value,初始化这个注解所表示的成员变量,所以我们需要自己写一个工具类,通过传入的activity获取注解,并利用反射初始化成员变量。以下是详细代码:
public class ViewUtil {
public static void viewInject(Activity activity){
viewInjects(activity);
}
private static void viewInjects(Activity activity){
Class<? extends Activity> clazz = activity.getClass();
Field[] fields = clazz.getDeclaredFields();
for(Field field:fields){
FindView viewFind = field.getAnnotation(FindView.class);
if(viewFind!=null){
int viewId = viewFind.value();
View view = activity.findViewById(viewId);
try {
field.setAccessible(true);
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
最后是具体的使用方法
public class MainActivity extends AppCompatActivity {
@FindView(R.id.btn)
Button btn;
@FindView(R.id.btn1)
Button btn1;
@FindView(R.id.btn2)
Button btn2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtil.viewInject(this);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
btn.setText("Test2");
}
});
}
}
以上是@Retention(RetentionPolicy.RUNTIME)运行时解读的具体写法。
优点是可读性强,写起来简单。
缺点便是常驻内存,并利用反射进行初始化,耗费内存
如何解决这个问题呢?其实我们可以利用apt,使用编译时解读或者类加载时解读,后面的文章我会重点讲到,这里就不多啰嗦了。
So easy!
网友评论