java:annotation注解

java:annotation注解

1 annotation

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

annotation可用于类、构造方法、成员变量、方法、参数等的声明中,该功能并不影响程序的运行,但是会对编译器警告等辅助工具产生影响。

2 定义Annotation类型

在定义annotation类型时,也需要用到定义接口的interface关键字,但需要在interface关键字前加上1个"@"符号,即定义Annotation类型的关键字是@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。

定义形式:

public @interface TryAnnotation {
}

注意:上面定义的Annotation类型@TryAnnotation不包含任何成员,这样的Annotation类型被称为marker annotation。

下面定义一个只包含一个成员的Annotation类型:

public @interface TryAnnotation {
    String val();
}
/*
String val();表示注解有一个名为 val 的可选参数。
不设置的话默认为""。
如果没有后面的 default "",则表示这是一个必须的参数。
不指定的话会报错。
*/

String:成员类型。可用的成员类型有String、Class、primitive(原始的)、enumerated和annotation,以及所列类型的数组(基本类型int\short\long\float\double\byte\boolean)。
val:成员名称。如果在所定义的Annotation类型中只包含一个成员,通常将成员名称命名为value(这里是val)。

下面是包含多个成员的Annotation类型:

@interface MyAn{
    String describe();
    int type();
}
@interface MyAn{
    String describe() default "xiaoxu";
    int type() default 28;
    Class<? extends Annotation> annotation();
}

定义成员时,可以设置默认值。

@interface MyAn{
    String describe() default "xiaoxu";
    int type() default 28;
}

@Target - 标记这个注解应该是哪种 Java 成员

在定义Annotation类型时,还可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类。如果未设置@Target,则表示适用于所有程序元素。枚举类ElementType中的枚举常量用来设置@Target

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}

通过Annotation类型@Retention可以设置Annotation的有效范围,枚举类RetentionPolicy中的枚举常量用来设置@Retention。

public enum RetentionPolicy {
    SOURCE,       /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */

    CLASS,        /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */

    RUNTIME       /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

枚举类RetentionPolicy(retention:保留)中的枚举常量:

SOURCE:表示不编译Annotation到类文件中,有效范围最小;
CLASS:表示编译Annotation到类文件中,但是在运行时不加载Annotation到JVM中。
RUNTIME:表示在运行时,加载Annotation到JVM中,有效范围最大。

定义一个注释构造方法的Annotation类型@Constructor_Annotation,有效范围为在运行时加载Annotation到JVM中
在这里插入图片描述
tips:黄色的是注解

以上缩写就是:
在这里插入图片描述
在这里插入图片描述
然后定义一个用来注释字段、方法和参数的Annotation类型@Field_Method_Parameter_An,有效范围为在运行时加载Annotation到JVM中。

//用于字段、方法和参数
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
//在运行时加载Annotation到JVM中
@interface Field_Method_Parameter_An{
    String describe();  //没有默认值的String型成员
    Class type() default void.class;  //有默认值的Class型成员
}

最后编写一个Record类,在该类中,运用前面定义的@ConstructorAn和@Field_Method_Parameter_An,对构造方法、字段、方法和参数进行注释。

import java.lang.annotation.*;

@Target(ElementType.CONSTRUCTOR)
//用于构造方法
@Retention(RetentionPolicy.RUNTIME)
//在运行时加载Annotation到JVM中
@interface ConstructorAn{
    String value() default "默认构造方法";
}

//用于字段、方法和参数
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
//在运行时加载Annotation到JVM中
@interface Field_Method_Parameter_An{
    String describe();  //没有默认值的String型成员
    Class type() default void.class;  //有默认值的Class型成员
}

class Record{
    @Field_Method_Parameter_An(describe = "编号",type = int.class)
    int id;
    @Field_Method_Parameter_An(describe = "姓名",type=String.class)
    String name;
    @ConstructorAn()
    public Record(){

    }
    @ConstructorAn("立即初始化构造方法")
    public Record(//注释构造方法
            @Field_Method_Parameter_An(describe = "构造-编号参数",type=int.class)int id,
            @Field_Method_Parameter_An(describe = "构造-姓名参数",type=String.class)String name
    ){
        this.id=id;
        this.name=name;
    }
    @Field_Method_Parameter_An(describe = "获得编号方法",type=int.class)
    public int getId(){//注释方法
        return id;
    }
    @Field_Method_Parameter_An(describe = "设置编号方法")
    public void setId(
            //成员type采用默认值注释方法
            //注释方法的参数
            @Field_Method_Parameter_An(describe = "设置编号参数",type = int.class)int id
    ){
        this.id=id;
    }
    @Field_Method_Parameter_An(describe = "获得姓名方法",type=String.class)
    public String getName(){
        return this.name;
    }
    @Field_Method_Parameter_An(describe = "设置姓名方法")
    public void setName(
            @Field_Method_Parameter_An(describe = "设置姓名参数",type = String.class)String name
    ){
        this.name=name;
    }
}

3 访问Annotation信息

如果在定义Annotation类型时,将@Retention设置为RetentionPolicy.RUNTIME,那么在运行程序时,通过反射就可以获取到相关的Annotation信息,如获取构造方法、字段和方法的Annotation信息。

类Constructor、Field和Method均继承了AccessibleObject类,在AccessibleObject中定义了3个关于Annotation的方法,其中方法isAnnotationPresent(Class<? extends Annotation>annotationClass)用来查看是否添加了指定类型的Annotation,如果是则返回true,否则false;方法getAnnotation(Class annotationClass)用来获得指定类型的Annotation,如果存在则返相应的对象,否则返回null;方法getAnnotations()用来获得所有的Annotation,该方法返回1个Annotation数组

在类Constructor和Method中还定义了方法getParameterAnnotations(),用来获得为所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明的顺序相同,如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。

3.1 访问Annotation信息

改造如上Record,实现在程序运行时通过反射访问Record类中的Annotation信息。

package com.base;

import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

public class Annota {
    public static void main(String[] args) {
        Record r=new Record();
        Class<? extends Record> r1=r.getClass();

        Constructor[] c=r1.getConstructors();
        List<Constructor> l= Arrays.asList(c);
        l.forEach(System.out::println);
        for (Constructor constructor : l) {
            System.out.println("----------------------");
            System.out.println(constructor);
            //查看是否具有指定类型的注释
            if(constructor.isAnnotationPresent(ConstructorAn.class)){
                //获得指定类型的注释
//                Annotation annotation = constructor.getAnnotation(ConstructorAn.class);
                ConstructorAn annotation = (ConstructorAn) constructor.getAnnotation(ConstructorAn.class);
                System.out.println(annotation.value()); //获得注释信息
            }
            Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                //获得指定参数注释的长度
                int length=parameterAnnotations[i].length;
                if(length==0)
                    //长度为0,表示没有为该参数添加注释
                    System.out.println("未添加Annotation的参数");
                else{
                    for (int i1 = 0; i1 < length; i1++) {
                        //获得参数的注释
                        Field_Method_Parameter_An pa=(Field_Method_Parameter_An) parameterAnnotations[i][i1];
                        System.out.println(pa.describe());//获得参数描述
                        System.out.println(pa.type());//获得参数类型
                    }
                }
            }
        }

