Java 基础教程

Java 流程控制

Java 数组

Java 面向对象(I)

Java 面向对象(II)

Java 面向对象(III)

Java 异常处理

Java 列表(List)

Java Queue(队列)

Java Map集合

Java Set集合

Java 输入输出(I/O)

Java Reader/Writer

Java 其他主题

Java 注解类型

在本教程中,我们将借助示例学习不同类型的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
 * 为什么它被弃用
 */
@Deprecated
accessModifier returnType deprecatedMethodName() { ... }

示例1:@Deprecated 注解示例

class Main {
  /**
   * @deprecated
   * 此方法已弃用,并已由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 {

  //重写方法
  public void display(){
    System.out.println("I am an animal");
  }
}

class Dog extends Animal {

  //重写方法
  @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子类的display()方法将覆盖超类Animal的display()方法。因此,子类的display()被调用。

3. @SuppressWarnings

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

我们可以指定要取消的警告类型。可以禁止的警告是特定于编译器的,但警告分为两类:弃用未检查

为了禁止显示特定类别的警告,我们刻意使用:

@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方法上使用此注解,因为它们不能被重写。现在,我们也可以将此注解用于私有方法。

示例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 ... list指定类型为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(); //这是一个抽象的方法
}

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

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

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

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(); //这是一种抽象方法
  default void secondMethod() { ... } 
  default void thirdMethod() { ... } 
}

自定义注解

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

其语法为:

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

这是您需要了解的有关自定义注解的信息:

  • 注解可以通过使用@interface后面跟注解名称来创建。

  • 注解可以具有看起来像方法的元素,但是它们没有实现。

  • 默认值是可选的。参数不能为空值。

  • 方法的返回类型可以是原始,枚举,字符串,类名或这些类型的数组。

示例6:自定义注解示例

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

class Main {
  @MyCustomAnnotation(value = "nhooo")
  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)

有三种类型:

  • 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可有以下几种类型之一:

元素类型Target
ElementType.ANNOTATION_TYPE注解类型
ElementType.CONSTRUCTOR构造函数
ElementType.FIELD字段
ElementType.LOCAL_VARIABLE局部变量
ElementType.METHOD方法
ElementType.PACKAGE
ElementType.PARAMETER参数
ElementType.TYPE用于描述类、接口(包括注解类型) 或enum声明

例如,

@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中定义的getAnnotationsByType()或getAnnotations()方法。