自定义注解详解

文章引用

深入理解Java:注解(Annotation)自定义注解入门

自定义注解详细介绍

说在最前

文章忽略特性的一些基本概念

注解的本质是反射

1. 自定义注解

注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测到这些标记从而执行一些特殊操作

  • 第一步:定义注解——相当于定义标记
  • 第二步:配置注解——把标记打在需要用到的程序代码中
  • 第三部:解析注解——在编译期或运行时监测到标记,并进行特殊操作

1.2 基本语法

注解类型的声明部分:

注解在Java中,与类、接口、枚举类似,因此其声明语法基本一致,只是所使用的关键字有所不同@interface在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。在自定义注解中,其实现部分只能定义一个东西:注解类型元素(annotation type element)

public @interface MyAnnotation {public String name();int age() default 18; //赋予默认值int[] array();
}

1.2.1 定义注解类型的注意项

  1. 访问修饰符必须为public,不写默认为public;
  2. 该元素的类型只能是基本数据类型、String、Class、枚举类型(enum)、注解类型(annotation:体现了注解的嵌套效果)以及上述类型的一位数组;
  3. 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
  4. ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
  5. default代表默认值,值必须和第2点定义的类型一致;
  6. 如果没有默认值,代表后续使用注解时必须给该类型元素赋值。

可以看出,注解类型元素的语法非常奇怪,即又有属性的特征(可以赋值),又有方法的特征(打上了一对括号)。但是这么设计是有道理的,我们在后面的章节中可以看到:注解在定义好了以后,使用的时候操作元素类型像在操作属性,解析的时候操作元素类型像在操作方法

1.3 元注解

元注解:专门修饰注解的注解。它们都是为了更好的设计自定义注解的细节而专门设计的。

1.3.1 @Target

@Target注解,是专门用来限定某个自定义注解能够被应用在哪种Java元素上面的

