java高级:注解

2023-09-14 20:48:53

认识注解&自定义注解

注解和反射一样,都是用来做框架的,我们这里学习注解的目的其实是为了以后学习框架或者做框架做铺垫的。

先来认识一下什么是注解?Java注解是代码中的特殊标记,比如@Override、@Test等,作用是:让其他程序根据注解信息决定怎么执行该程序。

比如:Junit框架的@Test注解可以用在方法上,用来标记这个方法是测试方法,被@Test标记的方法能够被Junit框架执行。
再比如:@Override注解可以用在方法上,用来标记这个方法是重写方法,被@Override注解标记的方法能够被IDEA识别进行语法检查。

注解不光可以用在方法上,还可以用在类上、变量上、构造器上等位置。

上面我们说的@Test注解、@Overide注解是别人定义好给我们用的,将来如果需要自己去开发框架,就需要我们自己定义注解。

自定义注解的格式如下图所示
请添加图片描述

比如:现在我们自定义一个MyTest注解,注意新建的时候不是class了,而是Annotation

public @interface MyTest1{
    String aaa();//一定要带小括号 前面的public可以不写 默认自动帮你写了
    boolean bbb() default true;	//default true 表示默认值为true,使用时可以不赋值。
    String[] ccc();
}

定义好MyTest注解之后,我们可以使用MyTest注解在类上、方法上等位置做标记。注意使用注解时需要加@符号,如下

@MyTest1(aaa="牛魔王",ccc={"HTML","Java"})
public class AnnotationTest1{
    @MyTest1(aaa="铁扇公主",bbb=false, ccc={"Python","前端","Java"})
    public void test1(){
        
    }
}

注意:注解的属性名如果是value的话,并且只有value没有默认值,使用注解时value名称可以省略。比如现在重新定义一个MyTest2注解

public @interface MyTest2{
    String value(); //特殊属性
    int age() default 10;
}

定义好MyTest2注解后,再将@MyTest2标记在类上,此时value属性名可以省略,代码如下

@MyTest2("孙悟空") //等价于 @MyTest2(value="孙悟空")
@MyTest1(aaa="牛魔王",ccc={"HTML","Java"})
public class AnnotationTest1{
    @MyTest1(aaa="铁扇公主",bbb=false, ccc={"Python","前端","Java"})
    public void test1(){
        
    }
}

到这里关于定义注解的格式、以及使用注解的格式就学习完了。

那注解本质是什么呢?

想要搞清楚注解本质是什么东西,我们可以把注解的字节码进行反编译,给AnnotationTest1类加个main,运行一下,会使用到的注解都编译成class,再使用XJad工具对class进行反编译。经过对MyTest1注解字节码反编译我们会发现:

1.MyTest1注解本质上是接口,每一个注解接口都继承子Annotation接口
2.MyTest1注解中的属性本质上是抽象方法
3.@MyTest1实际上是作为MyTest接口的实现类对象
4.@MyTest1(aaa="孙悟空",bbb=false,ccc={"Python","前端","Java"})里面的属性值,可以通过调用aaa()、bbb()、ccc()方法获取到。 【继续往下看,再解析注解时会用到】

请添加图片描述




接下来我们还需要学习几种特殊的注解

元注解

什么是元注解?元注解是修饰注解的注解。这句话虽然有一点饶,但是非常准确。我们看一个例子

请添加图片描述

接下来分别看一下@Target注解和@Retention注解有什么作用,它们都是用来修饰Test注解的元注解

@Target是用来声明注解只能用在哪些位置,比如:类上、方法上、成员变量上等
@Retention是用来声明修饰的注解保留周期,比如:源代码时期、字节码时期、运行时期

请添加图片描述

比如我们定义MyTest3注解时,使用@Target注解属性值写成下面样子,加上@Retention则可以决定MyTest3的保留周期,比如我们经常用到的@Test就是保留到运行阶段的,可以点进源码看一下。

//声明@MyTest3注解只能用在类上和方法上
@Target({ElementType.TYPE,ElementType.METHOD})	
public @interface MyTest3{
    
}



