Java注解Annotation的使用

时间:2022-01-17 20:13:48

最近看到很多框架都在用注解,包括Android的和javaweb 的,但是对java注解确实不了解,就像之前对反射一样“只知其名,不知其用”,在昨天看了用法之后在这里做一个总结。

根据我自己的理解,“注解”这个翻译确实很到位,不像反射一样给人摸不着头脑的感觉。“注解”看着其实很像另一个名词“注释”,他们俩的作用确实有些像,比如都是用一个东西来解读另一个东西,只是“注释”是用自然语言对该段代码进行解释让看的人清楚意思,而注解更多的是对代码进行解释让编译器清楚意思。我后来又想了一下,好像确实没有比“注解”更合适的翻译了。而且“注解”更偏向的是“解”,像解方程一样对其进行处理。

下面说一下Annotation的用法。
Annotation里面有元注解和自定义注解之分,元注解是对自定义注解进行定义的,我把它比作定义类的一些关键字。元注解一共包含以下四个,
四个元注解分别是:@Target,@Retention,@Documented,@Inherited ,其作用分别如下:

@Target: 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:
ElemenetType.CONSTRUCTOR———————-构造器声明
ElemenetType.FIELD ———————————–域声明(包括 enum 实例)
ElemenetType.LOCAL_VARIABLE——————– 局部变量声明
ElemenetType.METHOD —————————–方法声明
ElemenetType.PACKAGE —————————– 包声明
ElemenetType.PARAMETER ————————–参数声明
ElemenetType.TYPE————————————- 类,接口(包括注解类型)或enum声明

@Retention: 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:
RetentionPolicy.SOURCE ———————————注解将被编译器丢弃
RetentionPolicy.CLASS—————————-注解在class文件中可用,但会被VM丢弃
RetentionPolicy.RUNTIME VM——-将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
@Documented: 将此注解包含在 javadoc 中,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
@Inherited: 允许子类继承父类中的注解。

其中“Target”和“Retention”用的比较多。一个是说明该注解的作用的对象,一个是作用在哪些情况下。有些像类的权限符和对象的存活周期。

在使用注解的使用要使用“@”,比如使用元注解的时表示为“@Target”,如@Target(ElemenetType.FIELD)表示的是作用在类的属性中。同时,在自定义注解时也要用到“@”,并且还要用到的一个关键字是“interface”,组合起来就是“@interface”,如

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface A_Class {
String value();
}

以上就是定义一个注解的方法,是不是猛一看以为是接口呢,其实我在不了解这是注解定义的时候也以为它就是接口呢,了解之后才发现“哦!原来你就是注解啊!伪装得很深啊!”。

可以看到上面这个注解是有一个参数的,这个参数的类型就是String 参数名是“value”,只是这个后面是一个括号,不明白的还以为是一个抽象方法呢,我后来想之所以用interface,又用这个的定义方法,确实也是又道理的,因为它的使用是有些像接口的感觉,都是只是定义了,然后本身没有什么用处,只是在里面填写东西之后才有些用处。

在看讲解的时候注意到有一个一般使用习惯,就是在参数只有一个的时候一般将参数名设置为“value”,其实我倒是觉得这些无所谓,但是一般规范的话,大家可以了解一下。

在定义参数的时候可能会需要默认值,定义默认值的方法也非常的简单,例如为上面的value定义默认值的话就是下面这样:

String value() default "nn";

也就是在value后面加一个default,后面接一个值就行了。

注解的使用基本就是这些了,对了,Java本身还有五个内置的注解,它们虽然是系统定义好的,但和自定义的注解没有什么区别,所以我把它们也列为自定义注解一类。它们分别是

@Override:
子类中被@Override 修饰的方法,如果存在对应的被重写的父类方法,则正确;如果不存在,则报错。
@Override 只能作用于方法,不能作用于其他程序元素。

@Deprecated
用于表示某个程序元素(类、方法等)已过时。如果使用了被@Deprecated修饰的类或方法等,编译器会发出警告。
@SuppressWarnings
抑制编译器警告。指示被@SuppressWarnings修饰的程序元素(以及该程序元素中的所有子元素,例如类以及该类中的方法…..)取消显示指定的编译器警告。
例如,常见的@SuppressWarnings(value=”unchecked”)表示当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告
@SafeVarargs:
@SafeVarargs是JDK 7 专门为抑制“堆污染”警告提供的。

@FunctionalIterface:
(Java 8 新增的)
函数式接口。Java8规定:如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。
@FunctionalInterface就是用来指定某个接口必须是函数式接口,否则就会编译出错。

至于怎么让注解产生作用呢,那就要用到反射机制了,可以通过反射

Class clazz=Class.forName("com.test.A");
Annotation[] arr=clazz.getAnnotations();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

上述clazz.getAnnotations()方法可以获得包含类A中所有注解的数组

Field field=clazz.getField("name");
A_Field a_f=field.getAnnotation(A_Field.class);
System.out.println(a_f.value());

上面这段代码的作用是先获得A中名为“name”的Field,之后通过注解A_Field类获得A_Field的对象 a_f,在获得a_f对象之后就可以像别的对象一样对注解中的属性进行读取了。

关于注解我的理解和使用目前就是这些,有什么错误欢迎批评指正!