        System.out.println("\nfield------");
        Field[] f=r1.getDeclaredFields();
        List<Field> fl=Arrays.asList(f);
        fl.forEach(System.out::println);
        for (Field field : fl) {
            //查看是否具有指定类型的注释
            if(field.isAnnotationPresent(Field_Method_Parameter_An.class)){
                System.out.println("字段注解存在:");
                Field_Method_Parameter_An annotation = field.getAnnotation(Field_Method_Parameter_An.class);
                System.out.println(annotation.describe()); //获得字段的描述
                System.out.println(annotation.type()); //获得字段的类型
            }
        }

        System.out.println("\nmethod------");
        //访问方法及其包含参数得Annotation信息:
        Method[] m=r1.getDeclaredMethods(); //获得所有方法
        for (Method method : m) {
            if(method.isAnnotationPresent(Field_Method_Parameter_An.class)){
                System.out.println("\n方法注解存在");
                Field_Method_Parameter_An annotation = method.getAnnotation(Field_Method_Parameter_An.class);
                System.out.println(annotation.describe());//获得方法的描述
                System.out.println(annotation.type());//获得方法的返回值类型
            }
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                int length=parameterAnnotations.length;
                if(length==0)
                    System.out.println("方法没有Annotation的参数");
                else{
                    System.out.println("~~~~~~~~~~~~~~~~~~");
                    for (int i1 = 0; i1 < length; i1++) {
                        //获得指定类型的注释
                        Field_Method_Parameter_An fa = (Field_Method_Parameter_An) parameterAnnotations[i][i1];
                        System.out.println(fa.describe());//获得参数描述
                        System.out.println(fa.type());//获得参数类型
                    }
                }
            }
        }

    }
}

@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
@interface ConstructorAn{
    String value() default "默认构造方法";
}

@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface Field_Method_Parameter_An{
    String describe();
    Class type() default void.class;
}

class Record{
    @Field_Method_Parameter_An(describe = "编号",type=int.class)
    int id;
    @Field_Method_Parameter_An(describe = "姓名",type=String.class)
    String name;

    @ConstructorAn
    public Record() {
    }

    public Record(
            @Field_Method_Parameter_An(describe = "构造-编号参数",type=int.class) int id,
            @Field_Method_Parameter_An(describe = "构造-姓名参数",type=String.class) String name) {
        this.id = id;
        this.name = name;
    }
    @Field_Method_Parameter_An(describe = "获得编号方法",type=int.class)
    public int getId() {
        return id;
    }
    @Field_Method_Parameter_An(describe = "设置编号方法")
    public void setId(
            @Field_Method_Parameter_An(describe = "设置编号参数",type = int.class)int id) {
        this.id = id;
    }

    @Field_Method_Parameter_An(describe = "获得姓名方法",type=String.class)
    public String getName() {
        return name;
    }

    @Field_Method_Parameter_An(describe = "设置姓名方法")
    public void setName(
            @Field_Method_Parameter_An(describe = "设置姓名参数",type=String.class) String name) {
        this.name = name;
    }
}

结果如下:

public com.base.Record()
public com.base.Record(int,java.lang.String)
----------------------
public com.base.Record()
默认构造方法
----------------------
public com.base.Record(int,java.lang.String)
构造-编号参数
int
构造-姓名参数
class java.lang.String

field------
int com.base.Record.id
java.lang.String com.base.Record.name
字段注解存在:
编号
int
字段注解存在:
姓名
class java.lang.String

method------

方法注解存在
获得姓名方法
class java.lang.String

方法注解存在
获得编号方法
int

方法注解存在
设置姓名方法
void
~~~~~~~~~~~~~~~~~~
设置姓名参数
class java.lang.String

方法注解存在
设置编号方法
void
~~~~~~~~~~~~~~~~~~
设置编号参数
int
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值