java高级——注解

本文详细介绍了Java注解的概念、使用场景,包括自定义注解的创建、元注解如@Target和@Retention的作用,以及注解的解析过程,最后通过模拟单元测试框架展示了注解的实际应用,涉及反射技术。
摘要由CSDN通过智能技术生成

什么是注解

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

注解使用场景

  1. 比如:Junit框架的@Test注解可以用在方法上,用来标记这个方法是测试方法,被@Test标记的方法能够被Junit框架执行。
  2. 再比如:@Override注解可以用在方法上,用来标记这个方法是重写方法,被@Override注解标记的方法能够被IDEA识别进行语法检查。
  3. 注解不光可以用在方法上,还可以用在类上、变量上、构造器上等位置。

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

自定义注解

就是自己定义注解。

public @interface 注解名称{ 
    public 属性类型 属性名()default 默认值,
}

比如:现在我们自定义一个MyTest注解,注意在idea新建的时候不是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注解字节码反编译我们会发现:
20240226-235521-gV.png

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

元注解

什么是元注解?
元注解是修饰注解的注解。
20240226-235736-5a.png
常见的元注解有:@Target注解和@Retention注解

@Target注解

作用:声明被修饰的注解只能在哪些位置使用
格式:@Target(ElementType.TYPE)

1. TYPE,类,接口
2. FIELD,成员变量
3. METHOD,成员方法
4. PARAMETER,方法参数
5. CONSTRUCTOR, 构造器
6. LOCAL VARIABLE,局部变量

@Retention注解

作用:声明注解的保留周期。
格式:@Retention(RetentionPolicy.RUNTIME)

1. SOURCE
只作用在源码阶段,字节码文件中不存在。
2. CLASS(默议值)
保留到字节码文件阶段,运行阶段不存在.
3. RUNTIME(开发常用)
一直保留到运行阶段。

注解的解析

注解解析的概述

  1. 什么是注解的解析?
    就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。
  2. 如何解析注解?
  • 指导思想:要解析谁上面的注解,就应该先拿到谁。
  • 比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解。
  • 比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解。
  • Class、 Method、 Field, Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。

20240227-000742-VQ.png

注解解析的案例

需求如下:
20240227-001040-oM.png

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

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

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

    }
}

  1. 写一个测试类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()));
        }
    }
}

  1. 运行结果

parseClass()的运行结果如下:
20240227-001405-ly.png

parseMethods()的运行结果如下:
20240227-001433-jc.png

应用场景案例

模拟unit框架

需求

定义若干个方法,只要加了@MyTest注解,就会触发该方法执行,没有@MyTest注解的方法不能被框架执行。

分析

  1. 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在。
  2. 定义若干个方法,部分方法加上@MyTest注解修饰,部分方法不加。
  3. 模拟一个junit程序,可以触发加了@MyTest注解的方法执行。

实现步骤

  1. 第一步:先定义一个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====

原理就是反射 + 注解 实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿意做鱼的小鲸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值