java

java高级——注解

Nick · 2月27日 · 2024年本文4175字 · 阅读11分钟907

什么是注解

注解就是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注解字节码反编译我们会发现:
java高级——注解-Nickの博客

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

元注解

什么是元注解?
元注解是修饰注解的注解。
java高级——注解-Nickの博客
常见的元注解有:@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接口,它们都拥有解析注解的能力。

java高级——注解-Nickの博客

注解解析的案例

需求如下:
java高级——注解-Nickの博客

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

  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()的运行结果如下:
java高级——注解-Nickの博客

parseMethods()的运行结果如下:
java高级——注解-Nickの博客

应用场景案例

模拟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====

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

0 条回应
在线人数:1人 来访统计
新时代
Ado
隐藏