Android APT 系列 (二):APT 筑基之注解

news2024/11/17 1:33:28

注解介绍

元数据

元数据就是为其他数据提供信息的数据
注解

官方解释:注解用于为代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 JDK 1.5 开始添加到 Java 的。

简单的理解:注解就是附加到代码上的一种额外补充信息

注解作用

源码阶段注解: 编译器可利用该阶段注解检测错误,提示警告信息,打印日志等
编译阶段注解:利用注解信息自动生成代码、文档或者做其它相应的自动处理
运行阶段注解: 可通过反射获取注解信息,做相应操作
如何自定义定义一个注解

使用 @interface+ 注解名称这种语法结构就能定义一个注解,如下:

@interface TestAnnotation{

}

元注解

元注解就是为注解提供注解的注解

JDK 给我们提供的元注解
1、@Target
2、@Retention
3、@Inherited
4、@Documented
5、@Repeatable
@Target

@Target表示这个注解能放在什么位置上

ElementType.ANNOTATION_TYPE //能修饰注解
ElementType.CONSTRUCTOR //能修饰构造器
ElementType.FIELD //能修饰成员变量
ElementType.LOCAL_VARIABLE //能修饰局部变量
ElementType.METHOD //能修饰方法
ElementType.PACKAGE //能修饰包名 
ElementType.PARAMETER //能修饰参数
ElementType.TYPE //能修饰类、接口或枚举类型
ElementType.TYPE_PARAMETER //能修饰泛型,如泛型方法、泛型类、泛型接口 (jdk1.8加入)
ElementType.TYPE_USE //能修饰类型 可用于任意类型除了 class (jdk1.8加入)
  
@Target(ElementType.TYPE)
@interface TestAnnotation{
    
}

注意:默认情况下无限制
@Retention
@Retention 表示注解的的生命周期,可选的值有 3 个:

RetentionPolicy.SOURCE //表示注解只在源码中存在,编译成 class 之后,就没了
  
RetentionPolicy.CLASS //表示注解在 java 源文件编程成 .class 文件后,依然存在,但是运行起来后就没了
  
RetentionPolicy.RUNTIME //表示注解在运行起来后依然存在,程序可以通过反射获取这些信息
  
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation{

}

注意:默认情况下为 RetentionPolicy.CLASS
@Inherited

@Inherited表示该注解可被继承,即当一个子类继承一个父类,该父类添加的注解有被 @Inherited修饰,那么子类就可以获取到该注解,否则获取不到

@Inherited
@interface TestAnnotation{

}

注意:默认情况下为不可继承
@Documented

@Documented 表示该注解在通过javadoc 命令生成Api文档后,会出现该注解的注释说明

@Documented
@interface TestAnnotation{

}

注意:默认情况下为不出现
@Repeatable

@Repeatable是JDK 1.8新增的元注解,它表示注解在同一个位置能出现多次,这个注解有点抽象,我们通过一个实际例子理解一下

//游戏玩家注解
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface GamePlayer{
    Game[] value();
}

//游戏注解
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(GamePlayer.class)
@interface Game{
    String gameName();
}

@Game(gameName = "CF")
@Game(gameName = "LOL")
@Game(gameName = "DNF")
class GameTest{

}

注意:默认情况下不可重复
经验:通常情况下,我们会使用多个元注解组合来修饰自定义注解

注解属性

注解属性类型

1、基本数据类型
2、String
3、枚举类型
4、注解类型
5、Class 类型
6、以上类型的一维数组类型
定义注解属性

首先我们定义一些注解属性,如下:

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface TestAnnotation{
    //这就是注解属性的语法结构
    //定义一个属性并给了默认值
    String name() default "erdai";
  
    //定义一个属性未给默认值
    int age();
}

可能你会有些疑问:这难道不是在定义方法吗?还可以给默认值?
这些疑问先留着,我们继续分析
自定义注解默认都会继承 Annotation ,Annotation 是一个接口,源码如下:

public interface Annotation {
   
    boolean equals(Object obj);

    int hashCode();
  
    String toString();
    
    Class<? extends Annotation> annotationType();
}

我们知道,在接口中可以定义属性和方法,那么作为自定义注解,是否也可以定义呢?
可以,接口中的属性默认都是用public static final 修饰的,默认是一个常量,对于自定义注解来说,这点没有任何区别。而接口中的方法其实就相当于自定义注解的属性,只不过自定义注解还可以给默认值。因此我们在学习自定义注解属性时,我们应该把它当作一个新知识,加上我刚才对接口的分析对比,你上面的那些疑问便可以迎刃而解了
注解属性使用

1、在使用注解的后面接上一对括号,括号里面使用 属性名 = value的格式,多个属性之间中间用 ,隔开
2、未给默认值的属性必须进行赋值,否则编译器会报红

