Java内置注解

注解(annotation)自 Java 1.5 开始引入,也被称为元数据。

Java内置了几种注解(Java 注解深入理解

  • @Override:表示当前的方法定义将覆盖超类中的方法。注解目标为方法。源文件有效
  • @Deprecated:表示已被弃用,将在未来的某个版本移除。注解目标为类、成员变量、方法、方法参数、构造函数、局部变量和包。运行期有效
  • @SuppressWarnings:关闭编译器警告信息。注解目标为类、成员变量、方法、方法参数、构造函数和局部变量。源文件有效
  • @SafeVarargs(java 1.7新增):参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。注解目标为构造器和方法。运行期有效
  • @FunctionalInterface(java 1.8新增):函数式接口注解。注解目标为类、接口(包括注解类型)或enum声明。运行期有效

自定义注解

除了内置注解外,Java我们通过四种元注解(meta-annotation)建立自己的注解。元注解的作用就是负责注解其他注解。

  • @Target:表示注解可以用于什么地方。参数ElementType,包括:
    • CONSTRUCTOR:构造器声明
    • FIELD:域声明
    • LOCAL_VARIABLE:局部变量声明
    • METHOD:方法声明
    • PACKAGE:包声明
    • PARAMETER:参数声明
    • TYPE:类、接口(包括注解类型)或enum声明
    • TYPE_PARAMETER(java 1.8新增): 表示该注解能写在类型变量的声明语句中
    • TYPE_USE(java 1.8新增):表示该注解能写在使用类型的任何语句中(例如声明语句、泛型和强制转换语句中的类型)
  • @Retention:表示需要在什么级别保存该注解信息。参数RetentionPolicy,包括
    • SOURCE:在源文件中有效(源文件保留)
    • CLASS:在class文件中有效(class保留)
    • RUNTIME:在运行时有效(运行时保留),可以通过反射机制读取注解的信息
  • @Documented:将此注解包含在Javadoc中
  • @Inherited:允许子类继承父类中的注解
  • @Repeatable(java 1.8新增):可重复

当注解未指定Target值时,则此注解可以用于任何元素之上;如果一个注解要在运行时被成功提取,那么必须加上@Retention(RetentionPolicy.RUNTIME) 。

在实际应用中一般都会使用自己定义的注解。 适当的注解可以减少工作量,简化程序中的配置。提高效率。目前注解已经成为Spring开发的主流。

注解通过 @interface 关键字进行定义。

如下所示:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

}

注解的属性也叫做成员变量。注解的成员变量在注解的定义中以“无参方法”的形式声明只能用public或默认访问权修饰,方法名为成员变量的名字,返回值为成员变量的类型。如果只有一个参数成员,最好把参数名称设为"value"。

属性可以有默认值,默认值需要用 default 关键值指定。

注意:对于非基本元素,不能以null作为其值

示例如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    int id() default -1;
    String name() default "-1";
}

反射获取注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface methodAnnotation {
    int id() default -1;

    String name() default "null";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface classAnnotation {
    int id() default -1;

    String name() default "null";
}

@classAnnotation
class Anno {
    @methodAnnotation
    public void f(String msg) {
        System.out.println(msg);
    }
}

@Deprecated
public class AnnotationDemo extends Anno {
    @SuppressWarnings("unused")
    private static int a = 1;

    @Override
    public void f(String msg) {
        System.out.println(msg);
    }

    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
        Class<?> clazz = AnnotationDemo.class;
        // 获取类的所有注解,包含父类注解
        Annotation[] allInClass = clazz.getAnnotations();
        System.out.println("类注解(含父类):" + Arrays.toString(allInClass));
        // 获取类的所有注解,不包含父类注解
        Annotation[] allExceptFather = clazz.getDeclaredAnnotations();
        System.out.println("类注解(不含父类):" + Arrays.toString(allExceptFather));
        // 获取成员变量注解
        Field[] field = clazz.getDeclaredFields();
        Annotation[] allInField = field[0].getAnnotations();
        System.out.println("成员变量注解:" + Arrays.toString(allInField));
        // 获取方法注解
        Method[] method = clazz.getMethods();
        Annotation[] allInMethod = method[1].getAnnotations();
        System.out.println("方法注解:" + Arrays.toString(allInMethod));
        // 判断是否有Deprecated注解
        boolean specAnno = clazz.isAnnotationPresent(Deprecated.class);
        System.out.println("是否有Deprecated注解:" + specAnno);
    }
}

输出结果:

类注解(含父类):[@test.classAnnotation(name=null, id=-1), @java.lang.Deprecated()]
类注解(不含父类):[@java.lang.Deprecated()]
成员变量注解:[]
方法注解:[]
是否有Deprecated注解:true

可以看到,@Override与@SupressedWarnings因为是源文件有效而不能通过反射获取;getDeclaredAnnotations()方法不能获取父类注解。

10-04 16:01