Java注解
注解
什么是注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关
于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
注解的作用
- 标记,如告诉编译器一些信息
- 编译时动态处理,如动态生成代码
- 运行时动态处理,如得到注解信息
声明一个注解类型
Java 中所有的注解,默认实现`Annotation 接口:
1
2
3
4
5
6
7
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
与声明一个 “Class” 不同的是,注解的声明使用 @interface 关键字。一个注解的声明如下:
1
2
public @interface Lance{
}
元注解(4 个)
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 metaannotation(元注解)。
元注解指的是用来定义注解的注解
@Retention
@Retention 注解指定标记注解的存储方式,保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
- SOURCE 这个 Annotation 的信息保留在程序源代码里,源码如果经过了编译之后,Annotation 的数据就会消失,并不会保留在编译好的.class 文件里面。大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override,Deprecated,SuppressWarnings
- CLASS 这个 Annotation 类型的信息保留在程序源码里,同时也会保留在编译好的.class 文件里面,在执行的时候,并不会把这一些信息加载到虚拟机 (JVM) 中去.注意一下,当你没有设定一个 Annotation 类型的 Retention 值时,系统默认值是 CLASS。应用 APT?
- RUNTIME 在源码、编译好的.class 文件中保留信息,在执行的时候会把这一些信息加载到 JVM 中去的。比如,运行时 Ioc 框架利用反射获取某个对象上的注解信息
@Target
注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:
1
2
3
4
5
6
7
8
ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
@Inherited
@Inherited 是否可以被继承,默认为 false。 该注解可以被自动继承。如果自定义注解声明了@Inherited,子类就会沿着继承树去查找有改表示的注解。除了在类上使用外,用在其他地方的都不起作用。只能在继承的情况下起作用,如果实现一个借口@Inherited 也是不起作用的
@Documented
@Documented 是否会保存到 Javadoc 文档中,用于被 javadoc 工具提取成文档
标准注解(3 个)
Java 自带的几个注解
@Override
@Override 重写函数
@Deprecated
@Deprecated 废弃,不鼓励使用 (有更好方式、使用有风险或已不在维护)
@SuppressWarnings
@SuppressWarnings 忽略某项 Warning
自定义注解以及属性定义
- 1、通过 @interface 定义,注解名即为自定义注解名
- 2、声明注解的属性
注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。- a) Annotation 属性声明方式:
String name();
- b) Annotation 属性默认值声明方式:
String name() default "xxx";
- c) Annotation 属性的特殊属性 value:如果注解中有一个名称 value 的属性,那么使用注解时可以省略 value=部分:
@MyAnnotation("xxx")
- d) 还有一个特殊属性 value[];和 value 属性一样,可以省略,只写值,多个字用大括号{}包起来,如:
@MyAnnotation3({"hacket","hacket2"})
;如果数组属性中只有一个元素,这时候属性值部分可以省略大括号 如:@MyAnnotation3("hacket")
- a) Annotation 属性声明方式:
总结:
- (1) 通过 @interface 定义,注解名即为自定义注解名
- (2) 注解配置参数名为注解类的方法名,且:
- a. 所有方法没有方法体,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常
- b. 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组
- c. 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation
- (3). 可以加 default 表示默认值
1
2
3
4
5
6
7
8
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
String name(); // 注解属性声明
int age();
String gender() default "male"; // 注解属性的默认值
MyAnnotation2[] initParam(); // 注解属性为注解类型的数组
}
1
2
3
4
5
6
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
String paramName() default "";
String paramValue() default "";
}
1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
// String value();
// int value();
String[] value();
}
注解反射
具体的 api
1
2
3
method.getAnnotation(AnnotationName.class); // 得到该 Target 某个 Annotation 的信息,因为一个 Target 可以被多个 Annotation 修饰
method.getAnnotations(); // 得到该 Target 所有 Annotation
method.isAnnotationPresent(AnnotationName.class); // 表示该 Target 是否被某个 Annotation 修饰
实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Test {
// @MyAnnotation1(name = "hacket", age = 1, initParam = {@MyAnnotation2(paramName = "paramName", paramValue = "paramValue") })
// @MyAnnotation3("hacket")
// @MyAnnotation3(22)
@MyAnnotation3({ "hacket", "hacket2" })
@MyAnnotation1(name = "hacket", age = 1, initParam = { @MyAnnotation2(paramName = "paramName", paramValue = "paramValue") })
static class TestAnnotaion {
@MyAnnotation2(paramName = "hacket2", paramValue = "value2")
void test(String param, String value) {
System.out.println("test..." + param + ":" + value);
}
void test2() {
System.out.println("test2...");
}
}
public static void main(String[] args) throws Exception {
// 注解是否存在
boolean present = TestAnnotaion.class
.isAnnotationPresent(MyAnnotation1.class);
System.out.println(present);
boolean present3 = TestAnnotaion.class
.isAnnotationPresent(MyAnnotation3.class);
System.out.println(present3);
Method[] methods = TestAnnotaion.class.getDeclaredMethods();
for (Method method : methods) {
boolean present2 = method.isAnnotationPresent(MyAnnotation2.class);
if (present2) {
MyAnnotation2 annotation = method
.getAnnotation(MyAnnotation2.class); // 看方法上是否有MyAnnotation2注解
String paramName = annotation.paramName(); // 得到MyAnnotation2注解属性上的值
String paramValue = annotation.paramValue();
method.invoke(TestAnnotaion.class.newInstance(), paramName,
paramValue);
}
}
}
}
结果:
1
2
3
true
true
test...hacket2:value2
注解继承
父类的类上和方法上有自定义注解,子类继承父类后的情况:
自定义注解未写 @Inherited | 自定义注解写了 @Inherited | |
---|---|---|
子类类上能否继承到父类的类上注解 | 否 | 能 |
子类方法,重写了父类的抽象方法,这个方法能否继承到注解 | 否 | 否 |
子类方法,继承了父类上的方法,这个方法能否继承到注解 | 能 | 能 |
子类方法,覆盖了父类上的方法,这个方法能否继承到注解 | 否 | 否 |
@Inherited 修饰的注解只能作用于类上有效,作用在方法上不能继承;@Inherited 修饰的注解只能作用类上,在接口上继承失效
自定义注解
一、自定义 Annotation
- 1、定义新的 Annotation 类型使用@interface 关键字
- 2、声明注解的属性
注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。
- a) Annotation 属性声明方式:
String name();
- b) Annotation 属性默认值声明方式:
String name() default "xxx";
- c) Annotation 属性的特殊属性 value:如果注解中有一个名称 value 的属性,那么使用注解时可以省略 value=部分:
@MyAnnotation("xxx")
- d) 还有一个特殊属性 value[];和 value 属性一样,可以省略,只写值,多个字用大括号{}包起来
注意: 如果自定义注解中只有一个属性 value 或者一个 value 数组,如果有多个值,那么 value 不能省略
二、案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public @interface MyAnnotation1 {
String name(); // 注解属性声明
int age();
String gender() default "male"; // 注解属性的默认值
MyAnnotation2[] initParam(); // 注解属性为注解类型的数组
}
public @interface MyAnnotation2 {
String paramName() default "";
String paramValue() default "";
}
public @interface MyAnnotation3 {
// String value();
// int value();
String[] value();
}
// 应用了注解
public class Test {
@MyAnnotation1(name = "hacket", age = 1, initParam = { @MyAnnotation2(paramName = "paramName", paramValue = "paramValue") })
// @MyAnnotation3("hacket")
// @MyAnnotation3(22)
@MyAnnotation3({ "hacket", "hacket2" })
}