注解
一, 认识注解
先看百度百科对 java 注解的解释:
定义: 注解 (Annotation), 也叫元数据. 一种代码级别的说明. 它是 JDK1.5 及以后版本引入的一个特性, 与类, 接口, 枚举是在同一个层次. 它可以声明在包, 类, 字段, 方法, 局部变量, 方法参数等的前面, 用来对这些元素进行说明, 注释.
作用分类:
1编写文档: 通过代码里标识的元数据生成文档 [生成文档 doc 文档]
2 代码分析: 通过代码里标识的元数据对代码进行分析 [使用反射]
3编译检查: 通过代码里标识的元数据让编译器能够实现基本的编译检查 [Override]
提到的元数据, 可以理解为描述数据的数据, 看起来很绕, 元注解也是描述注解的注解, 后面说.
Annotation 都是 java.lang.annotation.Annotation 接口的子接口, 注解是一种特殊的接口, 从代码格式上来看确实是, 是在 interface 前加了一个 @符号, 格式为 @interface xxx{}
二, JDK 内置注解
jdk 就存在注解, 比如我们用 eclipse 子类覆盖父类方法时候用快捷间 alt+/ 会自带在方法名称上面贴上一个 @Override 的标签.
@Override
这个就是在子类覆盖父类的方法时候, 经常遇到
@Deprecated
这个元素是用来标记过时的元素, 想必大家在日常开发中经常碰到. 编译器在编译阶段遇到这个注解时会发出提醒警告, 告诉开发者正在调用一个过时的元素比如过时的方法, 过时的类, 过时的成员变量. 如 java.util.Data 类中有很多过时的方法
@SupressWarings
抑制警告, 有很多人有代码洁癖, 看到黄色的警告不爽, 用它就可以抑制住.
@SafeVarargs
java7 出现的, 抑制堆污染警告, 有点自欺欺人的感觉.
三, 元注解
元注解就是注解的注解, 来看一下有哪些元注解
@Retention
Retention 英文是保留的意思, 在这里可以约束注解的存活周期, 代码运行有三个周期, 分别为 Source(源代码),Class(字节码),Runtime(运行时期) 三个时期的值保留在 RetentionPolicy 这个枚举类中. 所以我们可以这样来玩,
- @Retention(RetetionPolicy. 常量值)//RESOURCE,CLASS,RUNTIME
- @Target
Target 是目标的意思, 这里约束这个注解在那可以贴 (类, 方法, 构造器, 参数等), 位置的常量在 ElementType 这个枚举类中
- @Target({
- ElementsType. 常亮值
- })//TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE 表示类, 接口, 枚举
- @Documented
Document 文档的意思, 也就是使用这个标签到时候会带到 API 文档中去
@Inherited
Inherited 是遗传的意思, 顾名思义, 这个注解会遗传到子类去
@Repeatable 可重复的, Java8 的一个新特性
四, 自定义注解
1. 注解的属性
在注解中定义属性, 必须是 8 种基本数据类型外加 类, 接口, 注解及它们的数组. 不能是 Integer.. 注解中属性可以有默认值, 默认值需要用 default 关键值指定.
如:
- @interface TestAnnotation{
- int id() default 0;
- String msg() default "hello";
- }
- @TestAnnotation(id = 3,msg = "aa")
- class Test1{
- }
- // 当注解中只有一个 value 属性时候, 贴标签的时候可以省略 value.
- // 当注解中没有属性时, 括号都可以省略 (override 注解)
- @interface Test1Annotation{
- int[] value();
- }
- @Test1Annotation({1,2})
- class Test2{
- }
2. 用反射操作注解
注解可以在类 Class, 方法 Method, 字段 Field, 构造器 Constructor 上等, 所以在各自的类中都存在获取注解的 API
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定类型的注释存在于此元素上, 则返回 true, 否则返回 false.
Annotation[] getAnnotations() 返回此元素上存在的所有注释.
<Annotation> getAnnotation(Class annotationClass) 如果存在该元素的指定类型的注释, 则返回这些注释对象, 否则返回 null. 获取注解对象
自定义一个注解:
- @Target({TYPE, FIELD, METHOD})// 注解可以放的目标
- @Retention(RetentionPolicy.RUNTIME)// 注解保留周期
- public @interface AnnotationTest {
- int id();
- String name();
- }
测试一下这个自定义的注解:
- @AnnotationTest(id = 1,name = "na")
- class Test{
- public static void main(String[] args) throws Exception {
- // 判断 Test(类元素) 是否应用了这个注解
- boolean hasAnnotation = Test.class.isAnnotationPresent(AnnotationTest.class);
- if(hasAnnotation){
- // 获取注解对象
- AnnotationTest t = Test.class.getAnnotation(AnnotationTest.class);
- System.out.println(t.name());
- System.out.println(t.id());
- // 获取所有的注解 s
- Annotation[] ans = Test.class.getAnnotations();
- for (Annotation an : ans) {
- System.out.println(an);
- }
- }
- System.out.println("============");
- // 获取方法中上的注解
- boolean has = Test.class.getMethod("work").isAnnotationPresent(AnnotationTest.class);
- if(has){
- AnnotationTest an = Test.class.getMethod("work").getAnnotation(AnnotationTest.class);
- System.out.println(an.name());
- }
- }
- @AnnotationTest(id = 0,name="hello")
- public void work(){
- }
- }
结果为:
- na
- 1
- @annotation.AnnotationTest(id=1, name=na)
- ============
- hello
五, 注解的用途
实践: 模拟 Junit4.x
Junit 是做单元测试的, junit4 用了注解, 用法是在一个公共无参的方法贴上一个 @Test 标签表示要测试这个方法, 但是在测试之前有可能会出现一些初始化操作, 在结束后可能需要释放一些资源, 需要初始化的方法上面贴上一个 @Before,Test 之后需要执行的需要贴上 @After.
现在我们来自定义注解来模拟一下这些操作, 需要在这里提的是, 其实注解本身没什么意义, 需要人为的设置它完成一些功能.
我们先定义三个注解:
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface MyAfter {
- }
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface MyBefore {
- }
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface MyTest {
- }
来一个测试这些注解功能的类
- public class Employee {
- @MyBefore
- public void init(){
- System.out.println("------- 初始化 -------");
- }
- @MyAfter
- public void end(){
- System.out.println("------- 销毁 ---------");
- }
- @MyTest
- public void save() {
- System.out.println("保存操作");
- }
- @MyTest
- public void delete() {
- System.out.println("删除操作");
- }
- @MyBefore
- public void ddd(){
- System.out.println("怎么说呢?");
- }
- }
好了, 接下来我们就得具体去设置这个注解的意义
思路和步骤:
获取 Employee 类的字节码对象, 得到所有的方法
对所有方法进行迭代, 将三类注解分别存储, 把有 @MyBefore 注解的存在 beforeList, 带有 @MyAfter 存在 afterList, 带有 @MyTest 存在 testList
对 testList 集合迭代, 在迭代 过程中先执行 beforeList 中的方法, 在执行 afterList 中的方法
- public class JunitTest {
- public static void main(String[] args) throws Exception {
- // 得到 Employee 中所有的方法
- Method[] mds = Employee.class.getDeclaredMethods();
- // 创建 Employee 的对象, 用来执行方法
- Employee e = Employee.class.newInstance();
- // 用集合存储三类注解标注的方法
- List<Method> beforeList = new ArrayList<>();
- List<Method> afterList = new ArrayList<>();
- List<Method> testList = new ArrayList<>();
- // 对所有的方法进行迭代, 分类
- for (Method md : mds) {
- if(md.isAnnotationPresent(MyBefore.class)){
- beforeList.add(md);
- }else if(md.isAnnotationPresent(MyAfter.class)){
- afterList.add(md);
- }else if(md.isAnnotationPresent(MyTest.class)){
- testList.add(md);
- }
- }
- // 在对 test 迭代的过程中, 先执行所有被 before 注解了的方法, 然后执行 test 注解的方法, 最后再执行 after 注解了的方法
- for (Method method : testList) {
- for (Method before : beforeList) {
- before.invoke(e); // 方法都没有参数
- }
- method.invoke(e);
- for (Method after : afterList) {
- after.invoke(e);
- }
- }
- }
- }
结果:
------- 初始化 -------
怎么说呢?
删除操作
------- 销毁 ---------
------- 初始化 -------
怎么说呢?
保存操作
------- 销毁 ---------
来源: https://www.cnblogs.com/tfper/p/9933219.html