注解的作用,可以编写文档,可以进行代码分析,可以进行编译检查等等,后面慢慢说,下面先描述一下注解。
java自带的注解之一 —— 标准注解
- @Override,表示当前方法将要覆盖父类的方法。使用这个注解,然后如果没有覆盖成功,编译器会出错。
如果不用这个注解,编译器大部分时候会认为你在子类写了个新方法,这就与我们的想法不同了。
- @Deprecated,如果你给一个元素加上了这个注解,然后又使用了这个元素,那么会出现一个警告信息。
- @SuppressWarnings,关闭(不当的)编译器警告。如果你在一处警告加上了这个注解,那么这个警告就消失了(表面上)。
java自带的注解之二 —— 元注解,以及自定义注解
元注解专门负责注解其他的注解。
日常使用的大部分注解都是自己定义的。这个时候就会用到元注解。下面通过一个栗子来说明
首先我们来自定义一个注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
- 这里,我们自定义了一个注解
@Test
,定义的时候很像接口,不过用的是@interface
,多了个“@”。
和其他任何java接口一样,注解也将会编译成class文件。
- 定义注解,这里用到了两个元注解:
@Target
和Retention
。前一个定义新写的注解可以用在什么地方,后一个来定义该注解可以在哪里使用(源代码、类文件或者运行时)。可以看到使用这两个元注解的时候,往里面分别传了值。
所有的元注解以及可以取的值以及具体的含义如下:
- @Target:表示该注解可以用于什么地方。可能的ElementType参数如下
ElementType | 说明 |
CONSTRUCTOR | 构造器的声明 |
FIELD | 域声明(包括enum实例) |
LOCAL_VARIABLE | 局部变量声明 |
METHOD | 方法声明 |
PACKAGE | 包声明 |
PARAMETER | 参数声明 |
TYPE | 类、接口(包括注解类型)或enum声明 |
- @Retention:表示需要在什么级别保存该注解信息。可选的RententionPolicy参数包括:
RententionPolicy | 说明 |
SOURCE | 注解将被编译器丢弃 |
CLASS | 注解在class文件中可用,但会被虚拟机(VM)丢弃 |
RUNTIME | VM将在运行期也被保留,因此可以通过反射机制读取注解信息 |
- @Documented:将此注解包含在Javadoc中。
- @允许子类继承父类中的注解。
大多数情况下,程序员一般自己定义注解。(不过Override算是用的最多的)
- 在注解中,你可以包含一些元素来表示一些值。在这里,你可以为这些被包含的元素指定默认值。下面针对这一点再来举个栗子:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test2 {
public int id();
public String name() default "yiran";
}
在这里,注解Test2
定义了两个元素:id
和name
(注意这里要在元素名后面加()
)。然后给元素name
设定了一个默认值,通过default
。如果使用该注解的时候,没有给出name
元素的值,那么将使用默认值。下面来举个栗子:
@Test2(id = 3, name = "zhangsan")
public void testMethod3() {}
@Test2(id = 4)
public void testMethod4() {}
上面这个栗子中,第一个注解对两个元素都进行了赋值,而第二个注解只对id
进行了赋值,那么此时name
的值为“yiran”(默认值)。
以上就是自定义一个注解,下面来说明如何通过注解处理器来处理注解
注解处理器
通过反射来处理注解。当一个注解被定义为“运行时可见”,那就可以使用反射。
Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。这个接口的实现类包括Class、Constructor、Field、Method、Package。
只要获取了某个程序元素的class,就可以获取这个元素里面的所有子元素(比如类里面的方法等等),就可以获取每一个元素子元素的注解。在AnnotatedElement 里面定义了四个方法:
1. getAnnotation:
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
这个方法返回了该元素指定类型的注解,显然是 某个元素. 来调用。
参数 对应注解的class对象。
返回 是对应注解的对象或者`null`。
2. getAnnotations:
Annotation[] getAnnotations()
3. getDeclaredAnnotations:
Annotation[] getDeclaredAnnotations()
这个方法返回此元素上面所有的注解,以数组的形式返回。与上面那个不同,这个方法只返回直接存在于该元素上的注解,继承来的注解不管。
4.isAnnotationPresent:
boolean isAnnotationPresent
(Class<? extends Annotation> annotationClass)
如果此元素上有指定的注解,返回true;否则返回false。
这里再来说一下反射,这里通过反射可以获得一个类中所有的东西,包括但不限于方法、变量等等,然后再通过上面的四种方法,可以获得每个“东西”(即“元素”)上的注解。
元素包括:
Class:类定义
Constructor:构造器定义
Field:累的成员变量定义
Method:类的方法定义
Package:类的包定义例如:Class既是类定义,又是元素
总之,只要能通过反射获得元素,就可以获得一切。
乱七八糟
- 标签中可用的元素(和上面刚说过的不太一样。。又感觉差不多)种类包括:
- 所有基本类型(不允许使用包装类型)
- String
- Class
- enum
- Annotation
- 以上类型的数组
从上面看,注解可嵌套
- 关于注解中的元素(好不容易和上面那个元素分开了)
元素不能有不确定的值,即要么使用注解时设定值,要么使用默认值。
但是,对于非基本类型的元素,不能设为
null
,无论是默认值还是使用时设定
- 注解不支持继承,即不能写一个新注解,然后
extend
一个老注解。
目前就先写这么多。目前看来,注解使用的场合越来越多,比如Spring中十分推荐使用注解配置bean。以上仅仅为注解的入门,注解尤其是注解处理器还能玩出非常大的花样。不过接下去应该还是会写一写关于Spring中用到的其他东西,不然乍一学蒙圈好久。