java注解编程_高级工程师带你彻底吃透Java注解

本文深入探讨了Java注解的核心概念,包括注解的本质——增强型注释,以及元注解的作用,如@Retention、@Target等。文章介绍了预置注解的用途,如@Override、@Deprecated,并讲解了自定义注解的创建和使用。通过理解注解的生命周期和作用范围,读者能更好地掌握高级工程师的编码技巧。
摘要由CSDN通过智能技术生成

前言

代码,就是我们身为程序员的名片。

简洁,优雅,统一,是我们的追求。

优秀的代码,会给浏览者一种艺术的美感。如DL大神的JUC包,感兴趣的小伙伴,可以研究一下。

那么日常中,各位看到的优秀代码,有着哪些特点呢?充分利用的工具类(lang3,lombok,Validation等等),完善的注解,统一的代码规范等等。还有的,就是Java语言的诸多高级特性(lambda,stream,io等)。

Java语言中,有三个特性,是高级工程师不可或缺的:

注解反射泛型如果代码中,存在这些东西,那么即使应用得还不够合理,也能够从侧面证明这位程序员的技术追求。

这三点是初级工程师很难掌握的,因为缺乏了解与需求(或者说想不到对应的需求)。而高级工程师为了给出更加具有通用性,业务无侵入的代码,就常常需要与这些特性打交道。

在不断积累后的今天,我觉得我可以尝试写一写自己对这些特性的认识了。

今天就从注解开始,阐述我对高级工程师的一些编码认识。

简介

我发现很多小伙伴总是在喜欢记忆一些注解的功能,比如表示非空的@NotNull等。

这里,我要从功能与原理角度说明两点:

功能:注解是一种“增强型”的注释。只不过相对于只能给人看的注释,注解可以给电脑(JVM,程序等)看。原理:注解的底层是Annotation接口的继承者。只不过相对于日常使用的接口,注解需要使用@interface,但是编译的结果依旧是接口继承(如TestAnnotation extend Annotation)。请大家牢记上面两点,这是有关注解认识的绝对核心。

只要大家抓住这两个角度去认识注解,那么很快就可以成为注解达人。后续很多阐述都会从这两个角度,去为大家解释。如为什么人们常说注解是无法继承的,为什么需要元注解等等。

注解的目录结构

87641d03060bd85f075dc70a4b6bd668.png

其实可以看到,JDK中有关注解的内容很少,非常适合作为三大特性的入门啊。因为注解的实现基础是存在于JVM中的,JDK只是提供了对应的工具。

Annotation接口

上面提到注解的底层是接口,这里以图为证。

f1ea747fdfb0788cd3a9fc3103dd8035.png

注意,仔细看这个接口的注释。注释中明确提出,虽然注解的本质是接口。但是直接引用Annotation接口,是无法实现注解功能的。

元注解

简介

通俗来说,元注解就是注解的注解。

首先元注解,是Java自带的预置注解。从这个角度,需要与@XXX修饰的自定义注解进行区分。

站在功能上来说,元注解就是专门修饰注解的“注释”,用来告诉编译器,虚拟机,相关的信息(如运行时间,目标对象)。

站在原理上来说,元注解也是注解,也是使用了@interface(底层依旧是继承Annotation接口)。

不过注解,在底层实现已经继承Annotation接口,那么就无法通过继承接口的方式(Java不支持多重继承),来保存元注解的信息(尤其这个信息往往不止一类)。那么注解的元注解信息是如何保存,并交给计算机的呢?答案就是通过RuntimeVisibleAnnotations进行相关信息的保存的。以下就是对DynamicPropertyVerification注解反编译的结果,重点在于反编译结果的最后一段。

898bda3a89e4843f0285c05a8404f074.png

64d2cd439398a1ca900b27765ae6bb3b.png

最后一段,通过RuntimeVisibleAnnotations,保存了所需要的元注解信息。

如果对JVM底层原理有了解的小伙伴,应该对RuntimeVisibleAnnotations不陌生。不了解的小伙伴,可以查看Class RuntimeVisibleAnnotations

预置注解

元注解

元注解是Java自带的,主要分为:

@Rentention:表示目标注解的保持策略。其value为RetentionPolicy。如果目标注解没有使用该注解,则默认使用RetentionPolicy.CLASS@Target:表示目标注解的应用目标类型。其value为ElementType。如果目标注解没有使用该注解,则目标注解可以用于除了TYPE_PARAMETER和TYPE_USE以外的任何地方(这两个类型都是Java8新添加的)。@Documented:表示目标注解可以出现在JavaDoc中。@Repeatable:表示目标注解可以在同一位置,重复使用。@Inherited:表示目标注解可以随着所修饰的类的继承关系,被子类继承。@Retention

源码:

00bbfb7bc089f212eef74a179468eedd.png

通过RetentionPolicy枚举表示目标注解的保持策略。

ea02e6883f20c5bb7da670cbaee954e0.png

为了便于大家理解,这里再举一些例子。这里挑选一些Java自带的,不用大家再去自己写demo,增加认知负荷:

@Retention(RetentionPolicy.SOURCE):如@Override注解,由于该注解只是用于进行代码检测,所以只要存在于源码中即可,故选择RetentionPolicy.SOURCE。类似的还有@SuppressWarnings注解等。@Retention(RetentionPolicy.CLASS):涉及注解处理器,所以实例很少。可以查看自定义注解之编译时注解(RetentionPolicy.CLASS)(一)。@Retention(RetentionPolicy.RUNTIME):如@Deprecated,由于该注解需要在运行时提示用户注解修饰的方法,类等已经过时,所以需要JVM中有对应“注释”信息,故采用RetentionPolicy.RUNTIME。类似的还有@Repeatable等。@Target

