原文: https://beginnersbook.com/2014/09/java-annotations/

Java 注解允许我们将元数据信息添加到我们的源代码中,尽管它们不是程序本身的一部分。注解从 JDK 5 添加到 java 中。注解对它们注解的代码的操作没有直接影响(即它不影响程序的执行)。

在本教程中,我们将介绍以下主题:注解的使用,如何应用注解,Java 中可用的预定义注解类型以及如何创建自定义注解。

注解有什么用?

1)编译器指令:Java 中有三种内置注解(@Deprecated@Override@SuppressWarnings),可用于向编译器提供某些指令。例如,@override注解用于指示编译器注解方法是否覆盖该方法。有关这些内置注解的更多信息,请参阅本文的下一部分。

2)编译时教程:注解可以为编译器提供编译时指令,软件构建工具可以进一步使用它来生成代码,XML 文件等。

3)运行时指令:我们可以定义在运行时可用的注解,我们可以使用 java 反射访问它们,并可用于在运行时向程序发出指令。我们将在稍后的同一篇文章的帮助下讨论这个问题。

注解基础知识

注解始终以符号@开头,后跟注解名称。符号@向编译器指示这是一个注解。

对于例如@Override
这里@符号表示这是一个注解,而Override是这个注解的名称。

我们可以在哪里使用注解?

注解可以应用于类,接口,方法和字段。例如,以下注解正在应用于该方法。

  1. @Override
  2. void myMethod() {
  3. //Do something
  4. }

这个注解在这里做的正是在下一节中解释的,但简单来说它指示编译器myMethod()是一个覆盖超类方法(myMethod())的重写方法。

Java 中的内置注解

Java 有三个内置注解:

  • @Override
  • @Deprecated
  • @SuppressWarnings

1)@Override

在覆盖子类中的方法时,我们应该使用此批注来标记该方法。这使得代码可读并避免维护问题,例如:在更改父类的方法签名时,必须更改子类中的签名(使用此批注的位置),否则编译器将抛出编译错误。如果您没有使用此注解,则很难跟踪。

例:

  1. public class MyParentClass {
  2. public void justaMethod() {
  3. System.out.println("Parent class method");
  4. }
  5. }
  6. public class MyChildClass extends MyParentClass {
  7. @Override
  8. public void justaMethod() {
  9. System.out.println("Child class method");
  10. }
  11. }

