JavaSE学习笔记 - 注解基础

  注解(Annotation)是元数据的一种形式,它提供描述程序但不属于程序本身的数据。注解没有直接作用于所标注的代码本身的功能。

注解是在Java SE 5时引入的一个新特性,很多时候它被称为元数据特性。注解有以下几种功能:

  • 提供信息给编译器 —— 注解能够编译器用来检测错误或抑制警告
  • 编译时和部署时处理 —— 注解处理工具可以处理注解信息以此生成代码、XML文件等等
  • 运行时处理 —— 一些注解能够在运行时被检查运用到

使用注解的好处:

  • 每当你创建描述符性质的类或接口并涉及于重复性的工作,那通常你就可以考虑使用注解来简化与自动化该过程。
  • 注解是在实际的源码级别保存所有程序相关的信息,而不是某种注释性的文字(comment),这使得代码更整洁,且便于维护。

注解基础

了解注解

  • 当我们看到@这个符号的时候,它表明紧跟它的就是一个注解。
    @Override@Entity@SuppressWarnings等等,它们都是注解。

  • 注解可以包含元素,我们可以给它指明名字或者直接忽略其名,同时可赋值于元素。
    @SuppressWarnings(value={"unchecked"})@SuppressWarnings("unchecked")

  • 如果注解自身没有元素,那么连括号也可以忽略。
    @Override@Test

  • 可以给同一声明使用多个注解

    1
    2
    3
    @Override
    @SuppressWarnings("unchecked")
    void f1(int parameter)
  • Java SE 8中支持重复注解(repeating annotation)特性,即在同一处声明使用多个同一个注解。

    1
    2
    3
    @Author(name="gtr")
    @Author(name="jack")
    class Foo { }

在哪里可以使用注解

  • 通常注解可用于所有声明处:各种类型(类、接口、枚举、注解)、域、构造器、方法、局部变量、方法参数。

  • 在Java SE 8中更为注解加入了类型注解(type annotation)的特性,它指明了注解可以用在使用类型的任何地方。

1
2
3
4
5
6
7
8
9
new @Interned MyObject();

myString = (@NonNull String) str;

class UnmodifiableList<T> implements
@Readonly List<@Readonly T> { ... }

void monitorTemperature() throws
@Critical TemperatureException { ... }

我们可以看到:在创建实例、强制类型转换、泛型类型参数、抛异常的声明,在这些地方都可以用上注解。

自定义注解

定义注解类型

注解类型是一种特殊的接口类型(此处的接口类型并非Java中interface这个关键字所定义的类型)。

定义注解类型的语法如下:

{InterfaceModifier} @ interface Identifier AnnotationTypeBody
或
modifiers @interface AnnotationName {
    elementDeclaration1
    elementDeclaration2
    ...
}

并且注解元素声明都具有以下这种形式:

type elementName();
或
type elementName() default value;

注解类型的小细节:

  • java.lang.annotation.Annotation是所有注解类型的父类型。
  • 注解类型不能被声明为泛型,并且它不能有extends子句(即不能进行继承扩展)。
  • 任何类或接口中都不能再定义一个与它们同名的内部注解。
  • 注解类型中可以包含注解元素、常量、类、接口这4种成员。

特殊注解类型:

  • 没有元素的注解类型被称为标记注解类型(marker annotation type)。
  • 注解类型中只有一个元素且该元素名为value;注解类型有多个元素,其中一个名为value,其余元素都有默认值。
    以上两种情况的注解类型被称为单元素注解类型(single-element annotation type)

注解元素中,方法的返回类型只能是以下这些类型:

  • 内置基础类型(primitive type)
  • String
  • Class<T>的原生类型或其参数化类型
  • 枚举类型
  • 注解类型
  • 以上这些类型作成分的数组类型

现在来自定义一个注解。

1
2
3
4
5
6
7
8
9
public @interface ClassPreamble {
String author();
String date();
int currentReversion() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// note use of array
String[] reviewers();
}

  注解的定义看起来就像是接口的定义,它们都使用关键字interface进行定义,只不过定义注解时必须要在这个关键字前多加一个符号@。这个注解有6个元素,并且其中一些元素被指定了默认值。

使用

定义完了注解类型,那么我们就可以开始使用它们了。

使用注解类型都采用以下这种语法:

@AnnotaionName(elementName1=value1, elementName2=value2, ...)

注解是一个将信息与程序架构关联在一起的标识,但它在运行时没有其它副作用的。

注解分为3种类型:

  • 普通注解(normal annotation)@TypeName([ElementValuePairList])
  • 标记注解(marker annotation)@TypeName
  • 单元素注解(single element annotation)@TypeName(value = ElementValue)

在这3种注解,其实可以把标记注解和单元素注解看作普通注解,平时可以当这2种是普通注解的简化形式。

关于注解元素这块的小细节:

  • 使用注解并指明元素值时,元素的顺序是无关紧要的,就是说元素的定义顺序与使用时的顺序可以不一样。
  • 如果在使用注解时未指明某个元素的值且这个元素有默认值,那么就使用定义注解时的默认元素值。
  • 当所定义的注解只有1个元素并且其元素名为value时,使用该注解时可以直接不写元素名而指定元素值。

现在就来使用之前定义的注解ClassPreamble

1
2
3
4
5
6
7
8
9
10
11
12
@ClassPreamble(
author="ExtremeGTR",
date="2/16/2017",
currentReversion=8,
lastModified="2/16/2017",
lastModifiedBy="ExtremeGTR",
// note array notation
reviewers={"Alice", "Bob", "Cindy"}
)
public class Demo1 {
// class code goes here
}

注解里的信息看上去就像是我们平时查API文档时所看到的信息。那么我们怎么才能让注解中的信息输出在类Demo1的API文档上?

解决办法肯定有,就是用@Documented这个注解标注在我们的自定义注解@ClassPreamble上。

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.annotation.Documented;

@Documented
public @interface ClassPreamble {
String author();
String date();
int currentReversion() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// note use of array
String[] reviewers();
}

预定义注解类型

  其实在Java SE API中就已经预定义了很多注解类型,这些注解类型包含于java.langjava.lang.annotation这两个包中,一般我们都称它们为预定义注解类型(predefined annotation type)标准注解类型(standard annotation type)。其中一些是提供给编译器使用的注解,而另一些是专职于标注其他注解的被称为元注解(meta annotaion)

用于编译的注解

@Deprecated

  • 凡是带@Deprecated注解的程序元素,它们都不再被开发者推荐使用的,即该注解表示此代码已经过时或者有危险性,而开发者已经提供了更好的解决方案给你使用。

  • 当你是开发者而又认为某些代码不应该再提供给接口用户使用,你就能用这个注解。
    当你使用被该注解标注的代码,编译器会发出警告。

@SuppressWarnings

  • 该注解可以禁用编译器所发出的某些警告。

  • 当你在开发的时候清楚某些编译器警告是多余,你就可以用@SuppressWarnings标注有警告的地方,这样注解就会关闭这个警告。

@Override

  • 该注解只应用在方法上,它的责任就是在编译时检测被标注的方法是否正确地被重载。