注解
Java注解
Java注解(Annotation)提供了一种为程序元素设置元数据的办法。
元数据(MetaData)是关于数据的数据。在编程语言上下文中,元数据是添加到程序元素如方法、字段、类和包上的额外信息。
Annotation能被用来为程序元素:类,方法,成员变量等设置元数据。
Annotation是一个接口,程序可以通过反射获取指定程序元素的Annotation对象,然后通过Annotation对象来获取注解里的元数据。
基本Annotation
使用Annotation时,前面加上@符号,当做一个修饰符使用。
5个基本的Annotation如下:
- @Override
- @Deprecated
- @SuppressWarnings
- @SafeVarargs
- @FunctionalInterface
@Override
@Override用来指定方法覆载的, 强制子类必须覆盖父类的方法.
@Override只能修饰方法
用于告诉编译器检查父类包含被重写的方法,否则编译出错
class Fruit {
public void info() {
}
}
class Apple extends Fruit {
@Override
public void info() {
System.out.println("Apple info");
}
}
@Deprecated
@Deprecated 用于表示某个程序元素:类,方法等已过时,当其他程序使用时,编译器会出现警告。
public class OverTest {
public void test(){
//出现编译警告
new Fruit().info();
}
}
class Fruit {
@Deprecated
public void info() {
}
}
@SuppressWarnings
@SuppressWarnings 用于取消显示指定的编译器警告。
会一直作用于该程序元素,以及所有子元素。
@SuppressWarnings(value = "unchecked")
public class OverTest {
public static void main(String[] args){
List items = new ArrayList();
items.add("a");
}
}
//去掉时@SuppressWarnings
注: OverTest.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
@SafeVarargs
当把一个不带泛型的对象赋给带泛型变量时,会发生『堆污染』(Heap pollution).
@SafeVarargs注解只能用在参数长度可变的方法或构造方法上,且方法必须声明为static或final,否则会出现编译错误。
public static void main(String[] args){
ArrayList<Integer> a1 = new ArrayList<>();
a1.add(new Integer(1));
a1.add(2);
showArgs(a1, 12);
}
@SafeVarargs
public static <T> void showArgs(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
// 注释掉@SafeVarargs, 使用-Xlint:unchecked编译
~: javac OverTest.java -Xlint:unchecked
OverTest.java:14: 警告: [unchecked] 参数化 vararg 类型T的堆可能已受污染
public static <T> void showArgs(T... array) {
^
其中, T是类型变量:
T扩展已在方法 <T>showArgs(T...)中声明的Object
1 个警告
@FunctionalInterface
@FunctionalInterface 用来指定某个接口必须是函数式接口。
只有一个抽象方法的接口为函数式接口。
@FunctionalInterface
interface FunInterfaceTest {
void test();
void abc(); //多个编译报错, 应该去掉
}
// 编译
Error:(13, 1) java: 意外的 @FunctionalInterface 注释
testCode.FunInterfaceTest 不是函数接口
在 接口 testCode.FunInterfaceTest 中找到多个非覆盖抽象方法
元Annotation
元 Annotation (Meta Annotation)用于修饰其他的Annotation定义。
在 java.lang.annotation
下面
@Retention
@Retention 用于修饰 Annotation 保留多长时间,
包含一个 RetentionPolicy
类型的成员。有下面三个值:
- SOURCE: 只保留在源代码中, 编译器直接丢弃这种Annotation
- CLASS : 编译器记录在class文件中,运行时,JVM不可获取Annotation信息, 这是默认值
- RUNTIME: 编译器记录在 class 文件中,运行时,JVM也可获取Annotation信息,通过反射获取该Annotation信息。
@Retention(value = RetentionPolicy.RUNTIME)
public @interface OverTest{}
通过 value = RetentionPolicy.RUNTIME
方式指定, 当Annotation的成员变量是 value 时, 可以直接写变量, 不用 valude=
@Retention(RetentionPolicy.RUNTIME)
public @interface OverTest{}
@Target
@Target 用于指定修饰的Annotation的目标, 可以修饰哪些程序单元,包含一个value成员变量, 使用 ElementType 类型
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, //类,接口, Annotation, 枚举
/** Field declaration (includes enum constants) */
FIELD, //成员变量
/** Method declaration */
METHOD, //方法
/** Formal parameter declaration */
PARAMETER, //参数
/** Constructor declaration */
CONSTRUCTOR, //构造器
/** Local variable declaration */
LOCAL_VARIABLE, //局部变量
/** Annotation type declaration */
ANNOTATION_TYPE, //注解
/** Package declaration */
PACKAGE, //包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
//可以在任何用到类型的地方使用
//比如 创建对象(new), 类型转换, 使用 implements实现, 使用throws抛出异常
TYPE_USE
}
@Target(ElementType.FIELD)
public @interface OverTest{}
@Documented
@Documented 修饰的Annotation类将被 Javadoc 工具提取成文档。所有使用该Annotation修饰的程序元素的API文档中将会包含Annotation说明。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface OverTest{}
@Inherited
@Inherited修饰的Annotation具有继承性, 如果某个类使用该注解,则子类也默认使用该注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface OverTest{}
下面使用 @OverTest
@OverTest
class Base{}
class OverTest2 extends Base {
public static void main(String[] args){
System.out.println(OverTest2.class.isAnnotationPresent(OverTest.class));
}
}
//Output
true
@Repeatable
@Repeatable 表明可重复注解,默认是不可重复的。
是容器的简化, 必须是 @Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(UsTags.class)
@interface UsTag{
String name() default "hi";
int age();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface UsTags{
UsTag[] value();
}
@UsTag(age = 2)
@UsTag(name = "san", age = 3)
public class RunTests {
public static void main(String[] args) throws ClassNotFoundException{
UsTag[] tags = RunTests.class.getDeclaredAnnotationsByType(UsTag.class);
for (UsTag tag : tags){
System.out.println(tag.name() + " age : " + tag.age());
}
UsTags con = RunTests.class.getDeclaredAnnotation(UsTags.class);
System.out.println(con);
}
}
//Output
hi age : 2
san age : 3
@testCode.UsTags(value=[@testCode.UsTag(name=hi, age=2), @testCode.UsTag(name=san, age=3)])
自定义注解
定义新的注解使用关键字 @interface
. 类似于接口。
public @interface OverTest{
String name();
int age();
}
@OverTest(name = "a", age = 1)
class Base{}
还可设置默认值:
@Inherited
public @interface OverTest{
String name() default "a";
int age() default 2;
}
//有默认值,不用指定
@OverTest
class Base{}
标记Annotation: 不包含成员变量的Annotation, 如 @Override, @Test 等
元数据Annotation: 包含成员变量。
提取Annotation信息
使用注解修饰类,方法等成员后,需要由开发者提供相应的工具提取并处理Annotation信息。
使用 Annotation接口 代表程序元素前面的注解,是所有注解的父接口。
java.lang.reflect
包下有 AnnotatedElement
接口, 代表程序中可以接受注解的程序元素, 主要有如下实现类:
- Class : 类定义
- Constructor: 构造器定义
- Field: 成员变量
- Method: 方法定义
- Package: 包定义
只有当定义Annotation时,使用了@Retention(RetentionPolicy.RUNTIME)
,运行时可见,JVM才会读取。
AnnotatedElement 是 所有程序元素(Class,Method等)的父接口,所以程序通过反射获取AnnotatedElement 对象后, 通过下面方法访问 Annotation信息。 - getAnnotation() : 返回指定的注解
- getDeclaredAnnotation(): 获取直接修饰该程序元素,指定类型的Annotation。
- getAnnotations(): 获取存在的所有注解
- isAnnotationPresent(): 判断注解是否存在
- getAnnotationsByType(): 由于重复注解, 返回指定的多个注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Testable{
}
class MyTestF {
@Testable
public static void m1(){
}
public static void m2(){
}
@Testable
public static void m3(){
throw new RuntimeException("fail");
}
}
class ProcessTest{
public static void process(String clazz) throws ClassNotFoundException{
int passed = 0;
int failed = 0;
for (Method method : MyTestF.class.getMethods()){
if(method.isAnnotationPresent(Testable.class)){
try {
method.invoke(null);
passed++;
}
catch (Exception e){
System.out.println( method + "run fail" + e.getCause());
failed ++;
}
}
}
System.out.println("success : " + passed + ", failed : " + failed);
}
}
public class RunTests {
public static void main(String[] args) throws ClassNotFoundException{
ProcessTest.process("MyTestF");
}
}
//Output
public static void testCode.MyTestF.m3()run failjava.lang.RuntimeException: fail
success : 1, failed : 1
APT
APT(Annotation Processing Tool)是一种注解处理工具, 对源代码文件进行检测,根据Annotation生成额外的源文件和其他文件。
在编译期,通过注解生成附属文件和源代码,
javac 编译时,使用 -processor 选项,可以指定处理的Annotation处理器。
Annotation处理器需要实现 下面接口。
package javax.annotation.processing;
public interface Processor {
}