1.1 注解声明
Java注解Annotation,有声明注解和元注解
- 元注解:Java提供的元注解,所谓元注解就是标记其他注解的注解(@Target,@Retention),即@Target(ElementType.ANNOTATION_TYPE)
- 声明注解:用@interface声明的注解
@Target
@Target 用来约束注解可以应用的地方(如类,方法,字段)
/*
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 9.7.4 Where Annotations May Appear
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** 标明该注解可以用于类、接口(包括注解类型)或enum声明 */
TYPE,
/** 标明该注解可以用于字段(域)声明,包括enum实例 */
FIELD,
/** 标明该注解可以用于方法声明 */
METHOD,
/** 标明该注解可以用于参数声明 */
PARAMETER,
/** 标明注解可以用于构造函数声明 */
CONSTRUCTOR,
/** 标明注解可以用于局部变量声明 */
LOCAL_VARIABLE,
/** 标明注解可以用于注解声明(应用于另一个注解上)*/
ANNOTATION_TYPE,
/** 标明注解可以用于包声明 */
PACKAGE,
/**
* 标明注解可以用于类型参数声明(1.8新加入)
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 类型使用声明(1.8新加入)
* @since 1.8
*/
TYPE_USE
}
当声明的注解未指定Target值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开,如下:
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
@Retention
用来约束注解的保留的地方,有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime)
/*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
1.2 注解元素
如何声明一个注解元素:
@Target(ElementType.TYPE)//只能应用于类上
@Retention(RetentionPolicy.RUNTIME)//保存到运行时
public @interface DBTable {
String name() default "";
}
上面这个例子说明,DBTable这个注解声明了一个String类型的name的元素。
default
关键字 表示默认值
name() 方法不能传参,否则编译出错:
@interface members may not have parameter
在使用注解元素的时候,使用键值对的方式,以方法名作为key,value表示元素的返回值
//在类上使用该注解
@DBTable(name = "MEMBER")
public class Member {
//.......
}
注解支持的元素数据类型除了String,还支持如下数据类型:
- 所有基本类型(使用基本类型但不允许使用任何包装类型)
- String
- Class
- enum
- Annotation
- 上面类型的数组
示例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Reference{
boolean next() default false;
}
public @interface AnnotationElementDemo {
//枚举类型
enum Status {FIXED,NORMAL};
//声明枚举
Status status() default Status.FIXED;
//布尔类型
boolean showSupport() default false;
//String类型
String name()default "";
//class类型
Class> testCase() default Void.class;
//注解嵌套
Reference reference() default @Reference(next=true);
//数组类型
long[] value();
}
编译器对元素的默认值有些过分挑剔。
- 元素必须要有值:要么定义元素的时候设置默认值,要么在使用注解的时候提供元素的值
- 元素的值不能为null。
1.3 注解不支持继承
注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口,这里我们反编译前面定义的DBTable注解:
import java.lang.annotation.Annotation;
//反编译后的代码
public interface DBTable extends Annotation
{
public abstract String name();
}
虽然反编译后发现DBTable注解继承了Annotation接口,请记住,即使Java的接口可以实现多继承,但定义注解时依然无法使用extends关键字继承@interface。
1.4 注解快捷方式
快捷方式就是注解中定义了名为value的元素,并且在使用该注解时,如果该元素是唯一需要赋值的一个元素,那么此时无需使用key=value的语法,而只需在括号内给出value元素所需的值即可。这可以应用于任何合法类型的元素,记住,这限制了元素名必须为value,简单案例如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerVaule{
int value() default 0;
String name() default "";
}
//使用注解
public class QuicklyWay {
//当只想给value赋值时,可以使用以下快捷方式
@IntegerVaule(20)
public int age;
//当name也需要赋值时必须采用key=value的方式赋值
@IntegerVaule(value = 10000,name = "MONEY")
public int money;
}
2.1 Java内置注解与其它元注解
Java常用内置注解三个:
- @Override:用于标明此方法覆盖了父类的方法
- @Deprecated:用于标明已经过时的方法或类
- @SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告.其内部有一个String数组,主要接收值如下:
- deprecation:使用了不赞成使用的类或方法时的警告;
- unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
- fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
- path:在类路径、源文件路径等中有不存在的路径时的警告;
- serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
- finally:任何 finally 子句不能正常完成时的警告;
- all:关于以上所有情况的警告。
Java常用内置元注解:
- @Target
- @Retention
- @Documented 被修饰的注解会生成到javadoc中
- @Inherited 可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解
3. 注解与反射机制
经过反编译后, 知道Java所有注解都继承了Annotation接口。也就是说 Java使用Annotation接口代表注解元素。
为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,如反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口
示例:
注解类:DocumentA
import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DocumentA {
}
被注解修饰类A:
@DocumentA
public class A {
}
测试类DocumentDemo:
public class DocumentDemo extends A{
public static void main(String[] args) {
Class> clazz = DocumentDemo.class;
//根据指定注解类型获取该注解
DocumentA documentA = clazz.getAnnotation(DocumentA.class);
System.out.println("A:"+documentA);
//获取该元素上的所有注解,包含从父类继承
Annotation[] annotations = clazz.getAnnotations();
System.out.println("an:"+ Arrays.toString(annotations));
//获取该元素上的所有注解,但不包含继承!
Annotation[] an2 = clazz.getDeclaredAnnotations();
System.out.println("an2:"+ Arrays.toString(an2));
//判断注解DocumentA是否在该元素上
boolean b=clazz.isAnnotationPresent(DocumentA.class);
System.out.println("b:"+b);
}
}
-----------------
输入结果:
A:@com.littlezan.test.testannotation.DocumentA()
an:[@com.littlezan.test.testannotation.DocumentA()]
an2:[]
b:true
4. 运行时注解处理器
通过注解Api和反射包中与注解相关的Api,解析注解内容来组装需要的数据
5. Java 8中注解增强
5.1 元注解@Repeatable
元注解@Repeatable是JDK1.8新加入的,它表示在同一个位置重复相同的注解。在没有该注解前,一般是无法在同一个类型上使用相同的注解的
//Java8前无法这样使用
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {}
Java8前如果是想实现类似的功能,我们需要在定义@FilterPath注解时定义一个数组元素接收多个值如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPath {
String [] value();
}
//使用
@FilterPath({"/update","/add"})
public class A { }
但在Java8新增了@Repeatable注解后就可以采用如下的方式定义并使用了
//使用Java8新增@Repeatable原注解
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//参数指明接收的注解class
public @interface FilterPath {
String value();
}
参考链接:
理解Java注解