JavaSe·高级特性篇(三) Java高级特性注解
1. 注解(Annotation)概述
1.1 定义
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
1.2 与注释的区别
注解:说明程序的。给计算机看的
注释:用文字描述程序的。给程序员看的
2. 作用分类
①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
3. JDK中预定义的一些注解
- @Override :检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated:该注解标注的内容,表示已过时
- @SuppressWarnings:压制警告
- 一般传递参数all @SuppressWarnings(“all”)
4. 自定义注解
4.1 格式
元注解
public @interface 注解名称{
属性列表;
}
4.2 本质
注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
4.3 属性
要求:
-
属性的返回值类型有下列取值
* 基本数据类型
* String
* 枚举
* 注解
* 以上类型的数组
-
定义了属性,在使用时需要给属性赋值
* 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
* 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
* 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
@MyAnnotation(value="哈哈哈")
public class MyAnnotationTest {
public static void main(String[] args) {
Class clazz = MyAnnotationTest.class;
Annotation a = clazz.getAnnotation(MyAnnotation.class);
MyAnnotation m = (MyAnnotation) a;
String info = m.value();
System.out.println(info);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnnotation{
String value() default "hehehe";
}
5. 元注解
JDK 的元 Annotation 用于修饰其他 Annotation 定义
JDK5.0提供了4个标准的meta-annotation类型,分别是:
- Retention
- Target
- Documented
- Inherited
5.1 @Retention
只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 的生命周期
@Rentention 包含一个 RetentionPolicy 类型的成员变量,使用@Rentention 时必须为该 value 成员变量指定值:
- RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释
- RetentionPolicy.CLASS:在class文件中有效(即class保留) , 当运行 Java 程序时, JVM不会保留注解。 这是默认值
- RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行 Java 程序时, JVM 会保留注释。程序可以通过反射获取该注释。
public enum RetentionPolicy{
SOURCE,
CLASS,
RUNTIME
}
5.2 @Target
用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素
5.3 @Documented
用于指定被该元 Annotation 修饰的 Annotation 类将被javadoc 工具提取成文档。默认情况下,javadoc是不包括注解的。
定义为Documented的注解必须设置Retention值为RUNTIME。
5.4 @Inherited
被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。
比如:如果把标有@Inherited注解的自定义的注解标注在类级别上,子类则可以继承父类类级别的注解。实际应用中,使用较少
6. 利用反射获取注解信息
- JDK 5.0 在 java.lang.reflect 包下新增了 AnnotatedElement 接口, 该接口代表程序中可以接受注解的程序元素
- 当一个 Annotation 类型被定义为运行时 Annotation 后, 该注解才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取
- 程序可以调用 AnnotatedElement对象的如下方法来访问 Annotation 信息
7. JDK8中注解的新特性
Java 8对注解处理提供了两点改进
此外,反射也得到了加强,在Java8中能够得到方法参数的名称。这会简化标注在方法参数上的注解。
7.1 可重复注解
@Repeatable(MyAnnotations.class)
@Target({TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
String value();
}
@MyAnnotations("hello")
@MyAnnotations("world")
public void show(){
}
7.2 类型注解
JDK1.8之后,关于元注解@Target的参数类型ElementType枚举值多了两个:TYPE_PARAMETER,TYPE_USE
- ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)
public class TestTypeDefine<@TypeDefine() U> {
private U u;
public <@TypeDefine() T> void test(T t){
}
}
@Target({ElementType.TYPE_PARAMETER})
@interface TypeDefine{
}
- ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
@MyAnnotation
public class AnnotationTest<U> {
@MyAnnotation
private String name;
public static void main(String[] args) {
AnnotationTest<@MyAnnotation String> t = null;
int a = (@MyAnnotation int) 2L;
@MyAnnotation
int b = 10;
}
public static <@MyAnnotation T> void method(T t) {
}
public static void test(@MyAnnotation String arg) throws @MyAnnotation Exception {
}
}
@Target(ElementType.TYPE_USE)
@interface MyAnnotation {
}
8. 模拟测试框架
- 写个MyTest.java注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest{
String name() default "lzcc";
}
- Step2:写个测试类TestDemo.java
class TestDemo{
@MyTest
public void test1(){
System.out.println("test1 方法执行了!");
}
@MyTest(name = "詹姆斯")
public void test2(){
System.out.println("test2 方法执行了!");
}
}
Step3:写个TestRunner
//1.获取字节码文件
Class clazz = TestDemo.class
//2.获取该类的方法
Method[] methods = clz.getMethods();
for(Method m : methods){
//3.获取方法上的注解
MyTest myTest = m.getAnnotation(MyTest.class);
if(null != MyTest){
System.out.println("方法名:" + m.getName());
System.out.println("注解的name属性:" + myTest.name());
//4.执行测试放啊
m.invoke(clz.newInstance());
System.out.println("--------------------------------");
}
}