注解
# 历史
JDK5.0 新增 -- 注解(Annotation),也叫元数据。
# 定义
注解就是代码里的特殊标记,这些标记可以在编译,类加载和运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。
使用注解时,要在其前面加上@
符号,并把该注解当成一个修饰符
使用。用于修饰它支持的元素。
Annotation
可以像修饰符一样被使用,可以用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation
的"name=value"
中。可以用来代替JavaEE 旧版中所遗留的繁冗代码和 XML 配置等。JPA(Java的持久化API)
是基于注解的,Spring2.5
以上都是基于注解的,Hibernate3.x
以后也是基于注解的,所以注解是一种趋势,一定程度上可以说:框架=注解+反射+设计模式
。
# 使用实例
# Junit 的注解
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class Demo01 {
@Before
public void init() {
System.out.println("方法执行开始");
}
@After
public void close() {
System.out.println("方法执行结束");
}
@Test
public void testAdd() {
System.out.println("测试add方法");
int result = 1 + 1;
Assert.assertEquals(2, result); // 第一个参数:预测结果,第二个参数:实际结果
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
方法执行开始
测试add方法
方法执行结束
# 且测试通过
2
3
4
5
# 文档注解
@author//标识一个类的作者
@deprecated // 指名一个过期的类或成员
{@docRoot} // 指明当前文档根目录的路径
@exeption // 标志一个类抛出的异常
@param // 说明一个方法的参数
@return // 说明返回值类型
@since // 标记当引入一个特定的变化时
@throws // 和 @exception 一样
2
3
4
5
6
7
8
一般使用在文档注释中,配合
javadoc
工具
# JDK 提供的 3 个内置注解
@Override
:限定重写父类方法,该注解只能用于此方法@Deprecated
:用于表示所修饰的元素(类,方法,构造器,属性等)已过时SuppressWarnings
:抑制编译器警告
public class Person {
public void eat() {
System.out.println("父类eat方法");
}
}
public class Student extends Person {
@Override
public void eat() { // 若果你这边方法名写错了,会提示你
System.out.println("子类重写eat方法");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
废弃注解,比如Date
类中的getMonth
方法,在IDEA
中使用的时候会有横线,表示方法已废弃或者过期方法,或者过时的方法
@Deprecated
public int getMonth() {
return normalize().getMonth() - 1; // adjust 1-based to 0-based
}
2
3
4
抑制编译期警告
public class Test {
public static void main(String[] args) {
@SuppressWarnings("unused")
int age = 10; // 正常来说,IDEA只定义不使用,是灰色,加上这个注解标明它未使用,Eclipse比较明显
// 抑制泛型
@SuppressWarnings({"unused", "rwatypes"})
ArrayList al = new ArrayList();
}
}
2
3
4
5
6
7
8
9
10
11
# 代替配置文件功能的注解
Servlet
等,这个去百度吧,感觉有点过时了。
# 自定义注解
使用很少,一般都是用现成的注解。
警告
@interface
和接口没有半毛钱关系!
以@SuppressWarnings
为例,内部String[] value();
提示
看上去是无参方法,实际上理解为一个成员变量,一个属性。
无参方法名字:成员变量的名字
无参方法的返回值:成员变量的类型
这个参数叫配置参数
无参数方法的类型:基本数据类型(8 种)、String、枚举、注解类型,还可以是以上类型对应的数组
注意:如果只有一个成员变量的话,名字尽量叫value
。
使用注解:
如果你定义了配置参数,就必须给赋值操作
@MyAnnotation(value = {"abc", "def", "hij"}) // 传入一个String的数组
public class Person {
}
2
3
如果只有一个参数并且这个参数的名字为value
的话,那么value=
可以省略不写
@MyAnnotation({"abc", "def", "hij"})
public class Person {
}
2
3
如果你给配置参数设置了默认值,那么使用的时候可以无需传值
public @interface MyAnnotation1 {
String value() default "abc";
}
@MyAnnotation1
@MyAnnotation({"abc", "def", "hij"})
public class Person {
}
2
3
4
5
6
7
8
一个注解的内部是可以不定义配置参数的
public @interface MyAnnotation2 {
}
2
内部没有定义配置参数的注解,可以称之为标记
,例如:@Override
内部定义配置参数的注解,可以称之为元数据
# 元注解
元注解时用于修饰其他注解的注解。
JDK5.0 提供了四种元注解:
Retention,Target,Documented,Inherited
# Retention
public enum RetentionPolicy {
/**
* 只在原文件有效
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用户声明注解的生命周期
RetentionPolicy.SOURCE,编译器直接丢弃这种策略的注解,在.class
文件中不会保留
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// 只在源文件有效 字节码文件中没有这个注解
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
String[] value();
}
2
3
4
5
6
7
8
@Retention(RetentionPolicy.CLASS)
在class
文件中有效,保留在.class
文件中,但是运行 Java 程序时,他就不会继续加载了,不会保留在内存中,JVM 不会保留注解。如果在注解中没有特别显式的声明,它的生命周期默认为CLASS
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {
String[] value();
}
2
3
4
5
6
7
RetentionPolicy.RUNTIME,在运行时有效,即运行时保留,当运行 Java 程序时,JVM 会保留注解,加载在内存中,那么程序可以通过反射获取该注解。
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String[] value();
}
2
3
4
5
6
7
# Target
用于修饰注解的注解,用于指定被修饰的注解能用于修饰哪些程序元素,
@Target
也包含一个名为value
的成员变量
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
// 可以修饰的 类型
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface MyAnnotation3 {
}
2
3
4
5
6
7
8
public class Student {
@MyAnnotation3
int age = 10;
@MyAnnotation3
public Student() {
}
@MyAnnotation3
public void eat() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Documented(用的很少)
用于指定被该元注解修饰的注解类将被 javadoc 工具 提取成文档,默认情况下,javadoc 是不包括注解的,但是加上了这个注解生成的文档中会带着注解。
案例:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
2
3
4
5
那么Deprecated
注解就会在 javadoc 提取的时候,提取到 API 中。
# Inherited(极少)
被它修饰的
Annotation
将具有继承性,如果某个类使用了被@Inherited
修饰的注解,则其子类将自动具有该注解。
import java.lang.annotation.Inherited;
@Inherited
public @interface MyAnnotation4 {
}
2
3
4
5
6
@MyAnnotation4
public class Person {
}
2
3
4
// 这边会有一个 @MyAnnotation4
public class Student extends Person {
// 此时如果使用反射获取该类的注解,则会从Person中的注解也会获得
}
2
3
4
5