java注解学习

什么是注解

先举个日常生活中的例子:在超市中有很多商品,并不是每个商品都有商标/条形码的,但是超市为了方便管理,给每个商品都贴了条码,并在系统中存储了信息,结账时直接扫码就行,这样,条码即不会对商品有影响,超市还提高了效率。

如果你明白了上面的例子,那么注解就很好理解了。你写的代码其实就是超市中的商品;写的代码是为了运行实现功能,对应的商品的意义也是实现价值,被购买;为了方便商品的操作,我们对商品贴了条码,对商品来说没什么影响,只是对我们、对收银员有一个标识的作用,对应的就是注解。

下面引入注解的正经说明:

注解是插入你代码中的一种注释或者说是一种元数据(meta data)。这些注解信息可以在编译期使用预编译工具进行处理(pre-compiler tools)

用我的的话就是:

注解就是给你的代码加一些解释性的标注,代码该怎么跑还是怎么跑。注解的意义就是有一些工具对你的代码进行处理时给那些工具指明个目标。

注解的定义

注解的定义方法

注解的定义很简单,使用 @interface关键字进行定义。

注解里的属性定义格式是 “属性类型 属性名+括号;”,如果像加个默认值的话就“属性类型 属性名+括号 default 默认值;”。

示例如下:

public @interface StudentCard{
    int id();
    String name() default "未命名";
    int level default 1;
}

使用此注解的操作如下:

//张三,学号001,卡级别10
@StudentCard(id=001,name="张三",level=10)

//名称为“未命名”,学号002,卡级别为默认的1
@StudentCard(id=002)

元注解

元注解就是可以注释到注解上边的注解,也称为基本注解

元注解就是专门为注解定义而使用的,下面对元注解进行罗列

@Retention

Retention英文意思是保留的意思,就是指你的注解(也就是你代码的标签)保留到什么时候。是编译时后摘了还是保存到运行时。

取值如下:

  • RetentionPolicy.SOURCE:只在源码阶段保留,编译之前就扔了【它的声明周期就能接触到编译器了吧,一般是写给编译器看的】
  • RetentionPolicy.CLASS:保留到编译阶段,也就是说编译生成的.class文件中还有,运行时就没了【没有特备声明的话默认就是这个选项】
  • RetentionPolicy.RUNTIME:在运行阶段也保留,从头到尾都保留【可以用来反射编程】

标记的格式如下:

@Retention(RetentionPolicy.SOURCE)

@Documented

这个标注的意思是把这个加入javaDoc,不怎么用,至少我是没怎么用过。

@Target

这个标注是用来限制你定义的注解适用的对象的。

可取值如下:

  • ElementType.TYPE:表明此定义的注解可适用于类、接口(包括注解)、枚举
  • ElementType.FIELD:表明此定义的注解可适用于类内的变量(枚举常量也行)
  • ElementType.METHOD:表明此定义的注解可适用于方法
  • ElementType.PARAMETER:表明此定义的注解可适用于方法传入的参数【举个栗子@PathVariable
  • ElementType.CONSTRUCTOR:表明此定义的注解可适用于构造函数
  • ElementType.LOCAL_VARIABLE:表明此定义的注解可适用于局部变量
  • ElementType.ANNOTATION_TYPE:表明此定义的注解可适用于声明的变量【什么鬼???】
  • ElementType.PACKAGE:表明此定义的注解可适用于包
  • ElementType.TYPE_PARAMETER:表明此定义的注解可适用于传入的类型参数
  • ElementType.TYPE_USE:不清楚了,源码的注解是 use of a type ,后面接触到再完善吧

因为一个注解可以用用在很多地方(看你想怎么用喽),所以@Target的使用方法如下:

@Target({ElementType.PARAMETER,ElementType.LOCAL_VARIABLE,ElementType.FIELD})

@Inherited

这个注解的意思是可继承。你写了一个注解X,然后用X标注了一个类A,B是A的子类,如果你定义注解时用了@Inherited标注,那么B就等于也被注解X标注了。

