注解(Annotation)是Java 5 版本之后可以在源代码中嵌入的一种补充信息,是 Java 平台中非常重要的一部分。这次的内容如下
注解本质上都是一种数据类型,是一种接口类型。到 Java 8 为止 Java SE 提供了 11 个内置注解( 5 个是基本注解,来自于 java.lang 包。 6 个是元注解,它们来自于 java.lang.annotation 包)
注:自定义注解会用到元注解(负责注解其他的注解)。
1)注解都是 @ 符号开头的,如重写 @Override 注解。 2)同 Class 和 Interface 一样,注解也属于一种类型。 3)默认情况下,注解可以在程序的任何地方使用,通常用于修饰类、接口、方法和变量等。 4)注解并不能改变程序的运行结果,也不会影响程序运行的性能(有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息) 5)注解可以元数据这个词描述(即一种描述数据的数据),因此可以说注解就是源代码的元数据。
1.生成帮助文档。这是最常见的,也是 Java 最早提供的注解。常用的有 @see、@param 和 @return 等;
2.跟踪代码依赖性,实现替代配置文件功能。比较常见的是 Spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
3.在编译时进行格式检查。如把 @Override 注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。
用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。
用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。
指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。抑制警告的关键字有很多,有兴趣的可自行去搜索查看。 1)抑制单类型的警告
@SuppressWarnings(“unchecked”)
2)抑制多类型的警告
@SuppressWarnings(“unchecked”,“rawtypes”)
3)抑制所有类型的警告
@SuppressWarnings(“unchecked”)
可用来抑制编译器警告。
注:@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。
用来指定某个接口必须是函数式接口,且@FunInterface 只能修饰接口,不能修饰其它程序元素。
注:函数式接口是为 Java 8 的 Lambda 表达式准备的,Java 8 允许使用 Lambda 表达式创建函数式接口的实例,因此 Java 8 专门增加了 @FunctionalInterface。
1)@Override注解
public class AnnotationDemo {
private String name="";
private int age;
//...
//@Override注解: 告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。
@Override
public String t0String(){ // toString写错,@Override下面会有红色波浪线提示(Method does not override method from its superclass)
return "Person [name="+name+",age="+age+"]";
}
}
2)@Deprecated注解 先创建一个Person类
@Deprecated
public class Person {
@Deprecated
protected String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Deprecated
public void setNameAndAge(String name,int age){
this.name=name;
this.age=age;
}
@Override
public String toString(){
return "Person[name"+name+",age="+age+"]";
}
}
再创建测试类
注:由于类 Person、成员变量 name 和setNameAndAge 方法被 @Deprecated 注解,这些被注解的 API 都会被画上删除线。调用这些 API 代码也会有删除线。
Java 9 @Deprecated 注解新增属性:
forRemoval:指定该 API 在将来是否会被删除。
since:指定该 API 从哪个版本被标记为过时。
如下
class Test {
// since属性指定从哪个版本开始被标记成过时,forRemoval指定该API将来会被删除
@Deprecated(since = "9", forRemoval = true)
public void print() {
System.out.println("你好,李四!");
}
}
public class DeprecatedTest {
public static void main(String[] args) {
// 下面使用info()方法时将会被编译器警告
new Test().print();
}
}
3)@SuppressWarnings 注解 调用2)中的Person类创建如下测试类,由于使用了@Deprecated注解表示API已经过时了,所以会产生删除线警告,但这里因为使用了 @SuppressWarnings 注解,就没有警告了(取消了警告)
public class PersonTest {
@SuppressWarnings({ "deprecation" })
public static void main(String[] args) {
Person p = new Person();
p.setNameAndAge("zhangsan",22);
p.name="lishi";
}
}
4)@SafeVarargs 注解
public class Test {
public static void main(String[] args) {
// 传递可变参数,参数是泛型集合
display(10, 20, 30);
// 传递可变参数,参数是非泛型集合
display("10", 20, 30); // 会有编译警告
}
public static <T> void display(T... array) { // 可变参数形式:方法名(类型...数组名)
//方法display()提示Annotate as '@SafeVararg'(即要在上一行添加@SafeVararg注解)
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
}
运行结果
java.lang.Integer:10
java.lang.Integer:20
java.lang.Integer:30
java.lang.String:10
java.lang.Integer:20
java.lang.Integer:30
5)@FunctionalInterface注解
// 使用@FunctionalInterface修饰函数式接口。
public interface FunInterface {
static void print() { // 静态方法
System.out.println("我的个人网站:https://zhengyquan.gitee.io/");
}
default void show() {
System.out.println("我的腾讯云社区首页:https://cloud.tencent.com/developer/user/10491946");
}
void test(); // 只定义一个抽象方法
}
// @FunctionalInterface 注解的作用只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。
注:@FunctionalInterface 注解主要是帮助程序员避免一些低级错误,例如,在上面的 FunInterface 接口中再增加一个抽象方法 abc(),编译程序时将出现如下错误提示:“@FunctionInterface”批注无效;FunInterface不是functional接口。
元注解负责对其它注解进行说明的注解,自定义注解时可以使用元注解
@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。
默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组, ElementType 常用的枚举常量。
@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量。如下
SOURCE:在源文件中有效(即源文件保留)
CLASS:在 class 文件中有效(即 class 保留)
RUNTIME:在运行时有效(即运行时保留)
@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。
注:如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。
@Repeatable 注解是 Java 8 新增加的,允许在相同的程序元素中重复注解,需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。
了解:Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。@Native 注解不常使用,了解即可。
@interface 关键字实现,与定义接口非常类似,如
public @interface Test {}
定义注解和定义类相似,注解前面的访问修饰符和类一样有两种,分别是公有访问权限(public)和默认访问权限(默认不写)。
一个源程序文件中可以声明多个注解,但只能有一个是公有访问权限的注解(同定义类,只能有一个public修饰的类,其他类可以有多个)。且源程序文件命名和公有访问权限的注解名一致(同定义类,源文件名和类名相同)
1.标记注解:没有定义成员变量的注解类型(仅利用自身的存在与否来提供信息)
2.元数据注解:包含成员变量的注解,由于可以接受更多的元数据,也被称为元数据注解。
示例 1)不包含任何成员变量的注解称为标记注解
// 定义一个标记注解
public @interface Test {
}
2)注解中可以定义成员变量
public @interface MyTag {
// 定义带两个成员变量的注解
// 注解中的成员变量以方法的形式来定义
String name();
int age();
// 成员变量也可以有访问权限修饰符,但是只能有公有权限和默认权限。
}
3)如果在注解里定义了成员变量,就需要为成员变量赋值
public class Test {
// 使用带成员变量的注解时,需要为成员变量赋值
@MyTag(name="xx", age=6)
public void info() {
...
}
...
}
4)注解中的成员变量也可以有默认值,可使用 default 关键字
public @interface MyTag {
// 定义了两个成员变量的注解
// 使用default为两个成员变量指定初始值
String name() default "李四";
int age() default 18;
}
5)如果为注解的成员变量指定了默认值,则使用该注解时直接使用默认值。
也可在使用 MyTag 注解时为成员变量指定值,指定后默认值不起作用
public class Test {
// 使用带成员变量的注解
// MyTag注释的成员变量有默认值,所以可以不为它的成员变量赋值
@MyTag
public void info() {
...
}
...
}