注解

Java注解用于为你的Java代码提供元数据。作为元数据,Java注解不会直接影响代码的执行,尽管某些类型的注解实际上可以用于此目的。

Java注解是从Java 5添加进来的。本文涵盖Java6中注解的样式。据我所知,Java注解在Java 7中没有改变,所以这篇教程也应该对Java 7有效。

注解的目的

Java注解通常用于以下目的:

  • 编译指示
  • 构建时指示
  • 运行时指示

Java有3个内置的注解,可以用来给Java编译器提供指令。这些注解在本文后面会有更详细的解释。

当你构建软件项目时,可以在构建时使用Java注解。软件构建过程包括生成源代码,编译源代码,生成XML文件(例如部署描述符),将编译后的代码和文件打包到JAR文件中等。构建软件通常由像Apache Ant或Apache Maven这样的自动构建工具完成。构建工具可能会扫描你的Java代码以获取特定的注解,并根据这些注解生成源代码或其他文件。

通常,编译之后,Java注解不会出现在你的Java代码中。但是,可以定义在运行时可用的注解。这些注解可以通过Java 反射器来访问,并用于给你的程序或者一些第三方API提供指令。

注解基础

注解简短地形式如下:

@Entity

@字符告诉编译器这是一个注解。@字符后面的名称是注解的名称。在上面的例子中,注解名称是Entity。

注解元素

Java注解可以包含元素,同时可以为其设置值。元素就像一个属性或参数。下面是一个带有元素的Java注解示例:

@Entity(tableName = "vehicles")

此示例中的注解包含名为tableName的单个元素,其值设置为vehicles。注解名称后面的元素被括在圆括号内。没有元素的注解不需要括号。

注解可以包含多个元素。如下所示:

@Entity(tableName = "vehicles", primaryKey = "id")

如果注解仅包含单个元素,则按照惯例将该元素命名为value,如下所示:

@InsertNew(value = "yes")

当注解只包含一个名为value的单个元素时,可以省略元素名称,只提供值。所示所示:

@InsertNew("yes")

注解放置的位置

你可以将Java注解放置在类,接口,方法,方法参数,字段和局部变量之上。下面是一个添加在类定义之上的示例注解:

@Entity
public class Vehicle {
}

注解以@字符开头,后跟注解的名称。上面示例中,注解名称是Entity。Entity注解是我编写的注解。它在Java中没有任何意义。

下面是一个更加丰富的注解示例:

@Entity
public class Vehicle {

    @Persistent
    protected String vehicleName = null;


    @Getter
    public String getVehicleName() {
        return this.vehicleName;
    }

    public void setVehicleName(@Optional vehicleName) {
        this.vehicleName = vehicleName;
    }

    public List addVehicleNameToList(List names) {

        @Optional
        List localNames = names;

        if(localNames == null) {
            localNames = new ArrayList();
        }
        localNames.add(getVehicleName());

        return localNames;
    }

}

上面的注解仍旧只是我已经创建的注解。它们在Java中没有具体的含义。

内置的注解

Java带有三个内置注解,这些注解用于给Java编译器提供指令。这些注解是:

  • @Deprecated
  • @Override
  • @SuppressWarnings

以下各节将对每个注解进行说明。

@Deprecated

@Deprecated注解用于将一个类,方法或字段标记为已弃用,这意味着不应再使用它。如果你的代码使用不推荐的类,方法或字段,编译器会给你一个警告。下面是@Deprecated Java注解示例:

@Deprecated
public class MyComponent {

}

在类声明上方使用@Deprecated 注解将该类标记为已弃用。

你也可以使用@Deprecated注解标记上面的方法和字段声明,将该方法或字段标记为已弃用。

当使用@Deprecated注释时,最好也使用相应的@deprecated JavaDoc符号,并解释为什么不推荐使用类,方法或字段,以及程序员应该使用什么。例如:

@Deprecated
/**
  @deprecated Use MyNewComponent instead.
*/
public class MyComponent {

}

@Override

@Override注解用于覆盖超类中方法的方法上。如果该方法与超类中的方法不匹配,编译器会给你一个错误。

单纯为了覆盖超类中的方法是不必非要使用@Override注解的。不过,使用它是一个好方式。如果有人在超类中改变了重写方法的名字,你的子类方法将不再覆盖它。没有@Override注解,你不会发现这个问题。而使用@Override注解,编译器会告诉你,子类中的方法不会覆盖超类中的任何方法。

下面是一个@Override 注解示例:

public class MySuperClass {

    public void doTheThing() {
        System.out.println("Do the thing");
    }
}


public class MySubClass extends MySuperClass{

    @Override
    public void doTheThing() {
        System.out.println("Do it differently");
    }
}

