Java注解Annotation

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 {
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值