Java 注解

1. 简介

从JDK5开始,Java开始了对元数据(MetaData)的支持,也就是Annotation(注解),这些标记可以在编译、类加载、运行时被读取。注解可以修饰包,类,构造器,方法,成员变量,参数,局部变量。为了使注解起作用,必须使用APT(注解处理工具)来访问和处理。

2. 基本注解

  • @Override 强制一个子类必须重写父类的方法,保证父类要包含一个被该方法重写的方法,否则会编译出错
public class Fruit {
    public void info() {
        System.out.println("水果");
    }
}

class Apple extends Fruit {
    //只能修饰方法
    @Override
    public void info() {
        System.out.println("苹果");
    }
}
  • @Deprecated表示某个程序元素(类,方法,接口)已过时,当使用这些元素时,编译器会给出警告
  • @SuppressWarnings 表示被该Annotation修饰的元素取消显示指定的编译器警告,@SuppressWarnings会一直作用于该元素的所有子元素。
    @SuppressWarnings(value="unchecked")
    public static void main(String[] args) {
        List<String> list = new ArrayList(); 
        //会提示Unchecked Assignment
    }
  • @SafeVarargs 抑制引发警告(修饰方法或构造器)
//堆污染:当把一个不带泛型的对象赋给一个带泛型的变量,就可能会发生堆污染
List l = new ArrayList<Number>();
List<String> ls = l;       // unchecked warning
l.add(0, new Integer(42)); // another unchecked warning
String s = ls.get(0);      // ClassCastException is thrown
/*
堆污染发生在静态类型为List<Number>的List对象l被分配给另一个具有不同静态类型List<String>的List对象ls时。由于类型擦除的缘故,类型List<Number> 和 List<String>都会变为List。这样,编译器允许对象l以原始类型List的方式被分配给对象ls。
*/
  • @FunctionalInterface 如果接口中只有一个抽象方法(可以包含多个默认访问或static方法),该接口就是函数式接口
@FunctionalInterface
public interface FunInterface {

    static void foo() {
        System.out.println("foo方法");
    }

    default void bar() {
        System.out.println("bar方法");
    }
    void test();
}

3. 元注解

用来修饰注解

  • @Retention 用来指定被修饰的注解保留多长时间
//RetentionPolicy.CLASS 把注解记录在class文件中
//RetentionPolicy.RUNTIME 记录在class文件中,并且可以通过反射来获取注解信息
//RetentionPolicy.SOURCE 只保留在源代码中,编译器直接丢掉

@Retention(value=RetentionPolicy.RUNTIME)
public @interface Testable {

}
  • @Target 用于指定被修饰的注解能用于修饰哪些程序单元
//ElementType.ANNOTATION_TYPE 只能修饰注解
//ElementType.CONSTRUCTOR 只能修饰构造器
//ElementType.FIELD 只能修饰成员变量
//ElementType.METHOD 只能修饰方法
//ElementType.TYPE 可以修饰类,接口(包括注解类型)或枚举
  • @Documented 用于指定被该注解修饰的注解将被javadoc工具提取成文档
  • @Inherited 指定被该注解修饰的注解具有继承性

4. 自定义注解

//在默认情况下,注解可修饰任何元素,包括类,接口,方法
public @interface Test {

}

//注解继承了Annotation接口
public @interface MyTag {
    //注解的成员变量
    String name(); // String name() default "AA";
    int age();     // int age() default 0;
}

5. 提取注解信息

通过反射来获取注解信息,java.lang.reflect包下的AnnotatedElement接口,该接口代表程序中可以接受注解的元素,其中有Class, Constructor, Field, Method等

Annotation[] arrays = Class.forName("Test").getMethod("info").getAnnotations();
for(Annotation an : arrays) {
    System.out.println(an);
}

6. 重复注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(FkTags.class)
public @interface FkTag {
    String name() default "AA";
    int age();
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FkTags {
    FkTag[] value();
}

@FkTag(name="FF", age=4)
@FkTag(age=8)
public class FkTest {

    public static void main(String[] args) {
        Class<FkTest> clazz = FkTest.class;
        FkTag[] tags = clazz.getDeclaredAnnotationsByType(FkTag.class);
        for(FkTag tag : tags) {
            System.out.println(tag.name() + " -> " + tag.age());
        }

        FkTags container = clazz.getDeclaredAnnotation(FkTags.class);
        System.out.println(container);
    }
}

你可能感兴趣的:(java)