解析注解

通过前面的学习我们能够自己定义注解,也能够把自己定义的注解标记在类上或者方法上等位置,但是总感觉有点别扭,给类、方法、变量等加上注解后,我们也没有干什么呀!!!

接下来,我们就要做点什么。我们可以通过反射技术把类上、方法上、变量上的注解对象获取出来,然后通过调用方法就可以获取注解上的属性值了。我们把获取类上、方法上、变量上等位置注解及注解属性值的过程称为解析注解

解析注解套路如下

1.如果注解在类上,先获取类的字节码对象,再获取类上的注解
2.如果注解在方法上,先获取方法对象,再获取方法上的注解
3.如果注解在成员变量上,先获取成员变量对象,再获取变量上的注解
总之:注解在谁身上,就先获取谁,再用谁获取谁身上的注解

请添加图片描述

来看一个案例,来演示解析注解的代码编写

请添加图片描述

按照需求要求一步一步完成

① 先定义一个MyTest4注解

//声明@MyTest4注解只能用在类上和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
//控制使用了@MyTest4注解的代码中,@MyTest4保留到运行时期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4{
    String value();
    double aaa() default 100;
    String[] bbb();
}

② 定义一个类Demo

@MyTest4(value="蜘蛛侠",aaa=99.9, bbb={"至尊宝","牛马"})
public class Demo{
    @MyTest4(value="孙悟空",aaa=199.9, bbb={"紫霞","牛夫人"})
    public void test1(){

    }
}

③ 写一个测试类AnnotationTest3解析Demo类上的MyTest4注解

