Java 注解类型

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
 * why it was deprecated
 */
@Deprecated
accessModifier returnType deprecatedMethodName() { ... }

示例 1: @Deprecated 注解示例

class Main {
  /**
   * @deprecated
   * This method is deprecated and has been replaced by newMethod()
   */
  @Deprecated
  public static void deprecatedMethod() { 
    System.out.println("Deprecated method"); 
  } 

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

输出

Deprecated method

2. @Override

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

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

示例 2: @Override 注解示例

class Animal {

  // overridden method
  public void display(){
    System.out.println("I am an animal");
  }
}

class Dog extends Animal {

  // overriding method
  @Override
  public void display(){
    System.out.println("I am a dog");
  }

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

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

输出

I am a dog

在这个例子中,通过创建 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("Deprecated method"); 
  } 
  
  @SuppressWarnings("deprecated")
  public static void main(String args[]) {
    Main depObj = new Main();
    depObj. deprecatedMethod();
  }
}

输出

Deprecated method

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


4. @SafeVarargs

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

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

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

示例 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);
  }
}

警告

Type safety: Potential heap pollution via varargs parameter lists
Type safety: A generic array of List<String> is created for a varargs 
 parameter

输出

Note: Main.java uses unchecked or unsafe operations.
[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

Java 8 首次引入了 @FunctionalInterface 注解。此注解表示声明它的类型是一个函数式接口。函数式接口只能有一个抽象方法。

示例 5: @FunctionalInterface 注解示例

@FunctionalInterface
public interface MyFuncInterface{
  public void firstMethod(); // this is an abstract method
}

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

@FunctionalInterface
public interface MyFuncInterface{
  public void firstMethod(); // this is an abstract method
  public void secondMethod(); // this throws compile error
}

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

Unexpected @FunctionalInterface annotation
@FunctionalInterface ^ MyFuncInterface is not a functional interface
multiple non-overriding abstract methods found in interface MyFuncInterface

使用 @FunctionalInterface 注解不是强制性的。编译器会将任何满足函数式接口定义的接口视为函数式接口。

我们使用此注解来确保函数式接口只有一个抽象方法。

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

@FunctionalInterface
public interface MyFuncInterface{
  public void firstMethod(); // this is an abstract method
  default void secondMethod() { ... } 
  default void thirdMethod() { ... } 
}

自定义注解

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

其语法为

[Access Specifier] @interface<AnnotationName> {         
  DataType <Method Name>() [default value];
}

以下是您需要了解的关于自定义注解的内容

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

示例 6: 自定义注解示例

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

class Main {
  @MyCustomAnnotation(value = "programiz")
  public void method1() {
    System.out.println("Test method 1");
  }

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

输出

Test method 1

元注解

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

1. @Retention

@Retention 注解指定了注解的可用级别。

其语法为

@Retention(RetentionPolicy)

有 3 种保留策略

  • 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() 方法。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战