Java 注解知识总结

引言

本博客总结自《Java 编程思想》第 20 章。

一、什么是注解

注解是 Java 5 引入的一种通过反射机制实现的语法特性,开发者可以通过在类、域、方法等元素前面标记一个“标签”达到对程序的源码类信息运行时进行某种说明或处理的效果,尽可能地简化代码,从而使程序开发更高效。但需要注意的是,编译器要确保在其构造路径上,必须有对应注解的定义。

Java 中在 1.5 之初内置了三个标准注解,@Deprecated、@Override 、@SupressWarning 。我们经常会在程序的各个角落看到它们。

以@SupressWarning为例,

package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {String[] value();
}

它一般用于去除不恰当的编译警告,俗称“报黄”。随着Java 慢慢的发展,也逐渐引入了更多的注解,比如在 Java 8 伴随着 Lambda表达式的加入,而一同入住 Java 大家庭的 @FunctionalInterface 注解:

package java.lang;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

 二、如何声明注解

注解的定义非常类似接口,不同的是,像上面的例子中,我们要在 interface 关键字前面加 “@”,以此来声明这是一个注解。

除此之外,Java 提供了四个元注解

@Target
@Retention
@Documented
@Inherited

其中,@Target 、@Retention 在定义注解时,一般情况下都是必选项。

元注解专职负责注解其他注解。

@Target :表示该注解可以用于什么地方。需要给它传入一个ElementType 枚举对象,常用选项有:

    CONSTRUCTOR : 构造器声明
    FIELD : 域声明(包括enum实例)
    LOCAL_VARIABLE : 局部变量声明
    METHOD : 方法声明
    PACKAGE : 包声明
    PARAMETER : 参数声明
    TYPE : 类、接口(包括注解声明)、enum 声明

@Retention :表示需要在什么级别保存该注解。需要传入一个 RetentionPolicy ,可选值:

    SOURCE : 注解将被编译器丢弃。
    CLASS : 注解在class文件中使用,但会被 JVM 丢弃。
    RUNTIME : vm将会在运行期间也保留该注解,因此可以通过反射机制读取注解的信息。

@Documented : 将此注解包含在javadoc 中。

@Inherited : 允许子类继承父类的注解。 

注解定义示例:

package com.mht.demo.注解;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {public int id();public String description() default "no description";
}

可以看到,注解的确非常像接口,同时,和其他任何 Java 接口一样,注解也将会被编译成为一个 class 文件

@MyTest 用到了两个元注解:@Target 、@Retention ,如上所示,@MyTest 只能用于方法上,如果希望自己的注解既可以用于方法上,也可以用于类上,那么可以这样写:

@Target({ ElementType.METHOD, ElementType.TYPE })

@Retention用于定义你的注解应用在什么级别,源代码(SOURCE)、类文件(CLASS)、运行时(RUNTIME)

注解中往往包含一些元素,比如上例中的 id 、description 。

在分析和处理注解时,程序或工具可以利用这些值,它们看起来像接口中的抽象方法,唯一不同的是我们可以为这些元素指定默认值。对于没有任何元素的注解如 @FunctionalInterface 被称为标记注解。

编译器会对注解中的元素进行类型检查,因此,将这些元素与数据库相关联是安全的。注解元素可用的类型有一定的限制,它不允许任何包装类型。被允许的注解元素类型有:

1、8大基本类型

2、String

3、Class

4、enum

5、Annotion : 嵌套注解,非常有用的技巧

6、以上类型的数组

如果使用了除上述几种以外的其他类型,那么编译器就会报错。 注意,注解的元素值永远不能为null要么在声明元素之初设置默认值,要么就在使用注解时添加该元素值,而且必须是不为 null 的值(如果希望注解中的某个元素是必填项,那么在声明时就可以不为其指定默认值)。因此,注解处理器无法通过null 来判断元素是否缺失。为了绕开这个限制,一般会通过 自己定义特殊值来判断元素是否存在,比如 -1 或 ""。

default 关键字来定义元素的默认值,在使用该注解时,如果没有给出元素的值 那么注解处理器就会使用此元素的默认值。

三、注解的使用与自定义注解处理器

以 @MyTest 为例,我们来看看注解如何使用,以及如何处理这个注解。

public class SomeService {@MyTest(id = 1, description = "Hello Annotation! Hello 2020 !")public void testMyTestFeature() {System.out.println("这是testMyTestFeature()方法!");}
}

我们定义了一个类,声明了一个方法,并为其标记我们的 @MyTest 注解。

接下来我们来实现一个注解处理器 MyTestProcessor :

