本章章节
> 9.1 注解(Annotation)
> 9.2 JDK提供的几个基本注解
> 9.3 自定义注解
9.1 注解(Annotation)简介
Annotation(注解)是 JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在JDK1.5之后称为注解。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释。所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。代码分析工具、开发工具和部署工具可以通过注解来进行验证或进行部署。它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等。
9.2 JDK提供的几个基本注解
注解的语法比较简单,除了@符号的使用之外,它基本与Java固有的语法一致。java.lang.annotation.Annotation接口是所有的Annotation都必须实现的接口。在JDK1.5 之后,系统中已经建立了如下的三个内建标准Annotation类型,用户直接使用即可。
系统内建的三种标准Annotation:
- @Override:覆写的Annotation
- @Deprecated:不赞成使用的Annotation(过期、失效)
- @SuppressWarnings:压制安全警告的Annotation(忽略警告)
三种内定的标准Annotation如图9-1所示:
图9-1 三种内定的Annotation
在Idea中输入@Override,然后按F3可以定位到@Override在Java中的声明位置。
另外,Java还提供了四种元注解,它们专门负责新注解的创建。
四种元注解:
@Target:表示该注解用于什么地方。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
可能的 ElemenetType 参数包括:
ElemenetType.CONSTRUCTOR 构造器之前声明。
ElemenetType.FIELD 域之前声明(包括 enum 实例)。
ElemenetType.LOCAL_VARIABLE 局部变量之前声明。
ElemenetType.METHOD 方法之前声明。
ElemenetType.PACKAGE 包之前声明。
ElemenetType.PARAMETER 方法参数之前声明。
ElementType.ANNOTATION_TYPE 注解类型之前声明。
ElemenetType.TYPE 类,接口(包括注解类型)或enum之前声明。
@Retention:表示在什么级别保存该注解信息。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
可选的RetentionPolicy参数包括:
RetentionPolicy.SOURCE 表示此注解信息只能保留在程序的源文件中,编译之后不会保存在编译好的类文件(*.class)之中。
RetentionPolicy.CLASS 表示此注解信息保留在程序源文件(*.java)和编译之后的类文件(*.class)之中,在使用此类的时候,这些注解信息不会被加载到JVM之中,如果一个注解声明时没有制定范围,则默认是此范围。
RetentionPolicy.RUNTIME 表示此注解信息保留在源文件(*.java)、类文件(*.class)和执行时也会加载。即JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
举一个例子,如@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息,相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被 Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated。
@ Documented:将此注解包含在javadoc中。
@ Inherited:允许子类继承父类中的注解。
9.2.1 @Override
该注解用在方法前面,用来标识该方法是重写父类的某个方法。@Override表示方法覆写的正确性。用来检测子类覆盖父类方法,是否与父类里方法签名一致。如果你不小心拼写错误,或者方法原型与要覆盖的方法不一致,则编译器就会发出错误提示。
在idea中输入@Override,然后按F3可以定位到@Override在Java中的声明位置。如下就是@Override注解的声明:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
除了@符号以外,@Override的定义很像一个空的接口。@Target(ElementType.METHOD)表示@Override注解可以用于什么地方,这里只能用于METHOD(方法)前面;@Rectetion(RectetionPolicy.SOURCE)用来定义注解在哪一个级别可用,SOURCE表示源代码中,即@Override会被编译器丢弃。
@Override使用举例:
class A{ public void disp(){ } } class B extends A{ @Override public void disp(int a){ } }
如果不写@Override,则编译没有任何问题,但是加上@Override之后,表示现在的disp是覆盖父类disp方法,但是却跟父类的方法原型不一致,所以此时会报错。
9.2.2 @Deprecated
该注解的作用是标记某个过时的类或方法。使用@Deprecated注释的Annotation本身是不建议使用的一个操作,表示已过时。当使用时,可能会出现了一个安全的警告信息。
在idea中输入@Deprecated,然后按F3可以定位到@Deprecated在Java中的声明位置。如下就是@Deprecated注解的声明:
@Documented @Retention(RetentionPolicy.RUNTIME) public @interface Deprecated { }
@Documented表示@Deprecated注解包含在javadoc文档中。@Retention(RetentionPolicy.RUNTIME)表示在运行期间也保留@Deprecated注解。
我们在idea中输入Date,然后按F3去查看java.util.Date这个类的说明时,可以发现,在该类中,有很多类似下面的语法:
@Deprecated
public Date(int year, int month, int date) {
this(year, month, date, 0, 0, 0);
}
这些语法的特点是,在方法的前面加上了@Deprecated修饰之后,该方法会多一条横杠,表示此方法已经过期。如果我们还使用这个方法的话,编译器可能会提示警告信息。
@Deprecated使用举例:
@Deprecated //修饰类
class C{
@Deprecated //修饰属性
private String name = "zhangsan";
@Deprecated //修饰方法
public int add(int a, int b){
return a + b;
}
}
9.2.3 @SuppressWarnings
@SuppressWarnings 用于压制警告信息。通常写完一个程序之后可能会有这样那样的警告信息,可以利用@SuppressWarnings来消除。
在idea中输入@Deprecated,然后按F3可以定位到@Deprecated在Java中的声明位置。如下就是@Deprecated注解的声明:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})表示@SuppressWarnings可以用于类、接口、枚举、域声明、方法、参数、构造器、局部变量的前面。@Retention(RetentionPolicy.SOURCE)表示 @SuppressWarnings只能在源文件中有效。内部是一个String[] value();数组属性,表示可以列举多项。
@SuppressWarnings使用举例:
public class Test { public static void main(String[] args) { @SuppressWarnings("all") int i = 0; } }
定义变量i没有使用过,会提示警告,通过@SuppressWarning ("all") 取消所有警告。可以同时压制多个警告信息,只需要以数组的形式出现即可。
如:@SuppressWarnings({"unused", "serial", "deprecation"})
关于@SuppressWarnings()中列举的含义参考下表:
9.3 自定义注解
9.3.1 定义注解
定义简单的注解的格式:
//@Target(适用场合)
//@Retention(保存级别)
//@Documented
//@Inherited
[public] @interface 注解名称{
数据类型 变量名称();
}
如果定义注解时没有指定@Target元注解来限制它的使用范围,那么该注解可以使用在 ElementType枚举指定的任何一个元素前。否则,只能声明在@Target元注解指定的元素前。
如果定义注解时没有指定@Retention元注解来说明它的生命周期,那么该注解默认的生命周期是保留在一个CLASS文件中。当然,也可以由一个@Retetion的元注解指定它的生命周期。
所有的注解默认情况下都是使用@Documented进行注释,而且在生成javadoc的时候可以通过@Documented设置一些说明信息。
下面是一个定义注解的实例:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface MyAnnotation { String value(); }
其中的@interface是一个关键字,用来声明一个注解。在设计annotation的时候必须把一个类型定义为@interface,而不能用class或interface关键字。所有的注解类都隐式继承于java.lang.annotation.Annotation,注解不允许显式继承于其他的接口。
一个注解可以拥有多个成员,成员声明和接口方法声明类似,这里,我们仅定义了一个成员,成员的声明有以下几点限制:
·成员以无参无抛出异常的方式声明,如“boolean value(String str);”、“boolean value() throws Exception;”等方式是非法的;
·可以通过default为成员指定一个默认值,如:“String level() default "LOW_LEVEL";”、“int[] high() default {2, 3, 4};”是合法的,当然也可以不指定默认值;
·成员类型是受限的,合法的类型包括原始类型及其包装类、String、Class、enums、注解类型,以及上述类型的数组类型。如“ForumService value();”、“List foo();”是非法的。
·如果注解只有一个成员,则成员名通常取名为value(),这样在使用时可以忽略成员名和赋值号(=),如@ MyAnnotation ("使用注解的实例")。注解类拥有多个成员时,如果仅对value成员进行赋值则也可不使用赋值号,如果同时对多个成员进行赋值,则必须使用赋值号,如@DeclareParents (name = "张三", age = 25)。
·注解类可以没有成员,没有成员的注解称为标识注解,解释程序以标识注解存在与否进行相应的处理;
·在操作中,对于一个注解而言有时候会固定其取值范围,只能取固定的几个值,那么这个时候实际上就需要依靠枚举。
9.3.2 使用注解
使用注解的语法:
一般格式:
@注解名(成员名1=成员值1, 成员名2=成员值2, ...)
如果是成员的类型是数组:
@注解名(成员名1={成员值1, 成员值2…}, 成员名2=成员值2, ...)
下面是一个使用注解注解的实例。
@MyAnnotation(value="my Annnotation") public class TestAnnotation { //… }
如果成员是数组类型,可以通过{}进行赋值,如boolean数组的成员可以设置为{true, false, true}。下面是几个注解使用的例子:
·多成员的注解
@MyAnnotation(id= 2868724, synopsis = "Enable time-travel", engineer = "Mr. Peabody")
·一个成员的注解,且成员名为value,可以省略成员名和赋值符号
@ MyAnnotation ("my Annnotation")
·无成员的注解
@Override
·成员为字符串数组的注解
@SuppressWarnings(value={"unchecked", "fallthrough"})
·成员为注解数组类型的注解
//MyAnno.java import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface MyAnno { String[] value(); }
//MyAnnotation.java import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface MyAnnotation { String[] name(); int age() default 25; MyAnno[] anno(); }
//TestAnnotation.java @MyAnnotation(name={"aaa", "bbb"}, anno={@MyAnno(value={"aaa", "bbb"}), @MyAnno(value={"ccc", "ddd", "eee"})}) public class TestAnnotation { public static void main(String[] args) { } }
9.3.3 得到注解
一个注解要想让其变得有意义,则必须结合发射机制取得注解中设置的全部内容。对于生命周期为运行期间的注解,都可以通过反射获得该元素上的注解实例。
1、声明在一个类中的注解
可以通过该类 Class 对象的 getAnnotation 或 getAnnotations 方法获得。
2、声明在一个字段中的注解
通过 Field 对象的 getAnnotation 或 getAnnotations 方法获得
3、声明在一个方法中的注解
通过 Method 对象的 getAnnotation 或 getAnnotations 方法获得
感谢阅读。如果感觉此章对您有帮助,却又不想白瞟