注解
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表达式