注解-入门

注解概述

  • 注解是JDK1.5的新特性。
  • 标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。
  • 注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。

注解的作用:
  • 使用javadoc生成帮助文档:里边可以包含注解**@author和@version**

  • 编译检查@Override @FunctionalInterface

  • 框架的配置(框架=代码+配置)

自定义注解

定义一个没有属性的注解

格式:

public @interface 注解名{ }

注意:

  • 注解使用的也是.java文件,编译生成的也是.class文件

  • 注解和类和接口和枚举都是同一个层次的,都是一种数据类型

定义一个有属性的注解

注解中没有成员变量,也没有成员方法

注解中可以包含属性,属性看成和抽象方法一个格式,但是可以包含默认值

定义格式:

public @interface 注解名{
	修饰符 数据类型 属性名();
	修饰符 数据类型 属性名() default 默认值;
}

注意:

  • 注解的属性修饰符省略不写则默认为 public abstract ;建议写出,增强语句的可读性

  • 注解属性的数据类型:

    ​ a.基本数据类型(4类8种):byte,short,int,long,float,double,char,boolean

    ​ b.引用数据类型:String类型,反射类型,注解类型,枚举类型

    ​ c.以及以上所有类型的一维数组

定义包含特殊属性value的注解:注解中只有一个属性,并且叫value;或者注解中有其他的属性,但是属性必须有默认值

示例

public @interface MyAnno02 {
    //定义一个int类型的属性
    //public abstract int a();
    //abstract int a();
    int a();

    //定义一个double类型的属性,给属性添加默认值8.8
    public abstract double d() default 8.8;

    //定义一个String类型的数组属性
    public abstract String[] arr();

    //定义反射类型的属性(了解)
    public abstract Class clazz();
    //定义注解类型的属性(了解)
    public abstract MyAnno01 my01();
    //定义枚举类型的属性(了解)
    public abstract Color c();
}


/* 定义包含特殊属性value的注解 */
public @interface MyAnno03 {
    public abstract String value();

    public abstract int aaa() default 10;
}

使用自定义注解

注解可以使用的位置:包上,类上|接口上,成员变量上,成员方法上,构造方法上,局部变量上,方法的的参数上…

注意:

  • 同一个位置,同名的注解只能使用一次

  • 不同的位置,同名的注解可以多次使用

注解的使用格式:

  • 没有属性的注解,通过 @注解名 可以直接使用。例如:@MyAnno01

  • 有属性的注解:必须使用键值对的方式,给注解中所有的属性都赋值之后,才能使用注解

    格式:

    ​ @注解名(属性名=属性值,属性名=属性值,属性名=属性值,…属性名=属性值)

    注:

    ​ a.有默认值的属性,可以不同赋值,使用默认值

    ​ b.给多个属性赋值,中间要使用逗号隔开

    ​ c.属性的数据类型是数组,属性的值需要使用 { } 包裹起来,说明这一组值是一个数组的属性值

    ​ 数组只有一个值,可以省略 { }

    ​ 示例:arr = {“a”,“b”,“c”} arr = {“a”} ==> arr = “a”

    ​ d.注解中只有一个属性,并且叫 value;或者注解中有其他的属性,但是属性均有默认值

    ​ 那么我们在使用注解的时候,给属性赋值,可以省略属性名,直接写属性值

    ​ 示例:(任意的数据类型)value=“aaa” ==> “aaa”

示例

@MyAnno01	// 没有属性的注解
@MyAnno02(a = 10, arr={"aaa","bbb","ccc"})	// 有多个属性的注解
public class UseMyAnno {
    @MyAnno01
    @MyAnno02(a=100, d=1.1, arr="aaa")	// arr属性的数据类型是数组,只有一个值的情况,可省略 {}
    private String name;

    @MyAnno01
    @MyAnno03(value="aaa")
    public UseMyAnno() {
    }

    @MyAnno01
    @MyAnno03("www")	// 可省略属性名的注解
    public UseMyAnno(String name) {
        this.name = name;
    }

    @MyAnno01
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

元注解

元注解:java已经定义好的注解,可以使用元注解修饰自定义的注解

1.java.lang.annotation.Target

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

属性:

  • ElementType[] value :只有一个属性,属性名叫value(使用的时候,就可以省略属性名,直接写属性值)

  • java.lang.annotation.ElementType:是一个枚举,枚举中的变量都是常量,可以通过枚举名.变量名直接使用

    ElementType枚举中的常量:

    TYPE,		// 类,接口
    FIELD,		// 成员变量
    METHOD, 	// 成员方法
    PARAMETER, 			// 方法参数
    CONSTRUCTOR, 		// 构造方法
    LOCAL_VARIABLE		// 局部变量
    

2.java.lang.annotation.Retention

作用:用来标识注解的生命周期(有效范围)

属性:

  • RetentionPolicy value: 只有一个属性,属性名叫value(使用的时候,就可以省略属性名,直接写属性值)

  • java.lang.annotation.RetentionPolicy:是一个枚举,枚举中的变量都是常量,可以通过枚举名.变量名直接使用

    RetentionPolicy枚举中的常量:

    SOURCE,  	// 注解只作用在源码(.java)阶段,生成的字节码文件(.class)中不存在
    CLASS,		// 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
    RUNTIME		// 注解作用在源码阶段,字节码文件阶段,运行阶段
    

示例

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

//声明自定义注解Book可以使用的位置:类上|接口上,构造方法上,成员变量上
//@Target(value={ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.FIELD})
//声明自定义注解Book:在.java文件中,在.class文件中和在内存中都有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    //书名
    public abstract String value();
    //价格,默认值为 100
    public abstract double price() default 100;
    //多位作者
    public abstract String[] authors();
}