如果在MySuperClass类中将doTheThing()改名字,会导致子类的该方法将不再覆盖它,这时编译器会产生一个错误信息提示你。

@SuppressWarnings

@SuppressWarnings注解使编译器能够抑制给定方法的警告。例如,如果某个方法调用了不推荐的方法,或者进行了不安全的类型转换,编译器可能会生成一个警告。你可以通过使用@SuppressWarnings批注注解包含代码的方法来禁止这些警告。

下面是一个@SuppressWarnings 注解示例:

@SuppressWarnings
public void methodWithWarning() {


}

创建自己的注解

可以创建自己的(自定义)注解。注释就像Java类或接口一样在文件中定义。下面里是自定义的Java注解示例:

@interface MyAnnotation {

    String   value();

    String   name();
    int      age();
    String[] newNames();

}

这个例子定义了一个名为MyAnnotation的注解,它有四个元素。注意@interface关键字。这是向Java编译器发出注解定义信号。

请注意,每个元素的定义与接口中的方法定义相似。它有一个数据类型和一个名字。你可以使用所有原始数据类型作为元素数据类型。你也可以使用数组作为数据类型。但是你不能使用复杂的对象作为数据类型。

要使用上面的注解,可以像下面这样编写代码:

@MyAnnotation(
    value="123",
    name="Jakob",
    age=37,
    newNames={"Jenkov", "Peterson"}
)
public class MyClass {


}

如你所见,我不得不为MyAnnotation注解中的每个元素都指定一个值。

元素默认值

你也可以指定元素的默认值。这样的元素变成可选的,可以省略。示例如下:

@interface MyAnnotation {

    String   value() default "";

    String   name();
    int      age();
    String[] newNames();

}

value元素可以在使用注解时省略。如果将其忽略掉,将使用value元素的默认值。下面是元素值省略的注释示例,以便将元素设置为默认值:

@MyAnnotation(
    name="Jakob",
    age=37,
    newNames={"Jenkov", "Peterson"}
)
public class MyClass {


}

注意value元素并没有出现注解使用的地方

@Retention

你可以为自定义注解指定它是否是在运行时可用,以便通过反射器进行检查。你可以使用@Retention批注注释你的批注定义。如下所示:

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

@Retention(RetentionPolicy.RUNTIME)

@interface MyAnnotation {

    String   value() default "";

}

注意在MyAnnotation定义之上添加的@Retention注解:

@Retention(RetentionPolicy.RUNTIME)

这是向Java编译器和JVM发出的信号,说明该注解应该在运行时通过反射来使用。

RetentionPolicy类包含另外两个可以使用的值:

RetentionPolicy.CLASS意味着注解存储在.class文件中,但在运行时不可用。如果你没有指定任何保留策略,则这是默认保留策略。

RetentionPolicy.SOURCE意味着注解只能在源代码中使用,而不能在.class文件中使用,而不能在运行时使用。如果你创建自己的注解用于扫描代码的构建工具,则可以使用此保留策略。这样.class文件不会被不必要的代码污染。

@Target

你可以通过@Target注解来指定自定义注解可用于注解的Java元素。示例如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

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

    String   value();
}

上面例子中的注解只能用于注释方法

ElementType类包含了下面这些可能值:

  • ElementType.ANNOTATION_TYPE
  • ElementType.CONSTRUCTOR
  • ElementType.FIELD
  • ElementType.LOCAL_VARIABLE
  • ElementType.METHOD
  • ElementType.PACKAGE
  • ElementType.PARAMETER
  • ElementType.TYPE

这些大部分可以名字知道其用途,但有一些不行。所以我会解释一下不能通过字面意思理解的值。

ANNOTATION_TYPE的使用目标是注解的定义。因此,该注解批注的注解只能用于注释其他注解。就像@Target和@Retention注解一样。

TYPE意味着可以是任何类型。比如类,接口,枚举或注解。

@Inherited

@Inherited注解表明,类中使用的自定义Java注解应该由子类继承。下面是一个@Inherited Java注解示例:

import java.lang.annotation.Inherited

@Inherited
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }
public class MySubClass extends MySuperClass { ... }

在这个例子中,类MySubClass继承了注释@MyAnnotation,因为MySubClass继承自MySuperClass,而MySuperClass具有@MyAnnotation注解。

@Documented

@Documented注解用于向JavaDoc工具发信号,通知你的自定义注解应该在JavaDoc中,使用了你自定义注解的类中可见。这是一个@Documented Java注解示例:

import java.lang.annotation.Documented;

@Documented
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }

为MySuperClass类生成JavaDoc时,JavaDoc中包含@MyAnnotation。

你不会经常使用@Documented注解,但在你需要它时,你应该知道它的存在。

下一篇:Lambda表达式

results matching ""

    No results matching ""