java基础-注解定义和解析

  • Post author:
  • Post category:java





一、什么是注解?

Annotation表示注解。是JDK1.5的新特性。

注解的主要作用:对我们的程序进行标注。通过注解可以给类增加额外的信息。

注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。



二、注解



1. 自定义注解的定义

public @interface 注解名称 {    
	public 属性类型 属性名();
}
注解中的属性可以有默认值,格式为:
数据类型   属性名()   default  默认值;

属性类型可以是基本数据类型,String,Class,注解,枚举,以及上述类型的数组。

public @interface MyAnno {
    /**
     * 空注解
     */
}
public @interface MyAnno2 {
    //public 属性类型 属性名() default 默认值;
    public String name();

    /**
     * 注解中的属性可以有默认值,格式为:
     *
     * 数据类型   属性名()   default  默认值;
     * @return
     */
    public int age() default 0;
    public String gender() default "男";
}
public @interface MyAnno3 {
    String value();
}



2. 自定义注解的使用

使用自定义注解格式:
		@注解名(属性名1=值1, 属性名2=值2)
注解中,有默认值的属性可以不赋值。
没有默认值的属性一定要赋值。
/**
 * 使用自定义注解格式
 * @注解名(属性名1=值1, 属性名2=值2)
 * 注解中,有默认值的属性可以不赋值。
 * 没有默认值的属性一定要赋值。
 */

@MyAnno2(name="测试")
public class Test {
    @MyAnno3("单个注解且属性为value可以省略value=")
    private int a;
    @MyAnno
    public static void test(){

    }
}

注意:

1.注解可以加在类定义上,变量定义上,方法定义上,不能单独存在。

2.一个注解不要在同一个地方加多次。

3.当注解中只有”一个属性”,并且属性名是”value”,使用注解时,可以省略value属性名



3. 常用元注解

元注解是指:修饰注解的注解

注解名 说明
@Target 指定注解能在哪里使用
@Retention 注解的生命周期,可以理解为保留时间
@Documented 表示注解在生成帮助文档时会保留下来
Inherited 表示该注解会被子类继承

元注解总共有4个,常用的就是前两个:@Target和@Retention。



3.1 @Target

作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。

可使用的值定义在 ElementType枚举类 中,常用值如下:

TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造方法
LOCAL_VARIABLE, 局部变量
/**
 * 常用的四个注解:
 * 分别是:
 * 可以用在类或者接口上
 * 可以用在变量上
 * 可以用在方法上
 * 可以用在构造方法上
 */
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.CONSTRUCTOR})
public @interface MyAnno4 {
    /**
     * @Target(ElementType枚举类) 用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置
     * TYPE,类,接口
     * FIELD, 成员变量
     * METHOD, 成员方法
     * PARAMETER, 方法参数
     * CONSTRUCTOR, 构造方法
     * LOCAL_VARIABLE, 局部变量
     */
}
@MyAnno4
public class Test02 {
    @MyAnno4
    private int a;
    @MyAnno4
    public Test02(){

    }
    @MyAnno4
    public static void test(){

    }
}



3.2 @Retention

作用:用来标识注解的生命周期。(即该注解会保留到哪个阶段)

可使用的值定义在 RetentionPolicy枚举类 中,常用值如下:
SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在。
CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在。(默认值)
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段。

在这里插入图片描述



3.2.1 RetentionPolicy.SOURCE

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE) //指定生命周期的在源码阶段,编译之后就消失了
public @interface Hello {
}

@Hello
public class test03 {
    public static void main(String[] args) {
    }
}

在这里插入图片描述

这里看到反编译之后在字节码文件中注解已经消失了



3.2.2 RetentionPolicy.CLASS(默认值)

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.CLASS) //不指定就是默认值,编译的时候存在,运行的时候就消失
public @interface Hello {
}

在这里插入图片描述

这里看到反编译之后在字节码文件中注解还存在,但是在运行过程中就已经不存在。

例如:

一个Message注解生命周期为默认

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public @interface Message {
    public String name();
    public int age();
}
@Message(name = "张三",age=18)
public class People {
}
/**
 * 解析注解
 */
