注解是java1.5引入的,比如@Override,@Deprecated等都是比较常见的注解,Java程序员在日常开发中注解使用广泛.
一.什么是注解
下面简单看下Java官方定义的注解,其中有些东西可能没见过,不过没关系,下面会讲.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
注解是一种描述数据的数据.官方称为元数据. 上面的Override表示的是重写父类的方法,这个注解让代码更具可读性,开发人员一看就知道这是拿来干什么的.其实这个注解在编译阶段就会被丢弃,因为这个注解只是拿来给编译器和开发人员看的.在运行时无实际意义.
二.为什么要引入注解
我暂时想到的是以下2点:
1. 规范
在Android中经常看到@StringRes,@NonNull等等注解,是为了让参数传递更规范,避免出错.
2. 注入
有时候在一个属性上,加一个注解,然后第三方框架就可以给这个属性赋值.这就是简单的注入.使用非常方便,代码解耦.比如出名的ButterKnife.还有Spring,Spring和注入相关的常见注解有Autowired、Resource、Qualifier、Service、Controller、Repository、Component。
三.Annotation是如何工作的?怎么编写自定义的Annotation?
对于上面的Override注解代码定义,大家可能还有点迷惑.就这样简单的定义一下,就可以了么??? 当然不是. Annotations是给大家在代码中使用的,并且不包含业务逻辑,那么就必须有人来实现业务逻辑. 那么谁来做这个事情->元数据的用户,需要从注解上面读取信息并实现必要的逻辑.Annotations仅仅提供它定义的属性(类/方法/包/域)的信息。
@MyTodo(author = "feiyang")
public static void main(String[] args) {
}
比如上面这段代码,我使用了注解,那么肯定需要去读取这个注解上面的值,然后做些什么操作,注解才有意义.That’s it.
编写注解
如果你仔细观察,你可能发现了,上面的Override定义中,Override本身是一个注解,但是注解上面居然还有注解……(/懵)
在Java1.5中java.lang.annotation提供了四种元注解,专门注解其他的注解:
@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target –注解用于什么地方
@Inherited – 是否允许子类继承该注解
Retention
定义该注解的生命周期
- RetentionPolicy.SOURCE 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
- RetentionPolicy.CLASS 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
- RetentionPolicy.RUNTIME 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Target
表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:用于描述实例变量
ElementType.METHOD 方法
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一个注释
ElementType.PACKAGE 用于记录java文件的package信息
下面来实操一下如何来定义注解,Annotations只支持基本类型、String及枚举类型。注释中所有的属性被定义成方法,并允许提供默认值。
@Target(ElementType.METHOD) //只能用在方法上
@Retention(RetentionPolicy.RUNTIME) //运行时也不会被丢弃的
public @interface MyTodo {
//枚举
enum Priority {
LOW, MEDIUM, HIGH
}
enum Status {STARTED, NOT_STARTED}
//普通的属性 带一个默认值
String author() default "xfhy";
Priority priority() default Priority.LOW;
Status status() default Status.STARTED;
}
使用方式:
@MyTodo(author = "feiyang", status = MyTodo.Status.STARTED, priority = MyTodo.Priority.HIGH)
public static void main(String[] args) {
}
目前为止,我们定义了自己的自定义注解,然后可以用于一个业务的方法之上,但是我们目前并不知道属性上面的值是多少.所以我们还需要去读取这些属性的值.
private void getMethodsAnnotation() {
Class<AnnotationTest> annotationTest = AnnotationTest.class;
//获取所有方法
for (Method method : annotationTest.getMethods()) {
//获取该方法上面的注解
MyTodo annotation = method.getAnnotation(MyTodo.class);
if (annotation != null) {
//获取方法名
System.out.println("Method name : " + method.getName());
//获取注解的值
System.out.println("Author : " + annotation.author());
System.out.println("Priority : " + annotation.priority());
System.out.println("Status : " + annotation.status());
}
}
}
这样我们就读取到了注解上的值了.