注解解析

作用:获取注解的属性值

原理:注解的解析底层使用的反射技术

java.lang.reflect.AnnotatedElement接口:在接口中定义了注解解析的方法

AnnotatedElement接口的实现类都重写了接口中的方法,都可以使用这些方法

​ 实现类:AccessibleObject,Class,Constructor,Field,Method,Package

AnnotatedElement接口中的常用方法:

boolean isAnnotationPresent(Class<?> annotationClass)	// 判断指定的对象(Class,Method...)上,是否包含指定的注解
/* 参数:
		Class<?> annotationClass:判断哪个注解,就传递哪个注解的class文件对象
								  判断类上,方法上有没有Book注解,就需要传递Book.class
   返回值:boolean
		 有指定的注解,返回true
		 没有指定的注解,返回false
*/
    
T getAnnotation(Class<T> annotationClass)	// 获取对象(Class,Method...)上指定的注解
/* 参数:
		Class<T> annotationClass:获取哪个注解,就传递哪个注解的class文件对象
								  获取类上,方法上的Book注解,就需要传递Book.class
   返回值:
		T:返回获取到的注解,获取的注解不存在,返回null
*/
    
Annotation[] getAnnotations() 			// 返回此元素上存在的所有公共注释。
Annotation[] getDeclaredAnnotations() 	// 返回直接存在于此元素上的所有注释,包含其他修饰符的注解

示例

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;

@Book(value = "西游记",authors = {"吴承恩"})
public class Demo01ParseAnnotation {
    @Book(value = "水浒传",authors = {"施耐庵","林冲"},price = 88.8)
    public void method(){}

    /* 解析类上的注解:获取类上注解的属性值 */
    @Test
    public void show01() throws ClassNotFoundException {
        //1.获取当前类Demo01ParseAnnotation的class文件对象
        Class clazz = Class.forName("com.itheima.demo09Annotation.Demo01ParseAnnotation");
        //2.使用class文件对象中的方法isAnnotationPresent判断类上是否包含指定的Book注解
        boolean b = clazz.isAnnotationPresent(Book.class);
        System.out.println(b);//true
        if(b){
            //3.如果类上包含Book注解,使用class文件对象中的方法getAnnotation获取Book注解
            Book book = (Book)clazz.getAnnotation(Book.class);
            //4.使用注解名.属性名(),获取属性值
            String value = book.value();
            System.out.println(value);
            double price = book.price();
            System.out.println(price);
            String[] authors = book.authors();
            System.out.println(Arrays.toString(authors));
        }
    }

    /* 解析方法上的注解:获取方法上注解的属性值 */
    @Test
    public void show02(){
        //1.获取当前类Demo01ParseAnnotation的class文件对象
        Class clazz = Demo01ParseAnnotation.class;
        //2.使用class文件对象中的方法getMethods获取类中所有的方法,返回一个Method数组
        Method[] methods = clazz.getMethods();
        //3.遍历Method数组,获取每一个Method对象
        for (Method method : methods) {
            //4.使用Method对象中的方法isAnnotationPresent判断方法上是否包含指定的Book注解
            boolean b = method.isAnnotationPresent(Book.class);
            System.out.println(method.getName()+"-->"+b);
            if(b){
                //5.如果方法上有Book注解,使用Method对象中的方法getAnnotation,获取Book注解
                Book book = method.getAnnotation(Book.class);
                //6.使用注解名.属性名(),获取属性的值
                System.out.println(book.value());
                System.out.println(book.price());
                System.out.println(Arrays.toString(book.authors()));
            }
        }
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    //书名
    public abstract String value();
    //价格,默认值为 100
    public abstract double price() default 100;
    //多位作者
    public abstract String[] authors();
}

注解和反射的综合案例

import java.lang.reflect.Method;

/*
    注解和反射的综合案例
    需求:
        模拟Junit单元测试的@Test注解:作用可以单独的运行某一个方法
        方法上添加了@Test注解,可以运行
        方法上没有添加@Test注解,不可以运行
 */
public class Demo01Test {
    public static void main(String[] args) throws Exception {
        //3.获取测试类的class文件对象
        Class clazz = Class.forName("com.itheima.demo10test.DemoMyTest");
        //4.使用class文件对象中的方法newInstance实例化对象
        Object obj = clazz.newInstance();
        //5.使用class文件对象中的方法getMethods,获取类中所有的成员方法,返回一个Method数组
        Method[] methods = clazz.getDeclaredMethods();//只会获取本类的方法,包含任意修饰符的,没有父类的方法
        //6.遍历数组,获取每一个Method对象
        for (Method method : methods) {
            //7.使用Method对象中的方法isAnnotationPresent判断Method对象上是否有MyTest注解
            boolean b = method.isAnnotationPresent(MyTest.class);
            //8.如果Method对象上有MyTest注解,使用invoke运行方法
            if(b){
                method.invoke(obj);
            }
        }
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//1.定义一个注解叫MyTest,使用元注解修饰(a.只能在方法上使用,b.运行时有效)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
//2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解修饰
public class DemoMyTest {
    @MyTest
    public void show01(){
        System.out.println("show01方法");
    }
    public void show02(){
        System.out.println("show02方法");
    }
    public void show03(){
        System.out.println("show03方法");
    }
    @MyTest
    public void show04(){
        System.out.println("show04方法");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值