@Repeatable

这个注解的意思是是否可重复的意思,这个的使用方法有点复杂了,因为可重复意味着你可以对一个地方打多个注解,注解的存储就得用数组了,所以你得在你的注解的基础上再定义一个以此注解数组为值的注解,说的有点拗口,我们直接看示例代码:

//先声明一个注解,你要用的注解
//这里加了IdCards.class 在处理IdCard.class 注解时就放在IdCards.class的值中
@Repeatable(IdCards.class)
public @interface IdCard{
    int id() default 1;
    String name() default "未命名";
    String department() default "商城研发部";
}


//下面是IdCards.class的定义
public @interface IdCards {
    IdCard[] value();
}

IdCards.class的定义有几个注意点:

  1. 属性名一定要用value
  2. 尽可能做到见名知意,知道是为谁服务的
  3. IdCards的适用对象范围、声明周期范围应不小于IdCard的范围

使用的时候IdCards不用使用,直接用IdCard疯狂的重复标注就行了

注解的使用

注解很好用,打标签嘛,贴上去就行了,这里主要将如何把标签读出来,也就是超市的扫码过程。

注解的提取

注解通过反射提取,可以使用Class对象的方法进行判断是否使用了哪个注解:

public boolean isAnnotationPresent(Class annotationClass);

可以通过下面的方法来获取特定的注解的对象:

public  A getAnnotation(Class annotationClass);

可以通过下面的方法获得所有的注解对象:

public Annotation[] getAnnotations();

注解对象内往往是私有属性,不可达,还要用一个方法来设置可达性:

Field.setAccessable(true);

附一个完整的使用的例子:

注解定义如下:

@Target({ElementType.PARAMETER,ElementType.LOCAL_VARIABLE,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Hehe {
    int intValue() default -1;
    String stringValue() default "undefinedStringValue";
    double doubleValue() default -1d;
}

注解的使用如下:

@Hehe(intValue = 200)
public class testAnnotation {
    @Hehe(stringValue = "hehe")
    private String lpc;
    public static void main(String args[]){
        testAnnotation testAnnotation = new testAnnotation();

        try{
            Field lpc = testAnnotation.getClass().getDeclaredField("lpc");
            lpc.setAccessible(true);

            Hehe hehe = lpc.getAnnotation(Hehe.class);

            if (hehe != null){
                System.out.println("变量注解中的值为: int="+ hehe.intValue());
                System.out.println("变量注解中的值为: string="+ hehe.stringValue());
                System.out.println("变量注解中的值为: double="+ hehe.doubleValue());
            }

            if ( testAnnotation.class.isAnnotationPresent(Hehe.class) ) {
                Hehe hehe1 = testAnnotation.class.getAnnotation(Hehe.class);

                System.out.println("类的注解中的值为 int="+hehe1.intValue());
            }

            try {
                Method testMethod = testAnnotation.class.getDeclaredMethod("wlgq",String.class);
                if ( testMethod != null ) {
                    // 获取方法中的注解
                    Annotation[] ans = testMethod.getAnnotations();
                    for( int i = 0;i < ans.length;i++) {
                        System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                    }
                }

            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }



        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        System.out.println(testAnnotation.getLpc());
    }

    @Hehe(intValue=100)
    public static void wlgq(String temp){
        System.out.println(temp);
    }
    public String getLpc() {
        return lpc;
    }

    public void setLpc(String lpc) {
        this.lpc = lpc;
    }
}

获得注解的值后就该怎么操作怎么操作呗。

注解的用法

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

还是回到官方文档的解释上,注解主要针对的是编译器和其它工具软件(SoftWare tool)。

当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。

现在,我们可以给自己答案了,注解有什么用?给谁用?给 编译器或者 APT 用的。

参考文献

https://blog.csdn.net/briblue/article/details/73824058 感谢大佬的作品

你可能感兴趣的:(java注解学习)