3分钟快速掌握注解(Annotation)
由于本人能力有限,文中若有错误之处,欢迎指正。
转载请注明出处:http://www.jianshu.com/p/e3949dedbe48
写在前面
本篇主要讲解注解的基本概念、基本使用,以及容易产生迷惑的地方。实例应用将在后续的简文中继续分析。
什么是注解?
官方解释:能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。
注解(也被成为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
通俗来说:其实注解也是一种注释,只不过,注解可以指定保留的时间,以及携带一些相关的数据。而注释只能在源码中存在。
元注解
注解当中也会存在注解,这种给自定义注解使用的注解称为---元注解。
- @Rentention
用来标记自定义注解的有效范围。有三种:
有效范围 | |
---|---|
RetentionPolicy.SOURCE | 只在源代码中保留,编译器会忽略,一般都是用来增加代码的理解性或者帮助代码检查之类的,比如@Override
|
RetentionPolicy.CLASS |
默认选择,能把注解保留到编译后的字节码class文件中,仅仅到字节码文件中,运行时是无法得到的。一般用在动态生成模板代码的类库中。比如ButterKnife
|
RetentionPolicy.RUNTIME | 注解不仅 能保留到class字节码文件中,还能在运行通过反射获取到,这是最常用的一种。 |
- @Target
规定注解所修饰的对象范围。
修饰范围 | |
---|---|
ElementType.CONSTRUCTOR | 构造器声明 |
ElementType.FIELD | 成员变量、对象、属性(包括enum实例) |
ElementType.LOCAL_VARIABLE | 局部变量声明 |
ElementType.METHOD | 方法声明 |
ElementType.PACKAGE | 包声明 |
ElementType.PARAMETER | 参数声明 |
ElementType.TYPE | 类、接口(包括注解类型)或enum声明 |
@Documented
标记着此注解会被javadoc工具提取成文档。@Interited
允许子类继承父类中的注解。将在最后以示例的形式讲解。
自定义注解
我们可以在使用元注解的基础上,使用
@interface
关键字实现自定义注解。其默认继承自:java.lang.annotation.Annotation
。如下所示:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface MyAnnotation {
int value() default 1;
boolean canBeNull() default false;
}
上面的自定义注解可以这样使用:@MyAnnotation @MyAnnotation() @MyAnnotation(1) @MyAnnotation(value=1) @MyAnnotation(canBuNull = true) @MyAnnotation(value = 1,canBuNull = true)
但不能这样使用:@MyAnnotation(true)
因为当括号中的内容不以键值对的形式出现时,默认使用的是value =
。
注解元素
注解元素名来自于注解类的方法名,且有以下特点:
- 方法返回值只能是
基本数据类型
,String
,Class
,annotation
,enum
或它们的一维数组
。 - 所有方法没有方法体,没有参数,没有修饰符,默认为
public abstract
修饰。不允许抛异常。 - 若只有一个默认元素,可直接用
value()
函数。一个元素都没有表示该注解为标记注解(Mark Annotation)
。
默认值
注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定。
注解元素的值不能为null
。
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface MyAnnotation {
int value() default 1;
boolean[] canWrite() default {true, false};
Class test(); // 定义的时候没有默认值,在使用的时候必须指定
}
// 使用示例
@MyAnnotation(value = 23, test = Integer.class)
int age;
@Interited元注解
允许子类继承父类中的注解。
当被@Inherited
标注的注解的Retention
为RetentionPolicy.RUNTIME
,则反射API增强了这种继承性。如果我们使用反射去查询一个@Inherited
注解类型的注解时,反射代码检查将展开工作:检查class和其父类
,直到指定的注解类型被发现,或者到达类继承结构的顶层Object
。
相对于其它元注解,@Interited元注解
比较特殊,下面将以示例的形式帮助大家更好的理解它的作用:
首先我们定义一个自定义注解@MyAnnotation
,并使用@Interited标记
:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface MyAnnotation {
String value() default "";
}
定义一个类SuperClass
:
@MyAnnotation("SuperClass")
public class SuperClass {
@MyAnnotation("SuperClass::testMethod")
public void testMethod(){
}
}
定义一个类SubClass
继承自SuperClass
:
public class SubClass extends SuperClass {
}
测试主程序:
public class Main {
public static void main(String[] args) throws Exception{
testSuper();
testSub();
}
private static void testSub() {
MyAnnotation myAnnotation = SubClass.class.getAnnotation(MyAnnotation.class);
if(myAnnotation == null){
System.out.println("注解不存在");
return;
}
String value = myAnnotation.value();
System.out.println(value);
}
private static void testSuper() {
MyAnnotation myAnnotation = SuperClass.class.getAnnotation(MyAnnotation.class);
if(myAnnotation == null){
System.out.println("注解不存在");
return;
}
String value = myAnnotation.value();
System.out.println(value);
}
}
运行以上程序,testSub()
testSuper()
都打印的是在SuperClass类
标记的注解值SuperClass
。而SubClass类
中没有使用注解,所以,可以确定是从父类中继承过来的。这里面起到关键作用的就是@Interited元注解
。
还有很多种情况,大家可以自行编码尝试,下面给出总结:
注: 父类使用的注解被@Inherited标注的情况下
- 如果父类的注解是作用在类上面,那么子类是可以继承过来。
- 如果父类的注解作用在方法上面,那么子类也可以继承过来。
- 如果子类重写了父类中使用了注解的方法,那么子类将无法继承该方法的注解。
- 实现类不能继承接口中的注解。
下一篇: Flask,web框架学习_持续更新