85f9d7c15027febcbe27345834447f80.png

通过ElementType枚举表示目标注解的应用目标类型。

235bc5a1fc25b4e72c22e83c63a7894c.png

这里不会一一举例,只会点出重点:

TYPE_PARAMETER与TYPE_USE是Java8新增加的。所以使用Java7的小伙伴要注意。ElementType.TYPE涵盖范围很广泛,在不知用哪个时,可以先用这个。@Documented

默认情况下,注解是不出现在 javadoc 中的。通过给目标注解加上 @Documented 元注解,能使目标注解出现在 javadoc 中。

47b7aca627532e71ee2c52d697f9a78c.png

从源码可以看出,@Documented是一个没有任何成员的标记注解。

@Repeatable

7bb775adba6e15034c7076c35ff5aa09.png

@Repeatable注解的使用,引用一个不错的demo。

8922fc6f0f6c3202264e7d664b126a7d.png

上述代码,其实分为两个部分:

使用@Repeatable注解,使得其修饰的@FilterPath,可以在目标上重复标记(便于设置不同的成员变量)。通过@FilterPaths注解(包含成员变量-FilterPath[] value ()),将@FilterPath集中到@FilterPaths中,便于后续逻辑处理。@Inherited

@Inherited同样是只能修饰注解的元注解,它所标注的目标注解具有继承性。

这里解释一下这个继承性,这并不是注解间的继承。而是指目标注解可以随着类的继承,而被子类继承。简单说,就是目标注解修饰的类,其后代类也会被该注解标注(可以通过getAnnotation方法获取)。

6eddf31084bb7d7175b33d5cad5931b1.png

这里不再赘述,感兴趣的小伙伴,可以查看Java 注解(Annotation)中的相关示例。

功能注解

Java预置的功能注解,主要分为:

@Override:该注解修饰的目标方法,必须是重写基类方法,或实现对应接口方法,否则编译器会报错。@Deprecated:该注解修饰的目标,表示已经过时,不推荐使用。编码时,使用该注解的目标,会有划线提示。@SuppressWarnings:该注解修饰的目标,将会忽略某些异常(由注解的value指定),从而通过编译器编译。@SafeVarargs:该注解修饰的构造函数(只能修饰构造函数),将会忽略可变参数带来的警告。该注解于Java7引入。@FunctionalInterface:该注解修饰的接口,为函数式接口。如java.util.function下的Consumer自定义注解

到了这里,大家应该对注解不再陌生了。

而在日常开发中,我们常常需要自定义开发一些注解。

自定义注解分为以下步骤:

[必选] 使用@interface来构建自定义注解。一般在创建自定义注解的同时,就达成了该要求。[可选] 使用@Target元注解。通过该注解,确定自定义注解的作用目标类型。注意:如果目标注解没有使用该注解,则目标注解可以用于除了TYPE_PARAMETER和TYPE_USE以外的任何地方(这两个类型都是Java8新添加的)。[可选] 使用@Retention元注解。通过该注解,明确自定义注解的生命周期,或者说自定义注解作用域。如果目标注解没有使用该注解,则默认使用RetentionPolicy.CLASS[可选] 添加成员变量。格式为“long value() default 1000L;”,与Java8的接口成员变量非常类似。注意:注解的成员变量只能采用无参方法表示。并且注解的成员变量,只能采用基本数据类型(char,boolean,byte、short、int、long、float、double)和String、Enum、Class、annotations数据类型,以及这一些类型的数组。[可选] 使用自定义注解。自定义注解的使用领域很多,主要分为两个方向:利用已有框架,不需要自己实现相关逻辑,自定义注解多作为标记注解。如配合SpringBoot的注解,形成自己的注解(相关的逻辑由SpringBoot自己处理)利用已有框架,需要自己实现部分逻辑(不涉及反射),但需要关联已有框架,并实现对应接口。如Validation框架的自定义校验注解,感兴趣的小伙伴,可以查看我之前写的Validation框架的应用。可选择已有框架,需要自己实现诸多逻辑。如在AOP中,我们常常需要通过反射,获取自定义注解的信息(如参数等),或者自定义注解修饰的目标的信息(如参数,方法名等)。这部分,我会在后续的反射部分详细说明。

总结

简单总结一下,本文主要描述了:

注解是什么:增强型的注释,本质是接口元注解是什么:注解的注解,作用是为了标识目标注解。包括@Target,@Retention,@Documented,@Repeatable,@Inherited.预置注解是什么:JDK自带的经典功能注解,如@Override,@Deprecated,@SuppressWarnings,@SafeVarargs,@FunctionalInterface。自定义注解如何实现:主要分为五步,但是其中必要的步骤,就一步:使用@interface来构建自定义注解。至此,Java注解的内容就基本展现了。

最后,还是强调两个方面:

注解就是增强型的注释(可被计算机识别的注释),本质是接口。把握住这两点,就非常好理解注解与它的各种规则,行为。注解本身并没有任何功能(因为它只是注释,本质也只是接口),需要其他代码支撑,它才能体现价值。希望对大家有所帮助,还有不清楚的地方,可以查看下列参考目录。

愿与诸君共进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值