你不了解的注解(一)

1.注解的概念
对于注解的概念,这里直接给出《Think In Java》第四版中的解释:
Annotations (also known as metadata) provide a formalized way to add information to your code so that you can easily use that data at some later point.
注解,也可称元数据,为我们在代码中添加信息提供一种形式化的方法,便于我们后续使用这些数据。
从概念中可以知道,注解是为代码添加信息的一种形式化的方法;那么添加什么信息以及这种方式有什么好处就是我们今天要弄明白的问题。

下面我们来看一下Java语言为我们提供的内置的四种标准注解。

2.四种标准注解
2.1 @Override
@Override注解源码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}

可以看到注解的定义形式与定义接口非常相似,只需要在关键字interface前加一个@,注解内没有任何属性和方法,类似于这种没有元素的注解称为标记注解,也就是说这种注解没有实质作用,只是为了标记。
那么这里,标记注解@Override标记什么呢?
根据类上的解释可知,@Override注解用于表明声明的方法将要重写父类中的方法;如果一个方法带有这个注解类型,那么编译器认为这个方法是下面情况的一种:①该方法确实覆盖或实现父类型中声明的方法;②该方法与Object类中public类型的方法有同样的签名(名字);否则编译器将会报错。可知,@Override注解是在编译阶段产生作用,其作用主要是为了标记方法为重载方法。
PS:注解头上的注解是元注解,后面会讲到。

2.2 @Deprecated
@Deprecated注解源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {}

该注解没有声明任何元素,因此@Deprecated注解也是标记注解,那么它标记什么呢?
被@Deprecated注解标记的方法表明该方法由于某种原因而不推荐使用,如果我们在代码中使用了此类方法,编译器将会发出告警提示你。
我们很少主动使用@Deprecated注解,一般都是框架或者Java语言API中使用,比如Date类的parse(args)方法。

2.3 @SuppressWarnings
@SuppressWarnings注解源码如下:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

该注解内含有一个元素,是正常的注解。那么这个注解有什么用呢?注解内的数组元素有什么用呢?
@SuppressWarnings注解的作用是抑制编译器警告。比如,你声明了一个变量但没有使用,编译器就会用黄色波浪线警告提示,这个时候编译器就会建议你使用@SuppressWarinings注解抑制警告,并会在注解后面说明警告的内容,这个内容就是注解声明的元素,具体内容由编译器供应商确定好。

2.4 @Native
@Native注解的源码如下:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {}

特别注意,这个标记注解是JDK1.8才有的,@Native注解的主要作用是定义为常量的属性值可能引用本地代码。此类注解应该用的不多,常出现于框架或者Java的API内。

前三个注解都位于java.lang包内,新加的@Native注解位于java.lang.annotation包内,且都是一个独立的.class文件,因此可以看得出来注解与类和接口位于同一级别上,也进一步说明了它的重要性。

3.五种元注解
元注解的作用就像它的名字一样,负责注解其他的注解。下面详细看看五种元注解。

3.1 @Target
@Target注解的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

注解@Target的主要作用是表明该注解用于什么地方,其内部元素的值由ElementType枚举类声明,共有TYPE(类/接口/枚举类),FIELD(属性),METHOD(方法),PARAMETER(参数),CONSTRUCTOR(构造函数),LOCAL_VARIABLE(本地变量),ANNOTATION_TYPE(注解类),PACKAGE(包),TYPE_PARAMETER(类型参数)和TYPE_USE(类型的使用)十种类型,其中TYPE_PARAMETER和TYPE_USE是JDK1.8版本新增的两种类型。几乎可以确认,Java语言任何地方都可以用注解进行修饰。

看这里的的@Target注解类型为ANNOTATION_TYPE,表明@Target注解只能用于注解类,这个在我们自定义注解的时候非常有用。我们回头看四种标准注解:

@Override注解@Target的值为METHOD,表明该注解只能用在方法上;
@Deprecated注解@Target的值为CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE,表明该注解可以用在包,类,属性,方法以及构造函数等上,这个进一步表明不推荐使用内容的类型很广;
@SuppressWarnings注解@Target的值为TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,表明该注解可以用在类,属性,方法以及构造函数上;
@Native注解@Target的值为FIELD,表明该注解只能用在属性上;
3.2 @Retention
@Retention注解的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

注解@Retention的主要作用是表明该注解保留到什么级别,内部元素就是具体级别,其值由枚举类RetentionPolicy声明,共有SOURCE(源码),CLASS(类文件),RUNTIME(运行时)三种级别。
看此处@Retention注解的级别为RUNTIME,表明该注解能保留到运行时。我们回头看四种标准注解:

@Override注解@Retention的值为SOURCE,表明该注解只能保留在源码级别,即在编译过程中会被丢弃掉;
@Deprecated注解@Retention的值为RUNTIME,表明该注解可以保留到运行时;
@SuppressWarnings注解@Retention的值为SOURCE,跟注解@Override一样,保留在源码级别;
@Native注解@Retention的值为SOURCE,与注解@Deprecated一样,只保留在源码级别;
注意:如果什么都不写,那么默级别为CLASS,它表明会保留到字节码中,但会被JVM丢弃。
3.3 @Documented
@Documented注解源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {}

注解@Documented的主要作用是该注解包含在Javadoc中。意思就是,如果会形成Javadoc文档,那么被修饰的注解也会保留到Javadoc中。

3.4 @Inherited
@Inherited注解的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {}

注解@Inherited的主要作用是该注解修饰的类可以继承父类中的注解。请注意,如果带注释的类型用于注释类以外的任何东西,则此元注释类型没有效果。还要注意,这个元注释只会导致从父类继承注释,实现接口上的注释没有效果。

3.5 @Repeatable
@Repeatable注解的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}

注解@Repeatable的主要作用是声明它(元)注释的注释类型是可重复的,@Repeatable注解的元素值表示可重复注释类型的包含注释类型,该注释类型还包含其他什么注释。

4.简单总结
此时回头想想注解的含义:为我们在代码中添加信息提供一种形式化的方法,便于我们后续使用这些数据。即通过注解这种方式为代码添加某种信息,这种信息可以是隐含的,如@override注解表示重载方法,这种信息也可以是很明显的,如@Native注解表示引用本地代码;这种信息可以是关于类,也可以是关于属性、参数和方法;同时,这种信息可以在不同阶段供我们使用。

站在某种角度看,注解提供的功能很强大,它提供的信息能够带来某种功能的实现,我们上面讨论的都是Java内置基本注解,实际项目中可以通过自定义注解来实现某种功能,这种功能没法穷尽,因为很多能用类实现的功能,也能用注解来实现;不过,只定义注解是不够的,我们还需要定义注解处理器,关于这部分内容,下一篇再讲吧。
 

上一篇:Java注解(二)


下一篇:opencv学习笔记9 查找绘制轮廓