public class AnnotationTest3{
    @Test
    public void parseClass(){
        //1.先获取Class对象
        Class c = Demo.class;

        //2.解析Demo类上的注解
        if(c.isAnnotationPresent(MyTest4.class)){
            //获取类上的MyTest4注解
            MyTest4 myTest4 = (MyTest4)c.getDeclaredAnnotation(MyTest4.class);
            //获取MyTests4注解的属性值
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }

    @Test
    public void parseMethods() throws NoSuchMethodException {
        //1.先获取Class对象
        Class c = Demo.class;

        //2.解析Demo类中test1方法上的注解MyTest4注解
        Method m = c.getDeclaredMethod("test1");
        if(m.isAnnotationPresent(MyTest4.class)){
            //获取方法上的MyTest4注解
            MyTest4 myTest4 = (MyTest4)m.getDeclaredAnnotation(MyTest4.class);
            //获取MyTests4注解的属性值
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }
}

运行测试类,parseClass()的运行结果如下:

请添加图片描述

parseMethods()的运行结果如下:

请添加图片描述




注解的应用场景

关于注解的定义、使用、解析注解就已经学习完了。接下来,我们再学习一下注解的应用场景,注解是用来写框架的,比如现在我们要模拟Junit写一个测试框架,要求有@MyTest注解的方法可以被框架执行,没有@MyTest注解的方法不能被框架执行。

第一步:先定义一个MyTest注解

@Target(ElementType.METHOD)	
@Retetion(RetetionPloicy.RUNTIME)
public @interface MyTest{
    
}

第二步:写一个测试类AnnotationTest4,在类中定义几个被@MyTest注解标记的方法

public class AnnotationTest4{
    @MyTest
    public void test1(){
        System.out.println("=====test1====");
    }
    
    @MyTest
    public void test2(){
        System.out.println("=====test2====");
    }
    

    public void test3(){
        System.out.println("=====test2====");
    }
    
    public static void main(String[] args){
        AnnotationTest4 a = new AnnotationTest4();
        
        //1.先获取Class对象
        Class c = AnnotationTest4.class;
        
        //2.解析AnnotationTest4类中所有的方法对象
        Method[] methods = c.getDeclaredMethods();
        for(Method m: methods){
            //3.判断方法上是否有MyTest注解,有就执行该方法
            if(m.isAnnotationPresent(MyTest.class)){
            	m.invoke(a);
        	}
        }
    }
}

运行结果:

=====test2====
=====test1====
更多推荐

tcp_v4_connect函数的解析

源码:inttcp_v4_connect(structsock*sk,structsockaddr*uaddr,intaddr_len){//解析输入的地址结构structsockaddr_in*usin=(structsockaddr_in*)uaddr;//获取TCP协议栈的全局death_row对象structi

STM32H5开发(5)----串口打印配置

STM32H5开发----4.开发板介绍概述样品申请硬件准备生成例程配置调试口代码生成配置项目配置调试配置串口重定向打印测试结果概述在使用STM32CUBEIDE开发STM32H5项目时,串口打印被证明是一项极其有益的调试工具,能够在开发过程中实时输出信息和调试数据,起到了至关重要的作用。通过充分利用串口打印功能,开发

Netty面试题(一)

文章目录前言一、BIO、NIO和AIO的区别?二、NIO的组成?三、Netty的特点?总结前言BIO、NIO和AIO的区别?NIO的组成?Netty的特点?一、BIO、NIO和AIO的区别?BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。伪异步IO:将请求连接放入线程池,一对

从零学算法(剑指 Offer 33)

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。参考以下这颗二叉搜索树:5/\26/\13示例1:输入:[1,6,3,2,5]输出:false示例2:输入:[1,3,2,6,5]输出:true我的思路:二叉搜索树就是左节点都

DS相关题目

DS相关题目题目一:消失的数字拿到这道题目之后,首先可以想到的一个解题方法就是,我们可以先排序,排完序之后,这个数组其实就是一个有序的数组了,那只用比较数组中的每一个元素和他对应的下标是不是相等的,如果是相等的,那么就说明对应的数字其实是存在的,如果是不相等的,那么就说明对应的数字其实就是不存在的了,但是如果要排序的话

GIS前端-地图操作与交互

GIS地图操作与交互地图操作与交互基本原理Leaflet提供的事件缩放控件常用的基础功能通常是一个应用系统所必需的,如地图的缩放、导航、定位、弹出框等,它让一张静态的地图动起来,让地图承载更多的空间信息,并以友好的交互方式呈现给用户。例如,一个大众应用的旅游GIS系统,如果仅仅在Web端显示一张地图,那么这时只能看到一

用户案例|Shopee 在多媒体理解业务的向量检索系统实践

Shopee是一家全球性的电商平台,业务范围辐射东南亚、拉美等多个地区。多媒体理解(MultimediaUnderstanding,下文简称MMU)团队是Shopee内专注于提供多媒体内容理解服务的团队,为电商、直播、短视频等业务提供支持。MMU团队需要支持公司不同业务场景对多媒体理解的需求。以向量检索为例,可能会有如

React 简介

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录ComponentJSXMultiplecomponentsprops:passingdatatocomponentsSomenotes我们现在将开始入门的可能是本课程最重要的主题,即React-库。让我们从制

ArmSom-W3开发板之PCIE的开发指南(一)

1.简介RK3588从入门到精通本⽂介绍RK平台配置pcie的方法开发板:ArmSoM-W32、PCIE接口概述PCIe(PeripheralComponentInterconnectExpress)是一种用于连接计算机内部组件的高速接口标准。以下是关于PCIe接口的简要介绍:高速传输:PCIe接口提供了高速的数据传输

0018Java程序设计-springboot智慧环卫养管作业平台

文章目录摘要目录系统设计开发环境摘要本智慧环卫养管作业平台就是建立在充分利用现在完善科技技术这个理念基础之上,并使用IT技术进行对环卫养管作业的管理,从而保证环卫养管作业能够高效的进行,可以实现环卫养管作业的在线管理,这样保证了资源共享效率的最优化,通过系统的管理,使系统的使用率达到最大化。论文采用图文论述方法,通过与

vite+vue3+ts项目基础配置

1.配置项目启动自动打开浏览器在package.json文件中:"scripts":{"dev":"vite",//项目初始化之后默认是这样的-->"dev":"vite--open",//改成这样,加上--open,这样是默认打开浏览器页面},2.项目中eslint校验工具的配置首先安装eslintpnpmiesli

热文推荐