Java 注解详解 (annotation)
Posted Heaven-Wang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 注解详解 (annotation)相关的知识,希望对你有一定的参考价值。
什么是java注解
注解是java5的新特性。注解可以看做一种注释或者元数据(MetaData),可以把它插入到我们的java代码中,用来描述我们的java类,从而影响java类的行为。
Java注解的目的
使用Java注解一般来说主要有三种目的
- 构建时指示: RetentionPolicy.SOURCE
- 编译期指示: RetentionPolicy.CLASS
- 运行时指示: RetentionPolicy.RUNTIME
Java注解可以用在构建期。当构建我们的工程时,构建进程会编译源码、生成xml文件,打包编译后的代码和文件到jar包。构建过程一般由构建工具自动完成,常用的构建工具有ant、maven。构建工具在构建时会自动扫描我们的代码,当遇到构建期注解时,会根据注解的内容生成源码或者其它文件。
注解基本概念
注解的构成
一个java注解由一个@符后面跟一个字符串构成,类似于这样:
@Entity
java注解中一般包含一些元素,这些元素类似于属性或者参数,可以用来设置值,比如我们有一个包含两个元素的@Entity注解:
@Entity(tableName = "vehicles", primaryKey = "id")
上面注解中有两个元素,tableName
和primaryKey
,它们各自都被赋予了自己的元素值。
注解的位置
注解可以用于描述一个类、接口、方法、方法参数、字段、局部变量等。在下边这个例子中,注解分别用在了类、字段、方法、参数和局部变量中:
//注解一个类
@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本身提供了三个内置注解,他们分别是:
1. @Deprecated
2. @Override
3. @SuppressWarnings
@Deprecated注解
@Deprecated
可以用来描述一个类、方法或者字段,表示java不赞成使用这些被描述的对象,如果我们使用了这些类、方法或者字段,编译器会给我们警告。@Deprecated
注解使用方法如下:
@Deprecated
public class MyComponent
在我们实际应用中,在使用@Deprecated
注解时,最好同时使用Java Doc的@deprecated
符号,用来描述当前类、方法或者字段是不赞成使用的,并且告诉开发者应该用哪个对象替换,如下面例子:
@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");
@SuppressWarnings
@SuppressWarnings
注解的作用是使编译器忽略掉编译器警告。比如,如果我们的一个方法调用了一个@Deprecated
方法,或者做了一个不安全的类型转换,此时编译器会生成一个警告。如果我们不想看到这些警告,我们就可以使用@SuppressWarnings
注解忽略掉这些警告:
@SuppressWarnings
public void methodWithWarning()
创建自定义注解
定义一个自定义注解
从上面内置注解可以看到,注解很方便也很有用,很多时候我们也需要创建我们自己的注解。创建注解其实和创建类或接口一样简单:
@interface MyAnnotation
String value();
String name();
int age();
String[] newNames();
上面代码便创建了一个@MyApplication
注解,它一共有四个元素。@interface
关键字就代表这是一个注解类型,所以使用@interface
关键字就可以创建注解了。
需要注意的是,注解中的每个元素定义类似于接口中的方法定义。每个元素定义包含一个数据类型和名称,注解元素的数据类型可以是java基本数据类型、String、数组,但不能是复杂对象类型。
下面这段代码演示了如何使用注解:
@MyAnnotation(
value="123",
name="Jakob",
age=37,
newNames="Jenkov", "Peterson"
)
public class MyClass
给注解元素设置默认值
我们可以通过default
关键字为某个元素设置默认值,当一个元素被设置默认值之后,这个元素便成了注解的可选元素。
下面我们为@MyAnnotation
注解的value
元素设置一个默认值:
@interface MyAnnotation
String value() default "";
String name();
int age();
String[] newNames();
当value
元素设置默认值之后,再使用时我们就可以省略掉value
元素,此时的value
值采用的是默认值:
@MyAnnotation(
name="Jakob",
age=37,
newNames="Jenkov", "Peterson"
)
public class MyClass
两个元注解:@Retention和@Target
元注解就是注解的注解。我们可以通过元注解来控制描述我们自定义注解的行为。
@Retention
@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)
上面这个注解会告诉编译器和JVM,这个注解需要在运行时有效,JVM会在运行时通过反射机制获取注解信息,关于如何在运行时利用反射获取注解信息,最后面会进行介绍。@Retention
注解的值一共有三种:
- RetentionPolicy.SOURCE : 注解只存在于源码中,不会存在于.class文件中,在编译时会被忽略掉
- RetentionPolicy.CLASS:注解只存在于.class文件中,在编译期有效,但是在运行期会被忽略掉,这也是默认范围
- RetentionPolicy.RUNTIME:在运行期有效,JVM在运行期通过反射获得注解信息
@Target
@Target
注解用来约束自定义注解可以注解Java的哪些元素。比如下面这个例子:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface MyAnnotation
String value();
这个例子中,@Target
的值是ElementType.METHOD
,通过它的名称可以看出,这个自定义注解只能注解类的方法。
ElementType
的值一共有以下几种:
- ElementType.ANNOTATION_TYPE
- ElementType.CONSTRUCTOR
- ElementType.FIELD
- ElementType.LOCAL_VARIABLE
- ElementType.METHOD
- ElementType.PACKAGE
- ElementType.PARAMETER
- ElementType.TYPE
其中大部分通过名字就能看出它的作用,不过有两个需要单独介绍一下:
- ElementType.ANNOTATION_TYPE:元注解类型,只能用来注解其它的注解,例如@Target和@Retention。
- ElementType.TYPE:可以用来注解任何类型的java类,如类、接口、枚举、或者注解类。
@Inherited
@Inherited
注解表示当前注解会被注解类的子类继承。比如有一个自定义注解:
java.lang.annotation.Inherited
@Inherited
public @interface MyAnnotation
如果有一个类使用了上面这个注解:
@MyAnnotation
public class MySuperClass ...
那么这个类的子类也会继承这个注解:
public class MySubClass extends MySuperClass ...
因为MySubClass
继承了MyClass
,而MyClass
的注解@MyAnnotation
是可继承的,最终MySubClass
也会有@MyAnnotation
注解。
@Documented
@Documented
的作用是告诉JavaDoc工具,当前注解本身也要显示在Java Doc中。比如我们用@Document
注解了我们的自定义注解:
import java.lang.annotation.Documented;
@Documented
public @interface MyAnnotation
如果一个类使用了这个注解:
@MyAnnotation
public class MySuperClass ...
那么当生成MySuperClass
的JavaDoc的时候,@MyAnnotation
也会出现在JavaDoc当中。
通过反射获得Java注解信息
上面对注解做了一个详细介绍,具体该如何使用我们的自定义注解呢?其实在现实应用中,我们的自定义注解一般都是起到运行时指示的作用,也就是运行时注解。对于运行时注解,我们可以通过反射机制获得注解信息。
比如我们有一个自定义注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation
public String name();
public String value();
并用这个注解注解了一个类:
@MyAnnotation(name = "hello name",value = "hello value")
public class MyClass
获取类的注解信息
public class TestAnnotation
public static void main(String[] args)
//通过反射获得MyClass的注解信息
MyAnnotation myAnnotation=MyClass.class.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.name());
System.out.println(myAnnotation.value());
获取方法的注解信息
public class TestAnnotation
public static void main(String[] args)
Method method = null;
try
method = MyClassB.class.getMethod("method");
Annotation annotation = method.getAnnotation(MyAnnotation.class);
if (annotation !=null)
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
catch (NoSuchMethodException e)
e.printStackTrace();
获取参数的注解信息
Method method = ... //obtain method object
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();
int i=0;
for(Annotation[] annotations : parameterAnnotations)
Class parameterType = parameterTypes[i++];
for(Annotation annotation : annotations)
if(annotation instanceof MyAnnotation)
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("param: " + parameterType.getName());
System.out.println("name : " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
获取字段的注解信息
Field field = ... // obtain method object
Annotation annotation = field.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation)
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
以上是关于Java 注解详解 (annotation)的主要内容,如果未能解决你的问题,请参考以下文章