1、什么是注解?
注解是放在java源码的类、方法、字段、参数上的一种注释。 注释会被编译器直接忽略,注解可直接被编译器打包进class文件,因此,注解是一种用作标注的元数据。 无论是哪一种注解,本质上都一种数据类型,是一种接口类型。到 Java 8 为止 Java SE 提供了 11 个内置注解。其中有 5 个是基本注解,它们来自于 java.lang 包。有 6 个是元注解,它们来自于 java.lang.annotation 包,自定义注解会用到元注解。
提示:元注解就是负责注解其他的注解。
基本注解包括:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs 和 @FunctionalInterface。
Java 5 定义了 4 个元注解,分别是 @Documented、@Target、@Retention 和 @Inherited。Java 8 又增加了 @Repeatable 和 @Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到。
2、注解的分类
由编译器使用的注解,@Override,@SuppressWarning(忽略警告),这类注解不会被编译器进入.class文件,编译后就被编译器扔掉;有工具处理.class文件使用的注解程序在运行时能够读取的注解,它们被加载后存放在JVM中,这也是最常用的注解,例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。3.基本注解
@Override注解:用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。 @SuppressWarning:指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。 @Deprecated:用于注解类、接口、成员方法、成员变量等,用于表示某个元素已过时,当其他程序调用过时的元素时,编译器会报错。 @SafeVarargs:抑制编译器警告,该注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。 @FunctionnalInterface:用来指定某个接口必须是函数式接口,作用只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。
4.元注解
一般用于自定义注解中,用于注解自定义注解。 @Target:用于指定一个注解的使用范围,下表是其参数value的常用枚举常量。
名称说明CONSTRUCTOR用于构造方法FIELD 用于成员变量(包括枚举常量)LOCAL_VARIABLE用于局部变量METHOD用于方法PACKAGE用于包PARAMETER用于类型参数(JDK 1.8新增)TYPE 用于类、接口(包括注解类型)或 enum 声明@Retention:用于描述注解的生命周期 SOURCE:在源文件中有效(即源文件保留) CLASS:在 class 文件中有效(即 class 保留) RUNTIME:在运行时有效(即运行时保留)
@Inherited:用于指定某个注解可以被继承,即如果父类被@Inherhited修饰的注解,则子类将将自动具有该注解。
5.定义注解 Java使用@Interface语法来自定义注解,格式如下:
@Inherited @Repeatable(Reports.class) //定义该注解是否可重复 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Report{ int type() default 0; String level() default "info"; String value default ""; }示例: 注解如何使用,完全由程序自己决定。例如,JUnit是一个测试框架,它会自动运行所有标记为@Test的方法。
我们来看一个@Range注解,我们希望用它来定义一个String字段的规则:字段长度满足@Range的参数定义:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Range { int min() default 0; int max() default 255; }在某个JavaBean中,我们可以使用该注解:
public class Person { @Range(min=1, max=20) public String name; @Range(max=10) public String city; }但是,定义了注解,本身对程序逻辑没有任何影响。我们必须自己编写代码来使用注解。这里,我们编写一个Person实例的检查方法,它可以检查Person实例的String字段长度是否满足@Range的定义:
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException { // 遍历所有Field: for (Field field : person.getClass().getFields()) { // 获取Field定义的@Range: Range range = field.getAnnotation(Range.class); // 如果@Range存在: if (range != null) { // 获取Field的值: Object value = field.get(person); // 如果值是String: if (value instanceof String) { String s = (String) value; // 判断值是否满足@Range的min/max: if (s.length() < range.min() || s.length() > range.max()) { throw new IllegalArgumentException("Invalid field: " + field.getName()); } } } } }这样一来,我们通过@Range注解,配合check()方法,就可以完成Person实例的检查。注意检查逻辑完全是我们自己编写的,JVM不会自动给注解添加任何额外的逻辑。