java-注解-自定义注解-注解解析

注解

1.注解概述

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

注解的作用:

​ 1.使用javadoc生成帮助文档:里边可以包含注解**@author和@version**

​ 2.编译检查@Override @FunctionalInterface

​ 3.框架的配置(框架=代码+配置):框架的时候讲

2.自定义注解(重点)



/*
    自定义注解:定义一个没有属性的注解
    格式:
        public @interface 注解名{
        }
    注意:
        注解使用的也是.java文件,编译生成的也是.class文件
        注解和类和接口和枚举都同一个层次
 */
public @interface MyAnnotation01 {
}


/*
    自定义注解:定义含有属性的注解
        注解的属性,可以看成是抽象方法,但是有默认值
    定义格式:
        public @interface 注解名{
            修饰符 数据类型 属性名();
            修饰符 数据类型 属性名() default 默认值;
        }
        注意:
            1.属性的修饰符固定使用public abstract,可以省略不写,不写默认也是;建议写出,可以增强语句的可读性
            2.属性的数据类型:
                a.基本数据类型(4类8种):byte,short,int,long,float,double,char,boolean
                b.引用类型:String类型,反射类型(Class),注解类型,枚举类型
                c.以及以上所有的类型的一维数组
 */
public @interface MyAnnotation02 {
    //定义一个int类型的属性
    public abstract int a();

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

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

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


/*
    定义枚举
        枚举中的成员变量都是常量
 */
public enum Color {
    /*
        public static final Color RED = new Color();
        public static final Color GREEN = new Color();
     */
    RED,GREEN
}

3.使用自定义注解(重点)



/*
    使用自定义注解
    注解可以使用的位置:
        包上,类上|接口上,成员变量上,成员方法上,构造方法上,局部变量上,方法的参数上...
    注意:
        同一个位置,同名的注解只能使用一次
        不同的位置,同名的注解可以使用多次
    注解的使用格式:
        1.没有属性的注解,通过@注解名可以直接使用
        2.有属性的注解必须使用键值对的方式,给注解中的所有属性赋值之后,才能使用
        格式:
            @注解名(属性名=属性值,属性名=属性值,..属性名=属性值)
            a.有默认值的属性,可以不用赋值,使用默认值
            b.给多个属性赋值中间要使用逗号分隔开
            c.属性是数组类型,属性值需要使用{ }包裹起来;数组只有一个值,可以省略{ }
                arr = {"a","b","c"}  arr={"a"}==> arr="a"
            d.注解中只有一个属性,属性的名叫value,那么赋值的时候可以省略属性名,直接写属性值
              或者注解有其他属性,但是必须有默认值
                value=10==> 10
 */
@MyAnnotation01
@MyAnnotation02(a=10,d=1.1,arr={"a","b","c"})
public class UseMyAnnotation {

    @MyAnnotation01
    @MyAnnotation02(a = 100,arr="aa")
    @MyAnnotation03(aaa=10,value = 20)
    private String name;
    private int age;

    @MyAnnotation01
    @MyAnnotation03(10)
    public UseMyAnnotation() {
    }

