前言
使用JAVA语言,就少不了提到注解。不管是看JDK中源码还是自己开发项目,都会用的。注解是java5引入的概念,那么先来详细的了解下什么是注解,有什么分类。怎么用?
参考资料
https://www.cnblogs.com/xdp-gacl/p/3622275.html
什么是注解
注解可以看做一种元数据,即一种描述数据的数据。注解本身是描述其他数据的。在jav中,注解就是源代码的元数据。比如,下面这段代码:
@Override
public String toString() {
return “This is String Representation of current object.”;
}
其中的Override就是一个重写的注解。注解在一个方法上,含义是告诉编译器在编译时候对此方法进行编译检查。如参数或者函数名称错误,可以提示编译错误,避免在运行时候才发现。
标准定义:
Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
注解分类
注解分元注解和自定义注解。
1. 元注解
元注解是一个特殊的注解,特殊在于可以注解到其他注解上。
元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
如以下代码::
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
TestAnnotation 可以在运行时候访问到。
如上3中注解的保留期不一样,访问方式也不一样,后面注解的获取中会讲到怎么获取有注解的类或者方法。
@Documented
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。暂时放过。
@Target
Target 是目标的意思,@Target 指定了注解运用的地方。限定了运用的场景。
如注解可以应用于方法上、类上、方法参数上等等。取值如下:
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTOR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.PARAMETER 可以给一个方法内的参数进行注解
- ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。注解的值可以同时取多个可以使用。暂时放过。
注解定义
注解通过 @interface 关键字进行定义的一个类。如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg();
}
注解属性
注解的属性即注解类的成员属性,注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
如上定义的id、msg
注解使用
定义好了注解类,怎么使用到目标代码上呢?使用时候如下,@加注解类到目标代码上。
@TestAnnotation(id=3,msg=“hello annotation”)
public class Test {
}
注解中属性可以有默认值,默认值需要用 default 关键值指定。比如:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id() default -1;
String msg() default “hello”;
}
注解数据获取
参考:https://blog.youkuaiyun.com/u014207606/article/details/52293939
A、对于运行时注解,@Retention(RetentionPolicy.RUNTIME) 获取如下:
类的注解获取:
1.先判断是否用了某个注解?
通过 Class 对象的 isAnnotationPresent() 方法判断。
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
}
2、然后通过 getAnnotation() 方法来获取 Annotation 对象,得到获取到类的注解。
@TestAnnotation()
public class Test {
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
}
}
方法或者成员注解获取:
通过反射获取到成员或者方法,在获取到注解,得到注解数据。
@TestAnnotation(msg="hello")
public class Test {
@Check(value="hi")
int a;
@Perform
public void testMethod(){}
@SuppressWarnings("deprecation")
public void test1(){
Hero hero = new Hero();
hero.say();
hero.speak();
}
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
//获取类的注解
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
try {
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
//获取一个成员变量上的注解
Check check = a.getAnnotation(Check.class);
if ( check != null ) {
System.out.println("check value:"+check.value());
}
Method testMethod = Test.class.getDeclaredMethod("testMethod");
if ( testMethod != null ) {
// 获取方法中的注解
Annotation[] ans = testMethod.getAnnotations();
for( int i = 0;i < ans.length;i++) {
System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
B、编译时注解@Retention(RetentionPolicy.SOURCE),需要通过注解解释器获取
注解解释器后续再介绍。先可以参考https://segmentfault.com/a/1190000009756015
总结
注解的基本语法,创建如同接口,但是多了个 @ 符号。
注解的元注解5个:@Retention、@Documented、@Target、@Inherited、@Repeatable
注解的属性:即接口的成员。一般是基本数据类型
运行注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。(定义注解时使用了@Retention(RetentionPolicy.RUNTIME)修饰,JVM才会在装载class文件时提取保存在class文件中的注解,该注解才会在运行时可见,这样我们才能够解析。)
JAVA默认的注解@Deprecated、@Override、@SuppressWarnings、@FunctionalInterface等。