在Java中,注解就是给程序添加一些信息,用字符@开头,这些信息用于修饰它后面紧挨着的其他代码元素,比如类、接口、字段、方法中的参数、构造方法等。注解可以被编译器、程序运行时和其他工具使用,用于增强或修改程序的行为。
- 创建注解:
首先看看什么是元注解,元注解是指注解的注解(用于定义注解本身),包括@Retention、@Target、@Document、@Inherited四种。
1. @Retention:定义注解的保留策略。
2. @Target:定义注解的作用目标。
3. @Document:说明该注解将被包含在javadoc中。
4. @Inherited:说明子类可以继承父类中的该注解。
下面看看@Override的定义:
@Target(ElementType.METHOD)
@Retention(RententionPolicy.SOURCE)
public @interface Override{
}
定义注解使用@interface,@Target表示注解的目标,@Override的目标是方法(ElementType.METHOD)。ElementType是一个枚举,主要可选值有:
TYPE:表示类、接口(包括注解),或者枚举声明;
FIELD:字段,包括枚举常量;
METHOD:方法;
PARAMETER:方法中的参数;
CONSTRUCTOR:构造方法;
LOCAL_VARIABLE:本地方法;
MODULE:模块(Java9引入的)。
目标可以有多个,用{}表示,如果没有声明@Target,默认为适用于所有类型。
@Retention表示注解信息保留到什么时候,取值只能有一个,类型为RetentionPolicy,它是一个枚举,有三个取值。
@SOURCE:只在源代码中保留,编译器将代码编译为字节码文件后就会丢掉。
@CLASS:保留到字节码文件中,但Java虚拟机将class文件加载到内存时不一定会在内存中保留。
@RUNTIME:一直保留到运行时。
如果没有声明@Rentention,则默认为CLASS。@Override是给编译器用的,所以@Retention都是RetentionPolicy.SOURCE。
可以为注解定义一些参数,定义的方式是在注解内定义一些方法,比如@SupperessWarnings内定义的方法value,返回值类型表示参数的类型,这里是String[]。使用@SuppressWarnings时必须给value提供值,比如:
//Java7中@SuppressWarning的定义
@Target({TYPE,FILED,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings{
String[] values;
}
//@SuppressWarnings注解的使用
@SuppressWarnings(value={"deprecation","unused"})
当只有一个参数时,且名称为value时,提供参数值时可以省略"value="。注解内参数的类型不是什么都可以的,合法的类型有基本类型、String、Class、枚举、注解,以及这些类型的数组。
参数定义时可以使用default指定一个默认值,比如,Guice中Inject注解的定义:
@Target({METHOD,CONSTRUCTOR,FIELD})
@Retention(RUNTIME)
@Documented //表示注解信息包含到生成的文档中
public @interface Inject{
boolean optional() default fasle;
}
它有一个参数optional,默认值为fasle。如果类型为String,默认值可以为"",但不能为null。如果定义了参数并且没有提供默认值。在使用注解时必须提供具体的值,不能为null。
与接口和类不同,注解不能继承。不过注解有一个与继承有关的元注解@Inherited,说明子类可以继承父类中的该注解,看个列子:
public class InheritDemo{
@Inherited
@Retention(RetentionPolicy.RUNTIME)
static @interface Test{
}
@Test
static class Base{
}
static class Child extends Bae{
}
public static void main(String[] args){
System.out.println(Child.class.isAnnotationPresent(Test.class));
}
}
Test是一个注解,类Base有该注解,Child继承了Base但没有声明该注解。main方法检查Child类是否有Test注解,输出为true,这是因为Test有注解@Inherited,如果去掉,输出会变为false。
2. 查看注解的信息
通过反射相关类中的与注解有关的方法,这里汇总说明下,Class、Field、Method、Constructor中都有如下方法:
//获取所有的注解
public Annotation[] getAnnotations()
//获取所有本元素上之直接声明的主键,忽略inherited来的
public Annotation[] getDeclaredAnnotations()
//获取指定类型的注解,没有返回null
public <A extens Annotation> A getAnnotation(Class<A> annotationClass)
//判断是否有指定类型的注解
public boolean isAnnotationPresent(Class<? extens Annotation> annotationType()
Annotation是一个注解,它表示注解,具体定义为:
public interface Annotation{
boolean equals(Object obj);
int hashCode();
String toString();
//返回真正的注解类型
class<? extens Annotation> annotationType();
}
实际上,内部实现时,所有的注解类型都是拓展的Annnotation。
对于Method和Contructor,它们都有方法参数,而参数也可以有注解,所以它们都有如下方法:
public Annotation[][] getParamterAnnotations()
返回值是一个二维数组,每个参数对应一个一维数组。1
Java编程的逻辑 ↩︎