Java 注解
Java 注解是程序源代码的元数据。在本教程中,我们将通过示例了解什么是 Java 注解、常见的注解类以及以及如何创建一个自定义注解。
在本教程中,我们将通过示例了解什么是 Java 注解、常见的注解类以及以及如何创建一个自定义注解。
Java 注解
Java 注解是程序源代码的元数据(关于数据的数据)。它们向编译器提供有关程序的附加信息。注解不影响编译程序的执行。注解的英文术语是 annotation。
使用注解以 @
开头,语法是: @AnnotationName
或 @AnnotationName()
。
我们用 @Override
注解举例说明以下:
@Override
注解用在子类方法上,用以标示该方法要覆盖父类的方法。@Override
注解是向编译器传达信息,如果子类的方法不符合覆盖方法的要求或者规则,编译器在编译时会给出错误提示。
示例 1:@Override 注解示例
class Animal {
public void displayInfo() {
System.out.println("I am an animal.");
}
}
class Dog extends Animal {
@Override
public void displayInfo() {
System.out.println("I am a dog.");
}
}
public class Main {
public static void main(String[] args) {
Animal d1 = new Dog();
d1.displayInfo();
}
}
输出
I am a dog.
在此示例中,该方法 displayInfo()
同时存在于 Animal
超类中和 Dog
子类中. 调用此方法时,将调用子类的方法而不是超类中的方法。
如果我们声明了 @Override
注解,却不是符合覆盖方法的规则,则编译器会给出错误。比如我们上 Dog
修改如下:
class Dog extends Animal {
@Override
public void displayInfo11111() {
System.out.println("I am a dog.");
}
}
则运行 javac Main.java
编译 Main.java
的时候,返回以下编译错误:
Main.java:8: 错误: 方法不会覆盖或实现超类型的方法
@Override
^
1 个错误
注解格式
注解还可以自己的参数/参数/成员。根据参数的多少,注解可分为:
- 标记注解,没有参数,相当于一个标记。
- 单参数注解
- 多参数注解
标记注解
标记注解不包含参数/参数/成员。它仅用于标记声明。
它的语法是: @AnnotationName
或 @AnnotationName()
。
由于这些注解不包含参数,因此可以排除括号。例如: @Override
。
单参数注解
单参数注解仅包含一个参数。
它的语法是: @AnnotationName(elementName = "elementValue")
如果只有一个参数,且当参数名是 value
时可以省略参数名称, 比如:
@AnnotationName(value = "elementValue")
可以简化为:
@AnnotationName("elementValue")
多参数注解
这些注解包含多个参数,参数之间以逗号分隔。
它的语法是: @AnnotationName(element1 = "value1", element2 = "value2")
当注解存在多个参数时,参数名不可以省略。
注解的位置
可以在任何声明之前/之上(上一行)使用注解。从 Java 8 开始,注解也可以放在类型之前。
1. 声明注解
如上所述,Java 注解可以放置在类、方法、接口、字段和其他程序参数声明之前。
示例 2:@SuppressWarnings 注解示例
import java.util.*;
public class Main {
@SuppressWarnings("unchecked")
static void wordsList() {
ArrayList wordList = new ArrayList<>();
// This causes an unchecked warning
wordList.add("gobeta");
System.out.println("Word list => " + wordList);
}
public static void main(String args[]) {
wordsList();
}
}
输出
Word list => [gobeta]
如果上面的程序是在不使用 @SuppressWarnings("unchecked")
注解的情况下编译的,编译器仍然会编译该程序,但会给出如下警告:
Main.java 使用未经检查或不安全的操作。
Word list => [gobeta]
我们收到警告: Main.java 使用未经检查或不安全的操作
,是因为下面的语句:
ArrayList wordList = new ArrayList<>();
这是因为我们还没有定义 ArrayList
的泛型类型。我们可以通过在尖括号 <>
内指定泛型来修复此警告。
ArrayList<String> wordList = new ArrayList<>();
2. 类型注解
在 Java 8 之前,注解只能应用于声明。现在,也可以使用类型注解。这意味着我们可以在任何使用类型的地方放置注解。
-
构造函数调用
new @Readonly ArrayList<>()
-
类型定义
@NonNull String str;
此声明指定非空
String
字符串变量要避免NullPointerException
空指针异常。@NonNull List<String> newList;
此声明指定了一个类型为
String
的非空列表。List<@NonNull String> newList;
此声明指定了一个类型为
String
的非空值列表。 -
类型转换
newStr = (@NonNull String) str;
-
扩展和实现子句
class Warning extends @Localized Message
-
抛出子句
public String readMethod() throws @Localized IOException
类型注解可以更好地分析 Java 代码并提供更强大的类型检查。
注解的分类
根据注解的目的和使用使用时机,可以按如下分类:
- 编译器指令 - 注解可用于向编译器提供指令、检测错误或抑制警告。内置的批注
@Deprecated
,@Override
,@SuppressWarnings
用于这些目的。 - 编译时指令 - 这些注解提供的编译时指令帮助软件构建工具生成代码、XML 文件等。
- 运行时指令 - 可以定义一些注解以在运行时向程序提供指令。这些注解是使用 Java 反射访问的。
Java 预定了一些编译器和编译时的指令,请查看 Java 预定义注解和 Java 元注解了解更多信息。
自定义注解
上面用到的都是系统预定义的注解,虽然这些注解能带给我们很大的帮助,但是某些场景下,我们需要自己开发注解。比如校验对象值、生成接口文档、权限控制等。
创建自定义注解的语法是:
public @interface AnnotationName {
DataType MethodName() default defaultValue;
}
说明:
@interface
用来标识一个注解类型。AnnotationName
是注解的名称,就像类名、接口名一样。MethodName
类似于接口中的抽象方法,没有实现。方法名称就是注解的参数名称。default
后面跟的是注解参数的默认值。默认值是可选的。DataType
是方法的返回类型,可以是原始、枚举、字符串、类名或这些类型的数组。
示例 3:自定义注解示例
@interface MyCustomAnnotation {
String value() default "default value";
}
public class Main {
@MyCustomAnnotation(value = "gobeta")
public void method1() {
System.out.println("Test method 1");
}
public static void main(String[] args) throws Exception {
Main obj = new Main();
obj.method1();
}
}
这个例子指示简单的展示了创建一个注解和标注注解。但是这个注解并没有实际的用途。我们创建了注解之后,一般会利用 Java 反射机制来获取这些注解信息,利用这些信息达到一些其他的目的,比如:校验对象值、生成接口文档等。这在稍后的课程中会讲解到。