跳到主要内容

Java 注解类型

提示
  1. Java注解的概念和分类:Java注解是源代码中的元数据,用于为程序元素(如类、方法或变量)提供额外信息。主要分为预定义注解、自定义注解和元注解。
  2. 预定义注解的使用:预定义注解如 @Deprecated, @Override, @SuppressWarnings, @SafeVarargs, @FunctionalInterface 用于标记过时的代码、覆盖父类方法、抑制编译器警告、标记安全的可变参数方法和定义函数式接口。
  3. 元注解的作用:元注解,如 @Retention, @Documented, @Target, @Inherited, @Repeatable,用于定义其他注解的行为。例如,它们可以指定注解保留策略、是否包含在文档中、适用的程序元素类型、是否可继承和是否可以重复应用。

Java 注解是我们程序源代码的元数据(关于数据的数据)。Java SE 提供了几种预定义的注解。此外,我们还可以根据需要创建自定义注解。

如果您不了解什么是注解,请访问 Java 注解 教程。

这些注解可以分为以下几类:

  1. 预定义注解

    • @Deprecated
    • @Override
    • @SuppressWarnings
    • @SafeVarargs
    • @FunctionalInterface
  2. 自定义注解

  3. 元注解

    • @Retention
    • @Documented
    • @Target
    • @Inherited
    • @Repeatable

预定义注解类型

1. @Deprecated

@Deprecated 注解是一种标记注解,表明元素(类、方法、字段等)已经被弃用,并且已被较新的元素取代。

其语法为:

@Deprecated
accessModifier returnType deprecatedMethodName() { ... }

当程序使用已声明为弃用的元素时,编译器会生成警告。

我们使用 Javadoc 的 @deprecated 标签来记录弃用的元素。

/**
* @deprecated
* 弃用的原因
*/
@Deprecated
accessModifier returnType deprecatedMethodName() { ... }

示例 1:@Deprecated 注解示例

class Main {
/**
* @deprecated
* 此方法已弃用,已被 newMethod() 替代
*/
@Deprecated
public static void deprecatedMethod() {
System.out.println("弃用的方法");
}

public static void main(String args[]) {
deprecatedMethod();
}
}

输出

弃用的方法

2. @Override

@Override 注解指定子类的方法覆盖了具有相同方法名称、返回类型和参数列表的超类方法。

覆盖方法时使用 @Override 不是强制性的。但是,如果使用它,当覆盖方法时出现错误(例如参数类型错误)时,编译器会给出错误提示。

示例 2:@Override 注解示例

class Animal {

// 被覆盖的方法
public void display(){
System.out.println("我是一只动物");
}
}

class Dog extends Animal {

// 覆盖的方法
@Override
public void display(){
System.out.println("我是一只狗");
}

public void printMessage(){
display();
}
}

class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.printMessage();
}
}

**输出 **

我是一只狗

在此示例中,通过创建 Dog 类的对象 dog1,我们可以调用它的方法 printMessage(),然后执行 display() 语句。 由于 display() 方法在两个类中都有定义,所以子类 Dog 的方法覆盖了超类 Animal 的方法。因此,调用的是子类的 display() 方法。

3. @SuppressWarnings

顾名思义,@SuppressWarnings 注解指示编译器在程序执行时抑制生成的警告。

我们可以指定要抑制的警告类型。可以抑制的警告是编译器特定的,但大致分为两类:弃用(deprecation)未检查(unchecked)

要抑制特定类别的警告,我们使用:

@SuppressWarnings("warningCategory")

例如,

@SuppressWarnings("deprecated")

要同时抑制多个类别的警告,我们使用:

@SuppressWarnings({"warningCategory1", "warningCategory2"})

例如,

@SuppressWarnings({"deprecated", "unchecked"})

类别 deprecated 指示编译器在我们使用已弃用的元素时抑制警告。

类别 unchecked 指示编译器在我们使用原始类型时抑制警告。

而未定义的警告会被忽略。例如,

@SuppressWarnings("someundefinedwarning")

示例 3:@SuppressWarnings 注解示例

class Main {
@Deprecated
public static void deprecatedMethod() {
System.out.println("已弃用的方法");
}

@SuppressWarnings("deprecated")
public static void main(String args[]) {
Main depObj = new Main();
depObj. deprecatedMethod();
}
}

输出

已弃用的方法

这里,deprecatedMethod() 已被标记为已弃用,如果使用将产生编译器警告。通过使用 @SuppressWarnings("deprecated") 注解,我们可以避免编译器警告。

4. @SafeVarargs

@SafeVarargs 注解声明标注的方法或构造函数不会对其可变参数(变量数量的参数)执行不安全操作。

我们只能在不能被覆盖的方法或构造函数上使用此注解。这是因为覆盖它们的方法可能会执行不安全的操作。

在 Java 9 之前,我们只能在 final 或 static 方法上使用此注解,因为它们不能被覆盖。现在我们也可以在私有方法上使用此注解。

示例 4:@SafeVarargs 注解示例

import java.util.*;

class Main {

private void displayList(List<String>... lists) {
for (List<String> list : lists) {
System.out.println(list);
}
}

public static void main(String args[]) {
Main obj = new Main();

List<String> universityList = Arrays.asList("Tribhuvan University", "Kathmandu University");
obj.displayList(universityList);

List<String> programmingLanguages = Arrays.asList("Java", "C");
obj.displayList(universityList, programmingLanguages);
}
}


警告