    public UseMyAnnotation(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


//定义只有一个属性的注解,属性名叫value
//也可以有其他的属性,但是属性必须有默认值
public @interface MyAnnotation03 {
    public abstract int aaa() default 100;
    public abstract int value();
}

4.注解练习_定义和使用Book注解



/*
    定义一个注解:Book
        - 包含属性:String value()   书名
        - 包含属性:double price()  价格,默认值为 100
        - 包含属性:String[] authors() 多位作者
 */
public @interface Book {
    //书名
    public abstract String value();
    //价格,默认值为 100
    public abstract double price() default 100;
    //多位作者
    public abstract String[] authors();
}


/*
    使用自定义注解Book
 */

@Book(value = "钢铁是怎样炼成的",authors = "奥斯托洛夫斯基")
public class UseBook {
    @Book(value = "java编程思想",price = 88.8,authors = {"aaa","bbb"})
    public String name;
}

5.元注解



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

/*
    元注解:java已经定义好的注解,可以用来修饰自定义的注解
    1.@Target
        作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
        属性:
            ElementType[] value:只有一个属性名字叫value,使用的时候,给属性赋值直接写属性值即可
            java.lang.annotation.ElementType:是一个枚举,枚举中定义的变量都是常量
                TYPE,类,接口
                FIELD, 成员变量
                METHOD, 成员方法
                PARAMETER, 方法参数
                CONSTRUCTOR, 构造方法
                LOCAL_VARIABLE, 局部变量
      2.@Retention
            作用:用来标识注解的生命周期(有效范围)
            属性:
                RetentionPolicy value:只有一个属性名字叫value,使用的时候,给属性赋值直接写属性值即可
                java.lang.annotation.RetentionPolicy:是一个枚举,枚举中定义的变量都是常量
                     SOURCE:注解只作用在源码阶段(.java文件中),生成的字节码文件(.class文件中)中不存在
                     CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
                     RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
 */
@Target({ElementType.TYPE,ElementType.METHOD})//声明自定义注解可以使用在类上和方法上
@Retention(RetentionPolicy.RUNTIME)//声明自定义注解生命周期在.java文件中.class文件中和内存中都有效
public @interface Book {
    //书名
    public abstract String value();
    //价格,默认值为 100
    public abstract double price() default 100;
    //多位作者
    public abstract String[] authors();
}

6.注解解析



import com.zl.demo03Annotation.MyAnnotation01;
import org.junit.Test;

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

/*
    注解解析:
        获取注解的属性值
    注解解析底层使用的都是反射技术
    java.lang.reflect.AnnotatedElement接口:在接口中定义了注解解析相关的方法
    接口的实现类:实现类都重写了接口中的方法,都可以使用
        AccessibleObject, Class, Constructor, Field, Method, Package
    接口中的方法:
        boolean isAnnotationPresent(Class annotationClass):判断指定的对象上(Class, Constructor, Field, Method)是否有指定的注解
            参数:
                Class annotationClass:判断哪个注解,就传递哪个注解的class文件对象
                    判断类上,方法上...有没有Book注解,就需要传递Book.class
            返回值:
                有指定的注解,返回true
                没有指定的注解,返回false
        T getAnnotation(Class annotationClass) 获取对象上(Class, Constructor, Field, Method)指定的注解
            参数:
                Class annotationClass:获取哪个注解,就传递哪个注解的class文件对象
                    获取类上,方法上...有没有Book注解,就需要传递Book.class
            返回值:
                T:返回指定的注解,获取不到返回null
        了解:
            Annotation[] getAnnotations()  获取对象上所有公共的注解
            Annotation[] getDeclaredAnnotations() 获取对象上所有声明的注解
 */
@MyAnnotation01
@Book(value = "西游记",price = 66.6,authors = "吴承恩")
public class Demo01ParseAnnotation {

    @Book(value = "水浒传",authors = "施耐庵")
    public void method(){}

    /*
        解析类上的注解
        实现步骤:
            1.获取类的class文件对象
            2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
            3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取到Book注解
            4.使用注解名.属性名()获取注解的属性值
     */
    @Test
    public void parseClassAnnotation() throws ClassNotFoundException {
        //1.获取类的class文件对象
        Class clazz = Class.forName("com.zl.demo05Annotation.Demo01ParseAnnotation");
        //2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
        boolean b = clazz.isAnnotationPresent(Book.class);
        System.out.println(b);
        //3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取到Book注解
        if(b){
            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));
        }
    }

    /*
        解析方法上的注解
        实现步骤:
            1.获取类的class文件对象
            2.使用class文件对象中的方法getMethods获取类中所有的公共成员方法,返回一个Method数组
            3.遍历Method数组,获取每一个Method对象
            4.使用Method对象中的方法isAnnotationPresent判断方法上是否有Book注解
            5.如果方法上有Book注解,使用Method对象中的方法getAnnotation获取Book注解
            6.使用注解名.属性名()获取属性的值
     */
    @Test
    public void parseMethodAnnotation(){
        //1.获取类的class文件对象
        Class clazz = Demo01ParseAnnotation.class;
        //2.使用class文件对象中的方法getDeclaredMethods获取类中所有的成员方法,返回一个Method数组
        Method[] methods = clazz.getDeclaredMethods();
        //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()));
            }
        }
    }
}

7.注解和反射的综合演示



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

//1.自定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}


//2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解
public class DemoMyTest {
    public void show01(){
        System.out.println("show01方法");
    }

    @MyTest
    public void show02(){
        System.out.println("show02方法");
    }

    @MyTest
    public void show03(){
        System.out.println("show03方法");
    }

    public void show04(){
        System.out.println("show04方法");
    }

    @MyTest
    public void show05(){
        System.out.println("show05方法");
    }
}


import java.lang.reflect.Method;

/*
    注解和反射的综合案例
    需求:
        模拟Junit测试的@Test
        添加了@Test注解的方法,可以运行
        没有添加@Test注解的方法,不可以运行
    分析:
        1.自定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
        2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解
        3.获取测试类的class文件对象
        4.使用class文件对象中的方法newInstance实例化对象
        5.使用class文件对象中的方法getMethods获取测试类中所有的成员方法,返回一个Method数组
        6.遍历Method数组,获取每一个Method对象
        7.使用Method对象中的方法isAnnotationPresent判断,Method上是否有指定的MyTest注解
        8.如果Method对象上有MyTest注解,使用invoke方法,运行Method
 */
public class Demo01AnnotationTest {
    public static void main(String[] args) throws Exception {
        //3.获取测试类的class文件对象
        Class clazz = Class.forName("com.zl.demo06AnnotationTest.DemoMyTest");
        //4.使用class文件对象中的方法newInstance实例化对象
        Object obj = clazz.newInstance();
        //5.使用class文件对象中的方法getMethods获取测试类中所有的成员方法,返回一个Method数组
        Method[] methods = clazz.getDeclaredMethods();
        //6.遍历Method数组,获取每一个Method对象
        for (Method method : methods) {
            //7.使用Method对象中的方法isAnnotationPresent判断,Method上是否有指定的MyTest注解
            if(method.isAnnotationPresent(MyTest.class)){
                //8.如果Method对象上有MyTest注解,使用invoke方法,运行Method
                method.invoke(obj);
            }
        }
    }
}



你可能感兴趣的:(JAVA,java)