//单个属性
@TestAnnotation(age = 18)
class Test{
    
}

//多个属性
@TestAnnotation(age = 18,name = "erdai666")
class Test{

}

注解属性获取

1、我们在获取属性的时候,可以先判断一下是否存在该注解,增强代码的健壮性,如下:

@TestAnnotation(age = 18,name = "erdai666")
class Test{

}

Class<Test> testClass = Test.class;
//获取当前注解是否存在
boolean annotationPresent = testClass.isAnnotationPresent(TestAnnotation.class);
//如果存在则进入条件体
if(annotationPresent){
    TestAnnotation declaredAnnotation = testClass.getDeclaredAnnotation(TestAnnotation.class);
    System.out.println(declaredAnnotation.name());
    System.out.println(declaredAnnotation.age());
}

2、获取类属性的注解属性

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface TestField{
    String filed();
}

class Test{
    @TestField(filed = "我是属性")
    public String test;
}

//通过反射获取属性注解
Class<Test> testClass1 = Test.class;
try {
    Field field = testClass1.getDeclaredField("test");
    if(field.isAnnotationPresent(TestField.class)){
        TestField fieldAnnotation = field.getDeclaredAnnotation(TestField.class);
        System.out.println(fieldAnnotation.filed());
    }
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}
//打印结果
我是属性

3、获取类方法的注解属性

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface TestMethod{
    String method();
}

class Test{
    @TestMethod(method = "我是方法")
    public void test(){

    }
}
//通过反射获取方法注解
Class<Test> testClass2 = Test.class;
try {
    Method method = testClass2.getDeclaredMethod("test");
    if(method.isAnnotationPresent(TestMethod.class)){
        TestMethod methodAnnotation = method.getDeclaredAnnotation(TestMethod.class);
        System.out.println(methodAnnotation.method());
    }
} catch (Exception e) {
    e.printStackTrace();
}
//打印结果
我是方法

JDK 提供的内置注解

JDK 给我们提供了很多内置的注解,其中常用的有:
1、@Override
2、@Deprecated
3、@SuppressWarnings
4、@FunctionalInterface
@Override

@Override 用在方法上,表示这个方法重写了父类的方法,例如toString 方法

@Override
public String toString() {
    return super.toString();
}

在这里插入图片描述可以看到用 @Deprecated 注解的方法调用的时候会被划掉
@SuppressWarnings

@SuppressWarnings 用于忽略警告信息,常见的取值如下:
deprecation:使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告)
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path:在类路径、源文件路径等中有不存在的路径时的警告
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告
finally:任何 finally 子句不能正常完成时的警告
rawtypes: 泛型类型未指明
unused:引用定义了,但是没有被使用
all:关于以上所有情况的警告

以泛型举个例子:
在这里插入图片描述当我们创建 List 未指定泛型时,编译器就会报黄提示我们未指明泛型,这个时候就可以使用这个注解了:
在这里插入图片描述@FunctionalInterface

@FunctionalInterface是 JDK 1.8 新增的注解,用于约定函数式接口,函数式接口就是接口中只有一个抽象方法

@FunctionalInterface
interface testInterface{
    void testMethod();
}

在这里插入图片描述

注解实际应用场景

使用自定义注解代替枚举类型

主要针对源码阶段注解
这个在我们实际工作中也挺常用的,使用枚举类型开销大,我们一般都会使用自定义注解进行替代,如下:

//1、使用枚举
enum EnumFontType{
    ROBOTO_REGULAR,ROBOTO_MEDIUM,ROBOTO_BOLD
}
//实际调用
EnumFontType type1 = EnumFontType.ROBOTO_BOLD;

//================================ 完美的分割线 ==================================
//2、使用自定义注解
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
@IntDef({AnnotationFontType.ROBOTO_REGULAR,AnnotationFontType.ROBOTO_MEDIUM,AnnotationFontType.ROBOTO_BOLD})
@interface AnnotationFontType{
    int ROBOTO_REGULAR = 1;
    int ROBOTO_MEDIUM = 2;
    int ROBOTO_BOLD = 3;
}
//实际调用
@AnnotationFontType int type2 = AnnotationFontType.ROBOTO_MEDIUM;

注解处理器 (APT)

主要针对编译阶段注解
实际我们日常开发中,经常会遇到它,因为我们常用的一些开源库如 ButterKnife,Retrofit,Arouter,EventBus 等等都使用到了APT 技术。也正是因为这些著名的开源库,才使得 APT技术越来越火,在本系列的下一篇中,我也会讲到。
运行时注解处理

主要针对运行阶段注解

举个实际的例子:例如我们开车去自助加油机加油,设定的Money是 200,如果少于 200则提示 加油中…,否则提示 油已加满,如果出现异常情况,提示 加油失败