类型安全:通过可变参数 lists 可能会产生堆污染
类型安全:为可变参数创建了一个 List<String> 的泛型数组

输出

注意:Main.java 使用了未检查或不安全的操作。
[Tribhuvan University, Kathmandu University]
[Tribhuvan University, Kathmandu University]
[Java, C]

这里,List ... lists 指定了一个类型为 List 的可变长度参数。这意味着 displayList() 方法可以有零个或多个参数。

上述程序编译时没有错误,但如果不使用 @SafeVarargs 注解会给出警告。

当我们在上面的示例中使用 @SafeVarargs 注解时,

@SafeVarargs
private void displayList(List<String>... lists) { ... }

我们得到相同的输出,但没有任何警告。使用此注解时,未检查的警告也会被抑制。

5. @FunctionalInterface

Java8 首次引入了 @FunctionalInterface 注解。这个注解表示其所用的类型声明是一个功能接口。功能接口只能有一个抽象方法。

示例 5:@FunctionalInterface 注解示例

@FunctionalInterface
public interface MyFuncInterface{
public void firstMethod(); // 这是一个抽象方法
}

如果我们添加另一个抽象方法,比如说

@FunctionalInterface
public interface MyFuncInterface{
public void firstMethod(); // 这是一个抽象方法
public void secondMethod(); // 这会导致编译错误
}

现在,当我们运行程序时,会得到以下警告:

意外的 @FunctionalInterface 注解
@FunctionalInterface ^ MyFuncInterface 不是功能接口
在接口 MyFuncInterface 中找到多个非覆盖的抽象方法

使用 @FunctionalInterface 注解并不是强制性的。任何符合功能接口定义的接口,编译器都会将其视为功能接口。

我们使用这个注解是为了确保功能接口只有一个抽象方法。

然而,它可以有任意数量的默认和静态方法,因为它们有实现。

@FunctionalInterface
public interface MyFuncInterface{
public void firstMethod(); // 这是一个抽象方法
default void secondMethod() { ... }
default void thirdMethod() { ... }
}

自定义注解

我们也可以创建自己的自定义注解。

其语法为:

[访问修饰符] @interface<AnnotationName> {
DataType <Method Name>() [default value];
}

以下是您需要了解的关于自定义注解的信息:

  • 可以通过使用 @interface 后跟注解名称来创建注解。
  • 注解可以有看起来像方法的元素,但它们没有实现。
  • 默认值是可选的。参数不能有 null 值。
  • 方法的返回类型可以是原始类型、枚举、字符串、类名或这些类型的数组。

示例 6:自定义注解示例

@interface MyCustomAnnotation {
String value() default "默认值";
}

class Main {
@MyCustomAnnotation(value = "mashangxue123")
public void method1() {
System.out.println("测试方法 1");
}

public static void main(String[] args) throws Exception {
Main obj = new Main();
obj.method1();
}
}


输出

测试方法 1

元注解

元注解是应用于其他注解的注解。

1. @Retention

@Retention 注解指定注解将保留到哪个级别。

其语法为:

@Retention(RetentionPolicy)

保留策略(Retention Policy)有三种类型:

  • RetentionPolicy.SOURCE - 注解仅在源码级别可用,编译器会忽略它。
  • RetentionPolicy.CLASS - 注解在编译时对编译器可用,但 Java 虚拟机(JVM)会忽略它。
  • RetentionPolicy.RUNTIME - 注解对 JVM 可用。

例如,

@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation{ ... }

2. @Documented

默认情况下,自定义注解不包含在 官方 Java 文档中。要在 Javadoc 文档中包含我们的注解,我们使用 @Documented 注解。

例如,

@Documented
public @interface MyCustomAnnotation{ ... }

3. @Target

我们可以使用 @Target 注解限制注解仅适用于特定目标。

其语法为:

@Target(ElementType)

ElementType 可以是以下类型之一:

元素类型目标
ElementType.ANNOTATION_TYPE注解类型
ElementType.CONSTRUCTOR构造函数
ElementType.FIELD字段
ElementType.LOCAL_VARIABLE局部变量
ElementType.METHOD方法
ElementType.PACKAGE
ElementType.PARAMETER参数
ElementType.TYPE类中的任何元素

例如,

@Target(ElementType.METHOD)
public @interface MyCustomAnnotation{ ... }

在这个示例中,我们将这个注解的使用限制为仅方法。

注意: 如果未定义目标类型,则注解可以用于任何元素。

4. @Inherited

默认情况下,注解类型不能从超类继承。然而,如果我们需要从超类继承注解到子类,我们使用 @Inherited 注解。

其语法为:

@Inherited

例如,

@Inherited
public @interface MyCustomAnnotation { ... }

@MyCustomAnnotation
public class ParentClass{ ... }

public class ChildClass extends ParentClass { ... }

5. @Repeatable

@Repeatable 标记的注解可以多次应用于同一声明。

@Repeatable(Universities.class)
public @interface University {
String name();
}

@Repeatable 注解中定义的值是容器注解。容器注解有一个数组类型的变量 value,用于存放上述可重复注解的值。这里,Universities 是包含注解类型。

public @interface Universities {
University[] value();
}

现在,@University 注解可以在同一声明上多次使用。

@University(name = "TU")
@University(name = "KU")
private String uniName;

如果我们需要检索注解数据,可以使用 反射 API

要检索注解值,我们使用反射 API 中定义的 getAnnotationsByType()getAnnotations() 方法。