public enum ElementType {/** 类,接口(包括注解类型)或枚举的声明 */TYPE,/** 属性的声明 */FIELD,/** 方法的声明 */METHOD,/** 方法形式参数声明 */PARAMETER,/** 构造方法的声明 */CONSTRUCTOR,/** 局部变量声明 */LOCAL_VARIABLE,/** 注解类型声明 */ANNOTATION_TYPE,/** 包的声明 */PACKAGE
}
//@CherryAnnotation被限定只能使用在类、接口或方法上面
@Target(value = {ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {String name();int age() default 18;int[] array();
}

1.3.2 @Retention

用来修饰自定义注解的生命力

注解的生命周期有三个阶段:

  1. .java源文件阶段;
  2. 编译到class文件阶段;
  3. 运行期阶段。同样使用了RetentionPolicy枚举类型定义了三个阶段:
public enum RetentionPolicy {/*** Annotations are to be discarded by the compiler.* (注解将被编译器忽略掉)*/SOURCE,/*** Annotations are to be recorded in the class file by the compiler* but need not be retained by the VM at run time.  This is the default* behavior.* (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)*/CLASS,/*** Annotations are to be recorded in the class file by the compiler and* retained by the VM at run time, so they may be read reflectively.* (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)* @see java.lang.reflect.AnnotatedElement*/RUNTIME
}
  1. 如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到;
  2. 如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它(例如@Override),我们在运行期也不能读取到;
  3. 如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME
  4. 在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS

1.3.3 @Documented

@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

1.3.4 @Inherited

@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

2. 自定义注解的使用

回顾一下注解的使用流程:

  • 第一步,定义注解——相当于定义标记;
  • 第二步,配置注解——把标记打在需要用到的程序代码中;
  • 第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。

到目前为止我们只是完成了第一步,接下来我们就来学习第二步,配置注解,如何在另一个类当中配置它。

2.1 在具体额Java类上使用注解

首先,定义一个注解、和一个供注解修饰的简单Java类

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@Documented
public @interface MyAnnotation {String name();int age() default 18;int[] score();
}
public class Student{public void study(int times){for(int i = 0; i < times; i++){System.out.println("Good Good Study, Day Day Up!");}}
}

2.2 特殊语法

特殊语法一:如果注解本身没有注解类型元素,那么在使用注解的时候可以省略(),直接写为:@注解名,它和标准语法@注解名()等效!

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface FirstAnnotation {
}
//等效于@FirstAnnotation()
@FirstAnnotation
public class JavaBean{//省略实现部分
}

特殊语法二:如果注解本本身只有一个注解类型元素,而且命名为value,那么在使用注解的时候可以直接使用:@注解名(注解值),其等效于:@注解名(value = 注解值)

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface SecondAnnotation {String value();
}
//等效于@ SecondAnnotation(value = "this is second annotation")
@SecondAnnotation("this is annotation")
public class JavaBean{//省略实现部分
}

特殊用法三:如果注解中的某个注解类型元素是一个数组类型,在使用时又出现只需要填入一个值的情况,那么在使用注解时可以直接写为:@注解名(类型名 = 类型值),它和标准写法:@注解名(类型名 = {类型值})等效!

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface ThirdAnnotation {String[] name();
}
//等效于@ ThirdAnnotation(name = {"this is third annotation"})
@ ThirdAnnotation(name = "this is third annotation")
public class JavaBean{//省略实现部分
}

3.自定义注解的运行时解析

系统是如何在程序运行时检测到注解,并进行一系列特殊操作

只有当注解的保持力处于运行阶段,即使用@Retention(RetentionPolicy.RUNTIME)修饰注解时,才能在JVM运行时,检测到注解,并进行一系列特殊操作。

3.1 反射操作获取注解

因此,明确我们的目标:在运行期探究和使用编译期的内容(编译期配置的注解),要用到Java中的灵魂技术——反射!

public class TestAnnotation {public static void main(String[] args) {try{//获取Student的Class对象Class stuClass = Class.forName("com.hong.example.Student");//这里的形参不能写成Integer.class,应该写int.classMethod stuMethod = stuClass.getMethod("study",int.class);if (stuMethod.isAnnotationPresent(MyAnnotation.class)){System.out.println("Student类上配置了MyAnnotation注解!");//获取该元素上指定类型的注解MyAnnotation myAnnotation = stuMethod.getAnnotation(MyAnnotation.class);System.out.print("name: "+myAnnotation.name()+", age: "+myAnnotation.age()+", score:");for (int i = 0 ;i < myAnnotation.score().length;i++){System.out.print(myAnnotation.score()[i]+" ");}}else{System.out.println("Student类上没有配置MyAnnotation注解");}System.out.println();}catch (ClassNotFoundException | NoSuchMethodException e){e.printStackTrace();}}
}
  1. 如果我们要获得的注解是配置在方法上的,那么我们要从Method对象上获取;如果是配置在属性上,就需要从该属性对应的Field对象上去获取,如果是配置在类型上,需要从Class对象上去获取。总之在谁身上,就从谁身上去获取!
  2. isAnnotationPresent(Class<? extends Annotation> annotationClass)方法是专门判断该元素上是否配置有某个指定的注解;
  3. getAnnotation(Class<A> annotationClass)方法是获取该元素上指定的注解。之后再调用该注解的注解类型元素方法就可以获得配置时的值数据;
  4. 反射对象上还有一个方法getAnnotations(),该方法可以获得该对象身上配置的所有的注解。它会返回给我们一个注解数组,需要注意的是该数组的类型是Annotation类型,这个Annotation是一个来自于java.lang.annotation包的接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/254677.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

查看 HTTP 请求的数据.

文章结构 如果是 GET 请求如果是 POST 请求方法1&#xff1a;DEBUG 窗口&#xff08;**爽、超级爽、吴迪爽**&#xff09;&#xff1a;方法2&#xff1a;写方法读取流中数据&#xff08;繁琐&#xff0c;难用&#xff09;&#xff1a; 我们可能会碰到 MVC 拿不到前端的参数&…

基于Html5的在线资料库的设计与实现(asp.NET,SQLServer)

在线资料库系统采用.NET开发平台进行开发&#xff0c;开发工具采用Microsoft Visual Studio 2010集成开发环境&#xff0c;后台编程语言采用C#编程语言来进行编程开发&#xff0c;数据库我们采用当下流行的SQL Server 2008数据库管理系统来存放平台中的数据信息&#xff0c;整个…

【软硬件测试】测试经验:软硬件结合测试要点

目录 一、应用行业 二、测试要点 三、硬件测试 &#xff08;1&#xff09;测试含义 &#xff08;2&#xff09;测试方法 &#xff08;3&#xff09;相关链接 四、结合测试 &#xff08;1&#xff09;测试含义 &#xff08;2&#xff09;测试工具 &#xff08;3&am…

【ros/ros2】LCN及ros2节点的LCN改写

文章目录 序言1. ros2两种节点类型2. LCN是什么3. LCN状态转换4. LCN状态转换要做的事5. LCN节点功能划分6. ros2节点的LCN改写 序言 背景&#xff1a;ros2节点改写为lifecycle node节点 1. ros2两种节点类型 Node&#xff1a;和ros1中一样的节点基类LifecycleNode&#xff…

桶排序 — 计数排序和基数排序

计数排序 int类型数组&#xff0c;其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲&#xff0c;数据状况是受限的。此时如果将数组从小到大进行排序&#xff0c;该如果实现&#xff1f; 这个实现很简单&#xff0c;实现一个统计数组范围从 0 ~ 150&#xff0c;遍历原…

816墨盒计算机无法与,816墨盒怎么加墨 816墨盒加墨方法及注意问题【详解】

导语&#xff1a;随着时代的快速发展&#xff0c;人们生活水平的不断提高&#xff0c;打印机在我们日常生活中的应用也变得非常广泛&#xff0c;利用打印机打印文件&#xff0c;还有一些重要的材料&#xff0c;方便了人们的生活&#xff0c;给人们的生活提供了很大的便利&#…

打印机 检测到用过的耗材或者赝品耗材

检测到用过的耗材或者赝品耗材 大家好&#xff0c;今天续着给大家分享下惠普的803/805墨盒加墨应该注意的事项&#xff0c;先预习&#xff0c;加墨就没那么多困惑了~ 加墨后打印白线条、溅墨怎么办&#xff1f; ①先用温水浸泡打印头约30秒&#xff08;注意不要泡到芯片&…

打印机墨盒问题

因为打印机墨盒属于耗材&#xff0c;容易损坏&#xff0c;从而造成打印机没法打印。对于家用打印机来说&#xff0c;一个打印机也就三四百块钱&#xff0c;然后换一个新墨盒就得花掉一百左右&#xff0c;心里感觉贼不爽&#xff0c;墨盒那么小一个&#xff0c;居然要那么贵&…

墨盒 连供漏墨恒压问题

你提出了一个连供压力平衡原理的问题。 连供形状各式各样&#xff0c;但基本原理都是相同的。 以红色为例&#xff1a; 如上图。打印机静止时&#xff0c;墨水室的墨水重力&#xff0c;等于墨水室上方因为空气变稀薄后产生的负压。墨水不会流动。实现了压力的静平衡。 打印机工…

【Java 并发编程】深入理解 AQS - AbstractQueuedSynchronizer

深入理解 AQS - AbstractQueuedSynchronizer 1. AQS1.1 什么是 AQS1.2 AQS 具备的特性 2. AQS 原理解析2.1 AQS 原理概述2.1.1 什么是 CLH 锁2.1.2 AQS 中的队列 2.2 AQS 共享资源的方式&#xff1a;独占式和共享式2.2.1 Exclusive&#xff08;独占式&#xff09;2.2.2 Share&a…

JVM学习笔记(中)

1、垃圾回收算法 标记清除法 特点&#xff1a; 速度较快会产生内存碎片 注意&#xff1a;这里的清除并不是真正意义上的清除&#xff0c;即每个字节都清0&#xff0c;而是记录一下被清除的对象的起始和结束的地址&#xff0c;当下一次分配给一个新对象时&#xff0c;新对象…

《Java并发编程实战》课程笔记(四)

互斥锁 原子性问题到底该如何解决呢&#xff1f; “同一时刻只有一个线程执行”这个条件非常重要&#xff0c;我们称之为互斥。如果我们能够保证对共享变量的修改是互斥的&#xff0c;那么&#xff0c;无论是单核 CPU 还是多核 CPU&#xff0c;就都能保证原子性了。 锁模型 …

RK3588平台开发系列讲解(驱动基础篇)设备树常用 of 函数

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、查找节点的 of 函数二、获取属性值的 of 函数三、实验示例3.1、查找的节点代码3.2、获取属性内容代码沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 设备树描述了设备的详细信息,这些信息包括数字类型的…

chatgpt赋能python:Python中-1的用法介绍

Python中-1的用法介绍 什么是-1&#xff1f; 在Python中&#xff0c;-1是一个特殊的索引值&#xff0c;它表示从序列的末尾开始向前数1个元素。这在对于列表、字符串、元组等序列类型进行操作时非常有用。 如何使用-1&#xff1f; 假设我们有一个列表&#xff1a; l [1, …

SpringBoot框架理解

1 SpringBoot入门 1.2 什么是SpringBoot 1 官网的解释 ​ Spring在官方首页是这么说的&#xff1a;说使用SpringBoot可以构造任何东西&#xff0c;SpringBoot是构造所有基于Spring的应用程序的起点,SpringBoot在于通过最少的配置为你启动程序。 2 我的理解 SpringBoot是Sp…

Flask-RESTful的使用

Flask-RESTful的使用 Flask-RESTful基本使用安装定义资源Resources创建API实例添加资源到API运行Flask应用 请求处理请求解析参数校验 响应处理数据序列化定制返回格式 其他功能蓝图装饰器集合路由命名规范路由名称 Flask-RESTful Flask-RESTful是一个用于构建RESTful API的扩展…

计算机对社会的应用是什么,电子计算对人类社会有什么贡献?应用的领域又有哪些?...

在人类历史上&#xff0c;蒸汽机的发明和电力的使用&#xff0c;曾经在生产技术上引起过划时代的工业革命&#xff0c;然而&#xff0c;这种革命&#xff0c;从本质上来讲&#xff0c;它仅仅是涉及到代替人的体力劳动&#xff0c;但电子计算机的发明&#xff0c;已经涉及到代替…

一位美女博士的人脸识别历程

2019-01-28 16:44:37 1月21日&#xff0c;科技评论期刊《麻省理工科技评论》发布了2018年“35岁以下科技创新35人”&#xff08;35 Innovators Under 35&#xff09;中国榜单。商汤科技研究总监、年仅29岁的石建萍博士荣登此榜&#xff0c;凭借在计算机视觉原创技术的卓越创新…

Android仿微信发图片的样式,做IM的同学的病有救了

一&#xff1a;前言 最近在搞IM&#xff0c;真的特别痛苦。脑袋大&#xff0c;对于我这种菜鸟来说太难了&#xff0c;比现在社会娶个媳妇还难&#xff0c;硬着头皮搞&#xff0c;终于文字&#xff0c;语音&#xff0c;表情搞完了&#xff0c;开始搞图片&#xff0c;看着微信发…

软件压力测试图片60张,看图测压力,你抗压么?

你压力大么&#xff1f;快跟K线君一起来测测&#xff01; 平行线 下图里的横线都是平行的 涉世越深的人&#xff0c;受社会侵蚀越严重 看到的直线越变形 你还是单纯的你吗&#xff1f; 你能看出几条笔直的横线&#xff1f; 我想静静 这是一张静止的图片 你的心理压力越大&#…