Java SE 5 引入了许多的语言变动,注解(元数据)就是其中之一,次要是在代码中附加补充信息。注解不会影响代码的逻辑,只是在编译、运行时,为代码退出配置与形容等性能。
注解应用 @interface
来定义注解。如下所示:
@interface Mapping {String name();
String[] path() default {};
Type type() default Type.GET;}
注解不能蕴含 extends
子句。然而,所有的注解类型都隐式地间接扩大自 java.lang.annotation.Annotation
接口。
当 Mapping
注解中只须要一个注解元素时,能够将名称设成 value
,这样指定值时就不须要指定注解元素的名称了。如 @Mapping("get")
。
注解中的元素类型能够应用根本类型、String
、Class
、enum
、注解类型以及后面所述类型组成的数组。而且能够应用 default
为注解元素提供默认值。如上所示的 String[] path() default {}
就是设置了默认值的注解元素。
注解申明
定义完的注解须要在代码中应用,申明注解能够呈现在包、类、接口、办法、结构器、实例域、局部变量、参数变量以及参数类型上。上面给出一个例子:
@Mapping("example") public class Sample {...}
留神:对局部变量的注解只能在源码级别上进行解决。类文件并不形容局部变量。因而,所有的局部变量注解在编译完一个类的时候就会被遗弃掉。同样地,对包的注解不能在源码级别之外存在。
内置注解
Java SE 提供了许多注解,但其中大部分是专用注解。其中有规定注解和元注解。
@Deprecated
该注解用于标记不想被应用的项上。当应用@Deprecated
标记时,编译器就会收回正告。这个注解与Javadoc
标签@deprecated
具备等同效用。@SuppressWarnings
该注解会告知编译器阻止特定类型的正告信息。如@SuppressWarnings("unchecked")
。@Override
该注解只能利用于办法上,示意以后的办法定义将笼罩基类的办法。如果你的类中重写equals(Object obj)
办法时,就能够应用@Override
注解标记。不是被笼罩的办法就不要用@Override
,那样编译器会报错。@SafeVarargs
该注解用于对具备泛型varargs
参数可平安应用。作用于办法或结构器上。该注解为 Java SE 7 新增注解。@FunctionalInterface
该注解用于标记只有一个形象办法的接口,申明为函数式接口。该注解为 Java SE 8 新增注解。
其中,不蕴含注解元素的非凡类型的注解被称为标记注解,它惟一的目标就是标记申明。确定标记注解是否存在的最好形式是应用 isAnnotationPresent()
办法,该办法是由 AnnotatedElement
接口定义的。
元注解
还有一些注解用以形容注解接口的行为属性,这种注解称为元注解(meta annotation)。Java 以提供了一些元注解。如下所示:
@Retention
@Retention
定义注解的保留策略,用以决定注解在什么地位被抛弃。而保留策略被封装在 java.lang.annotation.RetentionPolicy
枚举中。如下所示:
RetentionPolicy.SOURCE
只在源文件中保留,在编译期间会被摈弃。RetentionPolicy.CLASS
在编译期间被存储到.class
文件中。然而会在运行时通过 JVM 不能失去这些注解。RetentionPolicy.RUNTIME
在编译期间被存储到.class
文件中,并且在运行时能够通过 JVM 获取这些注解。因而,能够通过反射机制读取注解的信息。
留神:局部变量申明的注解不能存储在 .class
文件中。
如果 @Retention
不存在,则该 Annotation
默认为 CLASS
。因为通常咱们自定义的Annotation
都是 RUNTIME
,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)
这个元注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface Mapping {String name();
String[] path() default {};
Type type() default Type.GET;}
在程序执行期间通过 JVM 能够获取 Mapping
注解。
@Target
@Target
定义注解的申明类型,用来决定注解利用的地位,如类、办法、变量、参数以及包等。而定义的类型封装在 java.lang.annotation.ElementType
枚举中。如下所示:
ElementType.ANNOTATION_TYPE
注解类型申明。ElementType.CONSTRUCTOR
结构器申明。ElementTyoe.FIELD
域变量申明。ElementType.LOCAL_VARIABLE
局部变量申明。ElementType.METHOD
办法申明。ElementType.PACKAGE
包申明。ElementType.PARAMETER
参数申明。ElementType.TYPE
类、接口或者枚举申明。ElementType.TYPE_PARAMETER
类型参数。ElementType.TYPE_USE
类型应用。
如下所示,定义的注解 @Mapping
可用在办法上:
@Target(ElementType.METHOD)
public @interface Mapping {String name();
String[] path() default {};
Type type() default Type.GET;}
在 @Target
注解中也能够指定多个值,并用 {}
包围起来。如下所示:
@Target({
ElementType.METHOD,
ElementType.FIELD})
public @interface Mapping {String name();
String[] path() default {};
Type type() default Type.GET;}
如果不应用 @Target
,能够利用于任何申明上。因而,一般来说,显式指定指标是个好主见,能够明确阐明注解的用处。
TYPE_PARAMETER
和 TYPE_USE
枚举常量都是 Java SE 8 新减少的枚举常量。类型注解必须蕴含 ElementType_USE
,这样就能够注解办法的返回类型、办法内 this
的类型、强制转换、数组级别、被继承的类以及 throws
子句。还能够注解泛型,包含泛型类型参数边界和泛型类型参数。
@Documented
@Documented
注解是一个标记接口,用于告知某个工具注解将被文档化。@Documented
被设计为只能注解其余注解。
默认状况下,javadoc
不解决注解,但在注解上标记 @Documented
,就会被像 javadoc
这样的归档工具解决,以实现归档。
@Documented
@Target(ElementType.METHOD)
public @interface Mapping {String name();
String[] path() default {};
Type type() default Type.GET;}
@Inherited
@Inherited
是一个标记注解,用于另外一个注解申明。@Inherited
标记的注解能够被子类继承,而且只能用于被 @Target(ElementType.TYPE)
类型标注的注解。
因而,当查问子类的特定注解时,如果那种注解在子类中不存在,就会查看基类。如果那种注解存在于超类中,并且如果应用 @Inherited
进行了注解,就将返回那种注解。
定义一个标记了 @Inherited
的注解 @Persistent
。
@Inherited @interface Persistent {}
这里应用 @Persistent
注解来指明一个类的对象能够存储到数据库中。
@Persistent public class Person {}
那么继承自 Person
的子类也会主动被注解为长久化。
public class Student extends Person {}
@Repeatable
Java SE 8 能够应用 @Repeatable
注解将同种类型的注解屡次利用于某一项。为放弃向后兼容,可反复注解的实现者须要提供一个容器注解,它能够将这些反复注解存储到一个数组中。
@Repeatable(Mappings.class)
@Target(ElementType.METHOD)
public @interface Mapping {String name();
String path() default“”;
Type type() default Type.GET;}
@Target(ElementType.Type)
public @interface Mappings {Mapping[] value();}
通过 @Repeatable
润饰后,只有用户提供两个或更多 @Mapping
注解,那就会主动地被包装到 @Mappings
注解中。
@Mapping(name="GET Mapping", path="/profile")
@Mapping(name="POST Mapping", path="/profile", type=Type.POST)
public class AnnotationTest {}
这里,应用 getAnnotation()
办法获取反复注解的话,须要传入的是容器注解的类,而不是反复注解自身。AnnotatedElement
接口还提供了 getAnnotationsByType()
和 getDeclaredAnnotationsByType()
这两个办法来间接操作反复注解。
@Native
应用 @Native
注解润饰成员变量,示意这个变量能够被本地代码援用,经常被代码生成工具应用。@Native
注解不常应用。
注解解决
当注解指定 RUNTIME
保留策略时,运行时就能够通过反射 API 来查问注解。反射 API 用到的类如 Class
、Field
、Method
以及 Constructor
都实现了 AnnotatedElement
注解接口。
该接口提供了几个办法来获取注解。其中 getAnnotationsByType()
、getDeclaredAnnotation()
以及 getDelaredAnnotationsByType()
办法都是 Java SE 8 新增办法。
getAnnotation()
AnnotatedElement
注解接口提供的 getAnnotation()
办法能够取得与对象关联的注解。该办法的个别模式如下:
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
这里的 annotationClass
示意你要查找的注解类型。该办法返回对注解的一个援用,应用这个援用能够获取与注解元素关联的值。如果没有找到注解,该办法会返回 null
。上面应用该办法获取 @Mapping
注解。
Class<?> cls = AnnotationTest.class;
cls.getAnnotation(Mapping.class); // 获取 Mapping 注解
getAnnotations()
AnnotatedElement
注解接口提供的 getAnnotations()
办法会返回 Class
、Method
、Constructor
以及 Field
等类型的对象中的所有注解的一个注解数组。该办法的个别模式如下:
Annotation[] getAnnotations()
上面应用该办法获取所有注解。
Class<?> cls = AnnotationTest.class;
cls.getAnnotations(); // 获取 cls 对象中的所有注解
getDeclaredAnnotations()
AnnotatedElement
注解接口提供的 getDeclaredAnnotations()
办法返回的是调用对象中存在的所有非继承注解。该办法的个别模式如下:
Annotation[] getDeclaredAnnotations()
isAnnotationPresent()
AnnotatedElement
注解接口提供的 isAnnotationPresent()
办法用于判断调用对象是否存在指定的注解。该办法的个别模式如下:
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)`
如果 annotationClass
指定的注解与调用对象相关联,返回 true
,否则返回 false
。上面应用该办法判断 @Mapping
注解是否存在。
Class<?> cls = AnnotationTest.class;
if (cls.isAnnotationPresent(Mapping.class)) {...}
getDeclaredAnnotation()
AnnotatedElement
注解接口提供的 getDeclaredAnnotation()
办法返回与对象关联的注解。该办法会疏忽继承注解,个别模式如下:
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {...}
这里的 annotationClass
与 getAnnotation()
的参数一样,都是示意要查找的注解类型。
getAnnotationsByType()
AnnotatedElement
注解接口提供的 getAnnotationsByType()
办法用以获取与对象关联的注解。如果没有与此元素关联的注解,则返回值是长度为 0 的数组。该办法的个别模式如下:
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {...}
该办法与 getAnnotation(Class<T> annotationClass)
的区别在于,检测其注解对应的反复注解容器。
getDeclaredAnnotationsByType()
AnnotatedElement
注解接口提供了 getDeclaredAnnotationsByType()
办法。如果注解间接存在或间接存在,则返回该元素的注解,如果没有,则返回值为长度为 0 的数组。该办法的个别模式如下:
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {...}
此办法疏忽继承的注解,而且与 getDeclaredAnnotation(Class<T> annotationClass)
的区别在于,检测其参数是否为可反复的注解类型,如果是,尝试查看反复注解容器来查找该类型的一个或多个注解。
总结
通过注解,能够在不影响代码语义的状况下,提供了其形容信息,这些信息可能被内部工具主动解决。能够通过反射 API 获取注解,并进行操作。文档生成、编译查看、动静解决等方面都能够应用注解。
更多内容请关注公众号「海人为记」