public class Test {
    public static void main(String[] args) {
        //注解在谁头上就反射谁
        Class<People> clazz = People.class;
        //获取注解 getAnnotation(注解名)
        Message annotation = clazz.getAnnotation(Message.class);
        //通过注解名调用属性
        System.out.println(annotation.name());
        System.out.println(annotation.age());
    }
}

在这里插入图片描述

通过反射去获取注解值,这个时候程序已经在运行阶段,如果生命周期默认为CLASS那么,运行阶段注解已经消失了,值为null,所以会报空指针异常。



3.2.1 RetentionPolicy.RUNTIME

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Message {
    public String name();
    public int age();
}
@Message(name = "张三",age=18)
public class People {
}
/**
 * 解析注解
 */
public class Test {
    public static void main(String[] args) {
        //注解在谁头上就反射谁
        Class<People> clazz = People.class;
        //获取注解 getAnnotation(注解名)
        Message annotation = clazz.getAnnotation(Message.class);
        //通过注解名调用属性
        System.out.println(annotation.name());
        System.out.println(annotation.age());
    }
}

这里通过反射解析注解的时候通过注解去获取属性值,这个时候就已经是运行阶段了,注解@Message就需要指定生命周期为RUNTIME,运行阶段就可以通过反射获取到注解值。

在这里插入图片描述



三、注解解析

什么是注解解析?

使用Java反射技术,获得注解的属性数据的过程,称为注解解析

方法名 说明
T getAnnotation(Class annotationClass) 根据注解类型获得对应注解对象
Annotation[] getAnnotations() 获得当前结构上使用的所有注解
boolean isAnnotationPresent(Class annotationClass) 判断当前结构上是否使用了指定的注解,如果使用了则返回true,否则false

Field,Method,Constructor,Class 等反射相关的类可以调用以上方法

通过反射来解析注解,注解在谁头上就用谁来解析

注解在类上,使用Class来获取。

注解在构造方法上,使用Constructor来获取。

注解在成员方法上,使用Method来获取。

注解在成员变量上,使用Field来获取。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) //注解只能用在方法上
@Retention(RetentionPolicy.RUNTIME) //生命周期作用到运行阶段
public @interface BookMessage {
    String name();
    double price();
    String[] authors();
}
public class Book {
    @BookMessage(name="红楼梦",price = 39.9,authors ={"曹雪芹","高鹗"})
    public void show(){}

}
import java.lang.reflect.Method;

public class test02 {
    public static void main(String[] args) throws Exception {
        Class<Book> clazz = Book.class;
        Method show = clazz.getDeclaredMethod("show");
        BookMessage annotation = show.getAnnotation(BookMessage.class);
        System.out.println(annotation.name());
        System.out.println(annotation.price());
        System.out.println(annotation.authors()[0]);
        System.out.println(annotation.authors()[1]);
    }
}



四、模拟JUnit自带的@Test注解



1. 需求

模拟JUnit自带的@Test注解, 自动运行带@MyTest注解的方法



2. 分析

1.定义MyTest注解。

2.定义普通类并包含多个方法,在一些方法头上配@MyTest注解。

3.使用反射得到类中的所有Method。

4.遍历得到每个Method,如果Method上有MyTest, 就运行这个方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//指定注解只能用在方法上
@Target(ElementType.METHOD)
//指定注解的生命周期RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
	
}
public class Student {
    @MyTest
    public void weakup(){
        System.out.println("wake up!");
    }
    public void eat(){
        System.out.println("eat rice!");
    }
    public void study(){
        System.out.println("study!");
    }
    @MyTest
    public void sleep(){
        System.out.println("good night!");
    }
}
import java.lang.reflect.Method;

/**
 * 注解解析
 */
public class Test {
    /**
     * 使用反射,解析Student类,获取所有加上MyTest的方法,调用并运行
     */
    public static void main(String[] args) throws Exception{
        //1.获取Class对象
        Class<Student> clazz = Student.class;
        //2.反射出所有的方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            //3.判断方法上是否有注解
            if(method.isAnnotationPresent(MyTest.class)){
                //4.调用并运行方法(对象)
                method.invoke(clazz.getDeclaredConstructor().newInstance());
            }
        }

    }
}

在这里插入图片描述



版权声明:本文为weixin_50369190原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。