注解

108 阅读4分钟

1.注解简介

1.1 什么是注解

  • 注解:Annotation,@interface,是和class、inteface同一等级的代码说明。
  • 是一种记号,记号本身没有功能,是其它软件、其它代码增加的功能。
  • 可以这样理解注解:想像代码具有生命,注解就是对于代码中某些鲜活个体的贴上去的一张标签。简化来讲,注解如同一张标签。

💡注意:注解不同于注释。

  • 注释是给开发人员看的,没有任何功能。 ///**/ /***/
  • 注解是给框架/软件看的,可以由框架或者其它软件赋予注解一些功能

1.2 JDK的常见注解

  • 编译检查:

    • @Override:被@Override标记的方法,idea/eclipse会提供功能:检查语法
    • @Deprecated:被@Deprecated标记的方法,idea/eclipse会提供功能:把使用方法的代码划掉
    • @SuppressWarning:压制警告,idea/eclipse不会显示警告信息
  • 辅助生成文档:

    • @author:作者
    • @version:版本
  • 代替xml配置文件:

    • @WebServlet:Tomcat软件提供的,用于配置Servlet,代替web.xml的

2. 定义注解

2.1 定义注解的语法

@元注解
public @interface 注解名称{
    属性类型 属性名称() [default 默认值];
    ...
}

例如:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

2.2 元注解

元注解:由JDK提供的,用于限制/修饰自定义注解的注解。常见有的2个:

  • @Target:用来限制自定义注解可以用在什么地方。从ElementType里取值的,常用值有:

    • ElementType.TYPE:可以用在类、接口上
    • ElementType.METHOD:可以用在方法上
    • ElementType.FIELD:可以用在字段(成员变量)上
  • @Retention:用来限制自定义注解保留到什么阶段(生命周期)。从RetentionPolicy里取值,常用的:

    • RetentionPolicy.SOURCE:保留到源码阶段
    • RetentionPolicy.CLASS:保留到字节码阶段
    • RetentionPolicy.RUNTIME:保留到运行阶段

02.元注解@Retention的保留策略.png

2.3 注解的属性

注解的属性,并非所有类型都可以。只支持:

  • 8种基本数据类型
  • String类型
  • Class类型
  • 注解类型
  • 枚举类型:enum
  • 以及以上类型的一维数组形式

2.4 自定义注解示例

@Target({ElementType.METHOD, ElementType.FIELD})//可以用在方法上、字段上
@Retention(RetentionPolicy.RUNTIME)//保留到运行阶段
public @interface MyAnno {

    String value() default "default value";

    String[] abc() default "abc";

}

2.5 注解的本质

我们可以打开文件夹,找到MyAnno.class所在的路径,打开cmd,执行命令:javap MyAnno.class查看字节码内容:

image-20240228235558472.png

  • 注解的本质是一个接口,它继承了 java.lang.annotation.Annotation父接口;
  • 注解里的属性,其实是接口里的一个抽象方法
  • 我们在使用注解时添加的@MyAnno,其实是接口的实现类对象

3. 使用注解★

3.1 注解的使用方式

  • @注解名(属性名=值, 属性名=值,...)
  • 如果某个属性有默认值,在使用注解时,可以不用再赋值;如果没有默认值,使用时就必须赋值

value属性的特殊情况:

  • 如果注解里只有一个属性要赋值,并且属性名称是value,使用时可以简写成:@注解名(值)
  • 这个值就是设置给value参数的

3.2注解使用示例

public class Demo01MyAnnoTest {

    //不给属性赋值,所有属性使用默认值
    @MyAnno 
    private String str1;
    
    //给abc属性赋值aaaa,value属性不赋值(使用默认值)
    @MyAnno(abc="aaaa")
    private String str2;

    //给value属性赋值,abc属性不赋值(使用默认值)。只给value赋值,可以省略"value="不写
    @MyAnno("value的值")
    private String str3;
    
    //给abc和value两个属性都赋值
    @MyAnno(abc = "aaaa", value = "value的值")
    private String str4;
    
    @MyAnno(value = "VALUE1", abc = "abcabc")
    public void show(){
        System.out.println("======show()======");
    }
}

4. 解析注解

我们知道,注解本身是没有任何功能的,仅仅是相当于一个记号(标记)。把注解(记号)标记在某个类上或方法上,在使用反射调用时,可以根据注解上是否有注解,来增加不同的功能代码

4.1 解析注解相关API

ClassConstructorMethodField都实现了AnnotatedElement接口中注解相关的方法

image.png

常用的方法有:

方法参数返回值
isAnnotationPresent(Class annoClass)注解类型boolean,是否有此注解
getAnnotation(Class annoClass)注解对象

4.2 示例代码

public class Demo02MyAnnoParser{

    @MyAnno(value = "VALUE1", abc = "abcabc")
    public void show(){
        System.out.println("======show()======");
    }

    //如果show方法上有MyAnno注解,并且value值值是"VALUE",则执行show方法;否则不执行
    public static void main(String[] args) throws Exception {
        Demo02MyAnnoParser demo = new Demo02MyAnnoParser();

        Method method = demo.getClass().getMethod("show");

        //判断方法上是否有MyAnno注解
        boolean b = method.isAnnotationPresent(MyAnno.class);
        System.out.println("DemoMyAnnoParseTest类上有MyAnno注解吗?" + b);
        if (b) {
            //获取方法配置的注解对象
            MyAnno myAnno = method.getAnnotation(MyAnno.class);
            //获取注解对象的属性值
            String value = myAnno.value();
            String[] abc = myAnno.abc();
			
            System.out.println("value:" + value);
            System.out.println("abc:" + Arrays.toString(abc));
        }
    }
}