/*** '@MyTest'注解处理器* @author mht**/
public class MyTestProcessor {public static void processMyTest(Class<?> clz) {Method[] declaredMethods = clz.getDeclaredMethods();// getAnnotation() 方法会返回指定类型的注解,如果没有,则返回nullMyTest myTest = declaredMethods[0].getAnnotation(MyTest.class);// 这里一般都会判断获取到的注解是否为空if (myTest != null) {System.out.println("找到标记注解:id:" + myTest.id() + ", 描述:" + myTest.description());}}public static void main(String[] args) {processMyTest(SomeService.class);}
}

执行 main 方法,测试输出结果:

找到标记注解:id:1, 描述:Hello Annotation! Hello 2020 !

注解处理器虽然名字听起来很专业,但实际上,我们并不需要为我们处理注解的类或方法继承或实现什么。正如第一节开始所说的,注解是一种通过反射机制来实现的特性,我们可以通过 Class 对象来获取我们想要的注解。

像上面的代码有点过于简单了,一般情况下,我们可能会为一个目标添加多个注解,因此一般的处理注解的思路就是:

1、获取类信息(Class 对象可以直接获取类上的注解对象或注解对象数组)

2、通过 getDeclaredMethods() 等方法,获取目标信息(有时也有可能是 类或域)

3、通过 getAnnotation(Class<T> annotationClass)、getAnnotations() 等方法,获取一个或多个待处理的注解对象。

4、通过注解对象,获取内部元素,根据其值进行逻辑处理。

这是一个一般的注解处理思路,许多框架中的注解处理往往比较复杂,且经常需要配合遍历来处理多个类信息,多个注解的情况。

值得注意的是,注解往往都是被动的处理,它不能主动发出某种信号传递给注解处理器,也就是说,我们必须主动找到这些注解或将携带他们的类传入注解处理器。

某些框架在批量处理注解的时候,就必须为注解处理器指定一个尽可能小的路径范围,以此来扫描该路径下的类信息。

Mybatis 中的 @MapperScan 就是一个很好的例证,如果不为其指定一个扫描路径,Mybatis 框架就可能必须从 classpath 的根路径找起,这会非常影响框架处理效率。

综上就是关于 注解的总结和思考,欢迎文末留言。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 实操 —— Linux 系统性能分析

引言 最近配合解决压测&#xff08;性能测试&#xff09;方面的问题&#xff0c;了解到了一些可以监控 Linux 系统性能指标&#xff0c;如CPU、IO、内存等的工具。 此篇博客主要讲解 Linux 系统监控的一些重点内容以及 sar 命令的使用。 一、Linux 性能分析聚焦 Linux 性能…

Java 动态代理解析

引言 本博客总结自《Java 编程思想》第十四章 一、实现第一个动态代理程序 代理是软件设计中重要的设计思想&#xff0c;它允许我们在调用实际操作之前或之后解耦式地编写额外的操作&#xff0c;而一旦不需要这些操作了&#xff0c;就可以轻易的移除它们。 浏览了《编程思想…

MySQL 基础 ————高频函数总结

一、MySQL函数调用方式 函数调用的基本语法&#xff1a; SELECT 函数(实参列表) [FROM 表] 其中&#xff0c;对于函数&#xff0c;需要重点关注三点&#xff1a; 1、函数的名称 2、参数列表 3、函数功能 二、函数的分类 在 MySQL中&#xff0c;函数分为两类&#xff1a;1、…

MySQL 高级 —— 复合索引简介(多列索引)

引言 复合索引是指包含多个数据列的索引&#xff0c;与之概念相对的是单列索引&#xff0c;仅包含一个数据列。在大多数情况下&#xff0c;建立多列索引的好处都要多于单列索引。另外&#xff0c;复合索引最多支持16个列&#xff0c;但请一定不要让复合索引包含太多的列&#…

MySQL 优化 —— SQL优化概述(优化专题开篇词)

引言 最近为了研究索引的知识&#xff0c;特地去MySQL 官网研读了一番&#xff0c;发现MySQL官网有比较全面的MySQL优化方案和知识背景&#xff0c;所以希望通过一系列文章&#xff0c;将官网的知识翻译总结一下&#xff0c;避免日后去网上胡乱搜索产生不必要的知识勘误风险。…

MySQL 优化 —— WHERE 子句优化

引言 本文翻译自 MySQL 官网&#xff1a;WHERE Clause Optimization WHERE 子句优化 这一部分我们来讨论对 WHERE 子句的优化处理。本部分的案例都是以 SELECT 语句为例&#xff0c;但这些优化同样适用于 DELETE 和 UPDATE 语句中的 WHERE 子句。 注意 因为对 MySQL 优化器的…

MySQL 优化 —— IS NULL 优化

引言 本博客翻译自 MySQL 官网&#xff1a;IS NULL Optimization&#xff0c; MySQL版本 5.7。 MySQL 对 IS NULL 的优化 MySQL 可以对 IS NULL 执行和常量等值判断&#xff08;列名 常量表达式&#xff0c;如name Tom&#xff09;相同的优化。MySQL 可以利用索引和范围来…

MySQL 优化 —— ORDER BY 优化

引言 本文翻译自MySQL 官网&#xff1a;ORDER BY Optimization&#xff0c;MySQL 版本&#xff1a;5.7。 这一部分描述了MySQL何时会使用索引来满足order by子句&#xff0c;filesort 操作会在索引不能生效的时候被用到&#xff0c;以及优化器对order by的执行计划信息。 or…

MySQL 高级 —— 索引实现的思考

引言 最近看了一个公开课&#xff0c;是有关MySQL对索引设计的思考。详细讲解了几种索引实现的设计思考与利弊辨析&#xff0c;讨论了为什么MySQL默认情况下会使用B树索引&#xff0c;B树索引又对B树做了哪些结构改进。 本片博客通过个人的学习理解和总结&#xff0c;由几种简…

MySQL 优化 —— MySQL 如何使用索引

引言 本文翻译自MySQL 官网 &#xff1a;How MySQL Uses Indexes &#xff0c;MySQL 版本 5.7 。 提升 SELECT 操作性能最好的方式就是在查询的一列或多列上建立索引。索引的行为类似指向表数据的指针&#xff0c;可以让查询能够快速判断哪个记录满足 WHERE 子句中的条件&…

MySQL 优化 —— EXPLAIN 执行计划详解

引言 本博客大部分内容翻译自MySQL 官网 Understanding the Query Execution Plan 专题。另外有一些补充&#xff0c;则来自于网课以及《高性能MySQL&#xff08;第三版&#xff09;》。 根据我们的表、字段、索引、以及 where 子句中的条件等信息&#xff0c;MySQL 优化器会…

Git 初学札记(十)—— Reset 回退的三种状态解析

引言 工作中经常会涉及到需要本地代码覆盖更新的操作。有时候可能是从远端git 上直接覆盖更新&#xff0c;或者是其他本地分支覆盖更新当前分支等等。这个时候就需要用到 reset 操作。 reset 操作分为三种类型&#xff1a;Soft、Mixed、Hard。今天我们就来说说这三种类型究竟…

MySQL 高级 —— 深入理解 InnoDB 与 MyISAM

引言 在文件系统中&#xff0c;MySQL将每个数据库&#xff08;也可以称之为schema&#xff09;保存为数据目录下的一个子目录。创建表时&#xff0c;MySQL会在数据库子目录下创建一个与表同名的.frm文件保存表的定义。因为MySQL使用文件系统的目录和文件来保存数据库和表的定义…

Ts声明ElementUI控件

初用Ts&#xff0c;有时候想获取三方控件不太会声明类型&#xff0c;记录一下使用InstanceType导入类型 例如声明一个el-select <el-form-item label"类型:" prop"year" :loading"state.loading"><el-select v-model"props.ruleF…

关于 OutOfMemoryError 的总结与解决方法

引言 本文总结自周志明的《深入理解Java虚拟机》第二章部分内容。 这部分内容&#xff0c;可以为后续性能调优方面的工作起到铺垫作用。 一、什么是 OutOfMemoryError OurOfMemory 简称“OOM”&#xff0c; 直译为“内存耗尽”或“内存溢出”&#xff0c;当然&#xff0c;并…

Windows误关闭资源管理器重启的办法

引言 有时候Windows系统在开机后&#xff0c;在桌面底部的任务栏中无法正常加载必要的网络连接图标或音量图标等&#xff0c;导致无法手动操作音量或连接网络。这时候就会需要打开“任务管理器”重新启动“资源管理器”使其重新加载这些必要的控制图标。 但是由于操作失误&am…

MySQL高级 —— 高性能索引

引言 最近一直在抱着《高性能MySQL&#xff08;第三版&#xff09;》研究MySQL相关热点问题&#xff0c;诸如索引、查询优化等&#xff0c;这阶段的学习是前一段时间MySQL基础与官方的“阅读理解”的进一步延伸。 书中第五章详细阐述了如何设计高性能的索引&#xff0c;以及索…

MySQL高级 —— 查询性能优化

引言 承接《MySQL高级 —— 高性能索引》&#xff0c;本篇博客将围绕《高性能MySQL&#xff08;第三版&#xff09;》第六章内容进行总结和概括。 与索引的部分一样&#xff0c;SQL优化也是广大程序员深入MySQL的又一条必经之路。希望通过本篇博客的总结&#xff0c;能够为我…

哈希表的大小为何最好是素数

引言 为什么散列函数采用取模运算&#xff1f;又为什么取模运算的被取模数最好是素数&#xff1f;素数是如何在取模运算中很好的规避冲突的&#xff1f; 这些问题可能困扰诸多程序员很久了。我们总是说素数可以更好的避免冲突&#xff0c;但总是对各种长篇大论的分析望而却步…

Java常用设计模式————适配器模式

引言 由于无法直接使用某个类中的方法而采取的一种中间类转换的策略。将一个类的接口转换成另一个接口&#xff0c;让原本接口不兼容的类可以兼容。 适配器模式可以分为三种&#xff1a;类适配器、对象适配器、接口适配器。它们之间的区别主要体现在适配器角色与被适配角色之…