当前位置: 首页 > 科技观察

玩转Java注解:元注解、内置注解、自定义注解的原理与实现

时间:2023-03-20 20:29:24 科技观察

前言Java注解(Annotation),又称Java注解,是JDK5.0引入的注解机制。重要:与Javadoc不同,Java注解可以通过反射获取注解内容。话不多说,总之就是在编译器生成class文件的时候,在字节码中嵌入注解即可。Java虚拟机可以保留注解内容,注解内容可以在运行时获取。当然它也支持自定义Java注解。反射+注解,有没有感觉Java变成了动态语言?哈哈哈!我认为注解可以分为三个部分:内置注解、元注解和自定义注解。一、内置注解1、@Override重写概念:检查方法是否为重写方法。如果发现父类或者引用的接口没有这个方法,就会报编译错误。//不用管这个extends,我写它只是为了更方便直观的理解,Object是万物之源,不写就默认为它的子类,不需要吗解释太多?publicclassAnnotation1extendsObject{@OverridepublicStringtoString(){return"我是重新定义的toString方法";}}@Override(覆盖),这个大家应该不陌生,覆盖父类的方法。我们可以在Object类中看到toString()的样子。所以很明显,使用@Override(覆盖)注解,方法名和方法参数必须和父类保持一致,否则会报错。如下图所示:如果不加@Override(覆盖)注解,会正常编译。2.@Deprecated过期警告概念:标记过时的方法。如果使用这种方式,会报编译警告。在开发中,我们经常会遇到这样的情况,如下图所示:jdk中有大量这样的方法,就不举例了。自己写一个可能更容易理解。publicclassAnnotation1extendsObject{publicstaticvoidmain(String[]args){testDeprecated.toString1();}}classtestDeprecated{@DeprecatedpublicstaticStringtoString1(){return"我重新定义了toString方法";}}注意:这不是错误,而是警告,提醒可能我们的方法有问题,可能有更好的实现方法!3、@SuppressWarnings忽略警告概念:指示编译器忽略注解中声明的警告。在正常的开发中,我们会遇到这样的情况,如下图所示:这不是报错,是提示没有使用这个方法,是警告提示。添加@SuppressWarnings注解后。publicclassAnnotation1extendsObject{publicstaticvoidmain(String[]args){}@SuppressWarnings("all")publicstaticvoidtestSuppressWarnings(){System.out.println("Test+testSuppressWarningsignorewarnings!");}}方法在没有警告的情况下成功高亮!我们可以点进去看看为什么这个注解需要参数?看这里,这不是方法,这是参数。注解中的参数格式:calss+参数名+()!这个需要强记,后面我们自定义注解的时候也会用到。用另一种写法加深理解!如下图所示:注意:当注解中只有一个参数时,我们不需要添加参数名,注解会自动为我们匹配。2、meta-annotations的概念:顾名思义,meta-annotations就是用于注释的注释!1、@Retentionscope——(常用)概念:表示注释信息保存在什么层级。在实际开发中,我们一般写RUNTIME,除非项目有特殊要求!我们看一下@Retention的源码。可以看到,需要一个参数,输入参数看看。SOURCE:对源代码有用。CLASS:在类文件中有用,但会被jvm丢弃。RUNTIME:运行时有用。关系:RUNTIME>CLASS>SOURCE后面我们自定义注解的时候,每一个都需要用到这个注解!2、@Documented作用文档概念:在javadoc中包含这个注解,意思是这个注解会被javadoc工具提取到一个文档中。老规矩看源码:不加参数的注解,范围是RetentionPolicy.RUNTIME,运行时有用!这只是作为一个标记,你可以理解它。实际运行后,会将注解写入javadoc,方便查看。3、@Targettarget-(常用)概念:标示该注解应该用在哪个Java成员上!参数源码:注意这是一个数组形式的参数,证明可以传多个值。@Target(ElementType.TYPE)——接口、类、枚举、注解@Target(ElementType.FIELD)——字段、枚举常量@Target(ElementType.METHOD)——方法@Target(ElementType.PARAMETER)——方法参数@Target(ElementType.CONSTRUCTOR)-构造函数@Target(ElementType.LOCAL_VARIABLE)-局部变量@Target(ElementType.ANNOTATION_TYPE)-注解@Target(ElementType.PACKAGE)-包让我们试试:如果目标是错误的,就会报错报道!让我们将其更改为方法!编译正常通过。其他scope可以自己试,篇幅有限,不可能每一个都试一遍!4、@Inherited继承概念:标示该注解继承自哪个注解类(默认注解不继承自任何子类)。这个很简单,就是给某个类A加上@InheritedAnno注解的时候,如果类B继承了A,那么B也会带这个注解。5.新的注解——(只懂)从Java7开始,增加了3个注解:@SafeVarargs——Java7支持,忽略任何使用参数作为泛型变量的方法或构造函数调用产生的警告。@FunctionalInterface-Java8支持,标识匿名函数或功能接口。@Repeatable-Java8支持,表示一个注解可以在同一个声明中多次使用。3.自定义注解让我们定义一个我们自己的注解。@Retention(value=RetentionPolicy.RUNTIME)@Target(value=ElementType.METHOD)@Inherited@interfacemyAnnotation{Stringname()default"";intage()default18;Stringlike();StringIDCard()default"";}格式:已修改Symbol(pulic)+@interface+注解名+{参数等}可以使用default设置默认值。设置默认值后,在不传值的情况下使用注解时,不会报错,否则会报错!我们只需要传递没有默认值的参数。不传的话会报错:总结主要是要注意元注解的使用,因为我们在自定义注解的时候必须要用到!注解其实主要是配合反射使用,这里不再赘述。