现在我们通过注解来实现一下它,如下:

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface OilAnnotation{
    double maxOilMoney() default 0;
}

class GasStation{

    @OilAnnotation(maxOilMoney = 200)
    public void addOil(double money){
        String tips = processOilAnnotation(money);
        System.out.println(tips);
    }

    @SuppressWarnings("all")
    private String processOilAnnotation(double money){
        try {
            Class<GasStation> aClass = GasStation.class;
            //获取当前方法的注解
            Method addOilMethod = aClass.getDeclaredMethod("addOil", double.class);
            //获取方法注解是否存在
            boolean annotationPresent = addOilMethod.isAnnotationPresent(OilAnnotation.class);
            if(annotationPresent){
                OilAnnotation oilAnnotation = addOilMethod.getDeclaredAnnotation(OilAnnotation.class);
                if(money >= oilAnnotation.maxOilMoney()){
                    return "油已加满";
                }else {
                    return "加油中...";
                }
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return "加油失败";
    }
}

new GasStation().addOil(100);
//打印结果
加油中...
  
new GasStation().addOil(200);
//打印结果
油已加满

总结

本篇文章讲的一些重点内容:
1、自定义注解时,元注解的组合使用
2、注解属性的定义,使用和获取
3、一些常用的 JDK 内置注解
4、注解的实际应用及运行阶段注解的一个实践

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

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

相关文章

建议收藏:OpenKruise入门与实践

简介 OpenKruise 是一个基于 Kubernetes 的扩展套件&#xff0c;主要聚焦于云原生应用的自动化&#xff0c;比如部署、发布、运维以及可用性防护。 OpenKruise 提供的绝大部分能力都是基于 CRD 扩展来定义&#xff0c;它们不存在于任何外部依赖&#xff0c;可以运行在任意纯净的…

css 背景颜色级别高于背景图

<div class"bg-parent"> <img :src"employeeImg" class"bg-url" /> <div class"bg"> <el-icon class"plus-icon"> <Plus /> </el-icon> </div> </div> .bg-parent{ //父级…

Web服务器群集:Nginx之Rewrite重写

目录 一、理论 1.Nginx正则表达式 2.location匹配 3.rewrite重写 二、实验 1.基于域名的跳转 2.基于客户端 IP访问跳转 3.基于旧域名跳转到新域名后面加目录 4.基于参数匹配的跳转 5.基于目录下所有 php结尾的文件跳转 6.基于最普通一条url请求的跳转 三、总结 一、…

福昕Foxit PDF远程代码执行漏洞CVE-2023-27363分析与复现

漏洞概述 福建福昕软件开发股份有限公司是一家国际化运营的PDF电子文档解决方案提供厂商&#xff0c;提供文档的生成、转换、显示、编辑、搜索、打印、存储、签章、表单、保护、安全分发管理等涵盖文档生命周期的产品技术与解决方案。其下产品Foxit PDF Reader和Foxit PDF Edit…

性能测试——App性能测试需要关注的指标

目录 一、Android客户端性能测试常见指标&#xff1a; 二、预期标准指定原则 三、测试方法及工具 一.从用户角度出发 二.站在管理员的角度考虑需要关注的性能点 三.站在开发(设计)人员角度去考虑 四.站在测试工程师角度考虑 总结&#xff1a; 一、Android客户端性能测试…

无功功率补偿及电容器的简单介绍 安科瑞 许敏

摘要&#xff1a;主要对无功功率的概念、电容器的补偿方式、补偿容量的计算等方面进行了阐述&#xff0c;在此基础上介绍了电容器运行的注意事项。 关键词&#xff1a;电力电容器&#xff1b;补偿方式 &#xff1b;运行 0 引言 笔者从进入吕合煤业从事电工工作的那天起&#…

Wav2Lip原理以及训练

原理 1: 音视频同步判别器 常规SyncNet&#xff1a; 功能&#xff1a;音频和嘴唇同步 实质&#xff1a;判断音频和唇形在某个共同参数空间下的相似性。 网络结构&#xff1a; 一种伪孪生网络结构&#xff0c;分别提取嘴形特征和音频特征&#xff0c;然后通过对比损失计算两者…

VS2017+OpenCV4.5.1 安装与配置,扩展模块opencv_contrib的安装与配置

文章目录 VS2017OpenCV4.5.1 安装与配置&#xff0c;扩展模块opencv_contrib的安装与配置1、OpenCV下载&#xff1a;&#xff08;1&#xff09;下载地址&#xff1a;https://opencv.org/releases/page&#xff08;2&#xff09;解压到指定文件夹&#xff1a; 2、配置环境&#…

如何进行可视化的数据过滤?Sugar BI 的过滤组件教你快速实现

Sugar BI 中支持了 10种过滤组件&#xff0c;这些过滤组件都是让用户在浏览报表或大屏的时候&#xff0c;能够交互式的对页面上的图表进行数据的过滤。所有过滤组件对图表的数据过滤设置都是一样的&#xff0c;如下&#xff1a; 例如页面中已有两个图表&#xff08;这两个图表…

人脑髓鞘化

髓鞘化 大纲&#xff1a;髓鞘化定义&#xff1b;髓鞘化能用来干嘛&#xff1b;髓鞘化现阶段存在的痛点&#xff1b;现有方法如何解决问题&#xff1b;我们方法的优势。 定义 髓鞘化是指髓鞘发展的过程&#xff0c;它使神经兴奋在沿神经纤维传导时速度加快&#xff0c;并保证…

开窗函数分享

开窗函数定义 开窗函数&#xff1a;用于为行定义一个窗口&#xff0c;它一组值进行操作&#xff0c;不需要使用group by子句对数据进行分组&#xff0c;能够在同一行中同时返回基础行的列和聚合列。 划重点!!! 开窗函数返回&#xff1a;基础行列、聚合列 下面通过例子看一下…

OJ# 376 机器翻译

题目描述 ​ 小李的电脑上安装了一个机器翻译软件&#xff0c;他经常用这个软件来翻译英语文章。 ​这个翻译软件的原理很简单&#xff0c;它只是从头到尾&#xff0c;依次将每个英文单词用对应的中文含义来替换。对于每个英文单词&#xff0c;软件会先在内存中查找这个单词的…

简单易用多git服务器ssh密钥配置管理

文章目录 前言一、什么是ssh-key二、配置步骤添加ssh-key配置多ssh-key 总结 前言 快速理解如何配置管理多个git服务器的ssh&#xff0c;当我们有多个git帐号时会涉及如何管理不同的remote使用不同的git账户登陆推送 当前repo的origin remote是github&#xff0c;我们在推送时…

EMMC基础知识总结

1、说明 1.0 整体架构 ‘ EMMC最简单的可理解为带有控制器的FLASH&#xff0c;具体结构如下&#xff1a; EMMC&#xff1a; Embedded multiMediaCard EMMC. EMMC内部&#xff1a; host interface 、 flash controller 、 flash memory 1.1 flash memory 结构 EMMC 中一般…

学习Vue3——watchEffect(高级侦听器)

立即运行一个函数&#xff0c;同时响应式地追踪其依赖&#xff0c;并在依赖更改时重新执行。 watchEffect有两个参数 第一个参数就是要运行的副作用函数 第二个参数是一个可选的选项&#xff0c;可以用来调整副作用的刷新时机或调试副作用的依赖 API—watchEffect 基本用法 …

电脑必备,推荐几款好用的程序软件,让你工作更加高效

在当今的信息化时代&#xff0c;电脑已成为我们日常生活中必不可少的工具。而在电脑上安装一些好用的程序软件&#xff0c;能够大大提高我们的工作效率和体验。但是市面上的软件五花八门&#xff0c;要如何选择呢&#xff1f;下面将为大家推荐几款值得使用的程序软件&#xff0…

告别脚本小子系列丨JAVA安全(8)——反序列化利用链(下)

0x01 前言 在前面的文章中介绍了基于CC链的反序列化利用方式&#xff0c;并且通过最终调用Runtime类的exec方法达到命令执行的效果。在CC链中还可以通过xalan来执行命令。 xalan是java操作xml的库&#xff0c;属于java内置的官方库之一&#xff0c;在CC链中主要用到的是com.sun…

论文研读|TNNLS 期刊近三年对话生成工作介绍(2篇)

前言&#xff1a;本篇博客记录TNNLS期刊近三年的对话生成相关工作中本人比较感兴趣的两篇工作。首先给大家分享一下论文精确查找的方式&#xff0c;然后对两篇工作的主要思想进行简要介绍。 目录 1. 论文精确查找方法2. 论文简介2.1 面向用户个性保持与回复多样性的两阶段对话生…

错误:No module named ‘osgeo’

from osgeo import gdal 报错&#xff1a;No module named ‘osgeo’ pip install gdal 会出错&#xff0c;也不知道什么原因。 解决方案&#xff1a; 下载whl,然后pip install .whl即可。 详细步骤如下&#xff1a; whl下载地址&#xff1a;https://www.lfd.uci.edu/~go…

基于java+servlet+mysql-图书商城

基于javaservletmysql-图书商城 一、系统介绍二、功能展示1.项目骨架2.首页3.图书详情4.我的订单5.我的购物车6、注册7、登录8、图书管理9、订单管理 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于javaservlet…