我相信这个例子是自我解释的。要阅读有关此注解的更多信息,请参阅以下文章: `@Override内置注解

2)@Deprecated

@Deprecated注解表示已弃用标记的元素(类,方法或字段),不应再使用。只要程序使用已经使用@Deprecated注解标记的方法,类或字段,编译器就会生成警告。不推荐使用元素时,也应使用 Javadoc @deprecated标记对其进行记录,如以下示例所示。记下@Deprecated@deprecated的大小写差异。@deprecated用于文档目的。

示例:

  1. /**
  2. * @deprecated
  3. * reason for why it was deprecated
  4. */
  5. @Deprecated
  6. public void anyMethodHere(){
  7. // Do something
  8. }

现在,只要任何程序使用此方法,编译器就会生成警告。要阅读有关此注解的更多信息,请参阅以下文章: Java - @Deprecated注解

3)@SuppressWarnings

此批注指示编译器忽略特定警告。例如,在下面的代码中,我调用了一个不推荐使用的方法(假设方法deprecatedMethod()标有@Deprecated注解),因此编译器应该生成警告,但是我使用@SuppressWarnings注解来抑制弃用警告。

  1. @SuppressWarnings("deprecation")
  2. void myMethod() {
  3. myObject.deprecatedMethod();
  4. }

创建自定义注解

  • 注解是使用@interface创建的,后跟注解名称,如下例所示。
  • 注解也可以包含元素。他们看起来像方法。例如,在下面的代码中,我们有四个元素。我们不应该为这些元素提供实现。
  • 所有注解都扩展了java.lang.annotation.Annotation接口。注解不能包含任何extends子句。
  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Inherited;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. @Documented
  8. @Target(ElementType.METHOD)
  9. @Inherited
  10. @Retention(RetentionPolicy.RUNTIME)
  11. public @interface MyCustomAnnotation{
  12. int studentAge() default 18;
  13. String studentName();
  14. String stuAddress();
  15. String stuStream() default "CSE";
  16. }

注意:使用注解时,可以跳过在创建注解时设置了默认值的所有元素。例如,如果我将上述注解应用于类,那么我会这样做:

  1. @MyCustomAnnotation(
  2. studentName="Chaitanya",
  3. stuAddress="Agra, India"
  4. )
  5. public class MyClass {
  6. ...
  7. }

如您所见,我们没有给studentAgestuStream元素赋予任何值,因为设置这些元素的值是可选的(默认值已经在注解定义中设置,但是如果您希望可以分配新的使用注解时的值与我们对其他元素的处理方式相同)。但是,我们必须在使用注解时提供其他元素的值(没有设置默认值的元素)。

注意:我们也可以在注解中包含数组元素。这就是我们如何使用它们:
注解定义:

  1. @interface MyCustomAnnotation {
  2. int count();
  3. String[] books();
  4. }

用法:

  1. @MyCustomAnnotation(
  2. count=3,
  3. books={"C++", "Java"}
  4. )
  5. public class MyClass {
  6. }

再次回到主题:在自定义注解示例中,我们使用了这四个注解:@Documented@Target@Inherited@Retention。让我们详细讨论它们。

@Documented

@Documented注解表明使用此注解的元素应由 JavaDoc 记录。例如:

  1. java.lang.annotation.Documented
  2. @Documented
  3. public @interface MyCustomAnnotation {
  4. //Annotation body
  5. }
  1. @MyCustomAnnotation
  2. public class MyClass {
  3. //Class body
  4. }

在为类MyClass生成 javadoc 时,注解@MyCustomAnnotation将包含在其中。

@Target

它指定了我们可以使用注解的位置。例如:在下面的代码中,我们将目标类型定义为METHOD,这意味着下面的注解只能用于方法。

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Target;
  3. @Target({ElementType.METHOD})
  4. public @interface MyCustomAnnotation {
  5. }
  1. public class MyClass {
  2. @MyCustomAnnotation
  3. public void myMethod()
  4. {
  5. //Doing something
  6. }
  7. }

注意:1)如果您没有定义任何目标类型,则意味着可以将注解应用于任何元素。

2)除了ElementType.METHOD之外,注解可以具有以下可能的Target值。

  1. ElementType.METHOD
  2. ElementType.PACKAGE
  3. ElementType.PARAMETER
  4. ElementType.TYPE
  5. ElementType.ANNOTATION_TYPE
  6. ElementType.CONSTRUCTOR
  7. ElementType.LOCAL_VARIABLE
  8. ElementType.FIELD

@Inherited

@Inherited注解表示类中使用的自定义注解应该由其所有子类继承。例如:

  1. java.lang.annotation.Inherited
  2. @Inherited
  3. public @interface MyCustomAnnotation {
  4. }
  1. @MyCustomAnnotation
  2. public class MyParentClass {
  3. ...
  4. }
  1. public class MyChildClass extends MyParentClass {
  2. ...
  3. }

这里的类MyParentClass正在使用注解@MyCustomAnnotation,该注解用@inherited注解标记。这意味着子类MyChildClass继承了@MyCustomAnnotation

@Retention

它指示要保留带注解类型的注解的时间长度。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface MyCustomAnnotation {

}

这里我们使用了RetentionPolicy.RUNTIME。还有另外两种选择。让我们看看他们的意思:
RetentionPolicy.RUNTIME:注解应该在运行时可用,以便通过 java 反射进行检查。
RetentionPolicy.CLASS:注解将在.class文件中,但在运行时不可用。
RetentionPolicy.SOURCE:注解将在程序的源代码中提供,它既不在.class文件中,也不在运行时可用。

这就是“Java 注解”这个主题的全部内容。如果您有任何疑问,请随时在下面留言。