反射&注解

本文详细探讨了Java类的加载机制、类加载器的层次结构,以及如何使用反射获取和操作类的构造方法、成员变量与方法。同时,介绍了注解的概念、使用方法和元注解,展示了如何结合注解和反射进行动态配置与功能实现。
摘要由CSDN通过智能技术生成

反射

类加载器

在这里插入图片描述

类的初始化

  1. 创建类的实例
  2. 类的静态变量,或者为静态变量赋值
  3. 类的静态方法
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  5. 初始化某个类的子类
  6. 直接使用java.exe命令来运行某个主类
/*
    类的初始化(类什么时候进入到内存中)
 */
public class Demo01 {
    public static void main(String[] args) {
        //1.使用java.exe运行某个类的时候
        //2.创建对象
        new Person();
        //3.访问静态成员变量,给静态成员变量赋值
        System.out.println(Person.a);
        //4.访问静态成员方法
        Person.method();
        //5.创建类的子类对象的时候
        new Student();
        //6.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    }
}

public class Person {

    public Person() {
        System.out.println("Person类的构造方法");
    }

    public static int a = 10;
    public static void method(){
        System.out.println("静态方法!");
    }
}

public class Student extends Person {
    public Student() {
        super();
    }
}

类加载器概念

类加载器:负责加载类的对象。将class文件(硬盘)加载到内存中生成class对象。

类加载器的组成

  • BootstrapClassLoader 根类加载器

    ​ 也被称为引导类加载器,负责Java核心类的加载

    ​ 比如System,String等。

  • ExtClassLoader 扩展类加载器

    ​ 负责JRE的扩展目录中jar包的加载。

    ​ 在JDK中JRE的lib目录下ext目录

  • AppClassLoader 系统类加载器

    ​ 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
    在这里插入图片描述

  • 使用 类.class.getClassLoader() 获得加载自己的类加载器

  • 类加载器加载机制:全盘负责委托机制

    ​ 全盘负责:A类如果要使用B类(不存在),A类加载器必须负责加载B类。
    在这里插入图片描述

    • ​ 委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。
      ​ 如果加载,将直接使用。
      ​ 如果没有机制,自己再加载。
  • 采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象。
    类加载器的获取

/*
    ClassLoader:类加载器,负责把类加载到内存中,并为类生成一个class文件对象
    获取方式:
        可以使用Class类中的方法getClassLoader();
            ClassLoader getClassLoader() 返回该类的类加载器。
    ClassLoader类中的方法:
            ClassLoader getParent() 返回委托的父类加载器。
    AppClassLoader extends ExtClassLoader  extends BootstrapClassLoader extends  ClassLoader
 */
public class Demo {
    public static void main(String[] args) {
        show03();
    }

    /*
        直接获取根类加载器
     */
    private static void show03() {
        ClassLoader cl = String.class.getClassLoader();
        System.out.println(cl);//null
    }

    /*
        直接获取扩展类加载器
     */
    private static void show02() {
        ClassLoader cl = SunEC.class.getClassLoader();
        System.out.println(cl);//sun.misc.Launcher$ExtClassLoader@7ea987ac
    }



    //类名.class属性获取class文件对象
    private static void show01() {
        ClassLoader c1 = Demo.class.getClassLoader();
        System.out.println(c1);//sun.misc.Launcher$AppClassLoader@18b4aac2

        ClassLoader c2 = c1.getParent();
        System.out.println(c2);//sun.misc.Launcher$ExtClassLoader@4554617c

        ClassLoader c3 = c2.getParent();
        System.out.println(c3);//null  根类加载器,不是类(C)
    }
}

获取class文件对象的方式(重点)

/*
    获取class文件对象的方式(重点)
    class文件对象是由类加载器创建的,我们无权创建和销毁,class文件对象是唯一的,无论我们使用那种方式获取都是同一个
    1.使用Object类中的方法getClass
        Class<?> getClass() 返回此 Object 的运行时类。
    2.java会为每种数据类型都赋予一个class属性,这个class属性返回的就是class文件对象
        基本数据类型:int.class,double.class,char.class,boolean.class...
        引用数据类型:int[].class,ArrayList<String>.class,String.class,Person.class
    3.可以使用Class类中的静态方法forName
        static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象
        参数:
            String className:全类名(包名+类名) 可以确定类的唯一性
            com.itheima.demo03Reflect.Person
 */
public class Demo01Reflect {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.使用Object类中的方法getClass:Person类默认继承了Object类,所以可以使用Object类中的getClass方法
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);//class com.llz.demo03Reflect.Person

        //2.java会为每种数据类型都赋予一个class属性,这个class属性返回的就是class文件对象
        System.out.println(int.class);
        System.out.println(double.class);
        System.out.println(String.class);
        Class c2 = Person.class;
        System.out.println(c2);//class com.llz.demo03Reflect.Person

        //3.可以使用Class类中的静态方法forName
        Class c3 = Class.forName("com.llz.demo03Reflect.Person");
        System.out.println(c3);//class com.llz.demo03Reflect.Person

        //class文件对象是由类加载器创建的,我们无权创建和销毁,class文件对象是唯一的,无论我们使用那种方式获取都是同一个
        System.out.println(c1==c2);//true 引用数据类型==比较的是对象的地址值
        System.out.println(c1==c3);//true
        System.out.println(c2==c3);//true
    }
}

Class类常用方法(了解)

/*
    Class类常用方法(了解)
        String getSimpleName() 获取类名
        String getName()  获取全类名(包名+类名)
    -------------------------------------
    使用Class类中的forName方法,获取class文件对象,会执行类中的静态代码块(数据库:JDBC)
 */
public class Demo03ClassMethod {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取Class文件对象
        Class clazz = Class.forName("com.llz.demo03Reflect.Person");

        String simpleName = clazz.getSimpleName();
        System.out.println(simpleName);//Person

        String name = clazz.getName();
        System.out.println(name);//com.llz.demo03Reflect.Person
    }
}

反射概述

在这里插入图片描述

反射准备工作

public class Person {
    /*static {
        System.out.println("Person类的静态代码块!");
    }*/
    private String name;
    private int age;
    public String sex;

    public Person() {
        System.out.println("Person类的空参数构造方法!");
    }

    public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        System.out.println("Person类的满参数构造方法!");
    }

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person类的私有参数构造方法!");
    }
    
    private void method(){
        System.out.println("Person类的私有method方法!");
    }

    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;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

使用反射技术获取类中的构造方法,并使用获取到的构造方法实例化对象

/*
    使用反射技术获取类中的构造方法,并使用获取到的构造方法实例化对象
    实现步骤:
        1.获取Person类的class文件对象
        2.使用class文件对象中的方法getConstructor|getConstructors或者类中的(公共)构造方法
        3.使用构造方法Constructor类中的方法newInstance实例化(创建)对象
 */
public class Demo04Constructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取Person类的class文件对象
        Class clazz = Class.forName("com.llz.demo03Reflect.Person");
        //2.使用class文件对象中的方法getConstructor|getConstructors或者类中的(公共)构造方法
        /*
            Class类中的方法:获取多个构造方法
                Constructor<?>[] getConstructors()  获取类的所有公共构造方法
                Constructor<?>[] getDeclaredConstructors()  获取类声明的所有构造方法(包含公共的,受保护的,默认的,私有的)
         */
        Constructor[] cons = clazz.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }
        System.out.println("------------------------");
        Constructor[] declaredCons = clazz.getDeclaredConstructors();
        for (Constructor con : declaredCons) {
            System.out.println(con);
        }
        System.out.println("------------------------");
        /*
            Class类中的方法:获取指定的构造方法
                Constructor<T> getConstructor(Class<?>... parameterTypes)  获取类的指定公共构造方法
                Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)  获取类的指定构造方法(包含公共的,受保护的,默认的,私有的)。
                参数:
                    ...:可变参数,调用的方法参数是可变参数,可以传递任意个参数(不传,1,多个...)
                    Class<?>... parameterTypes:传递构造方法参数的class文件对象
                        (String.class,int.class,double.class,...)
           注意:
                类中没有指定的构造方法,会抛出NoSuchMethodException:没哟这个方法异常
         */
        //public Person()
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);//public com.llz.demo03Reflect.Person()

        //public Person(String name, int age, String sex)
        Constructor con2 = clazz.getConstructor(String.class, int.class, String.class);
        System.out.println(con2);//public com.llz.demo03Reflect.Person(java.lang.String,int,java.lang.String)

        //private Person(String name, int age)
        Constructor con3 = clazz.getDeclaredConstructor(String.class, int.class);
        System.out.println(con3);//private com.llz.demo03Reflect.Person(java.lang.String,int)
        System.out.println("----------------------------");
        //3.使用构造方法Constructor类中的方法newInstance实例化(创建)对象
        /*
            java.lang.reflect.Constructor<T>类
                 T newInstance(Object... initargs)  实例化对象的方法(创建对象)
                 参数:
                    Object... initargs:传递创建对象的实际参数  new Person("张三",18,"女");
                 返回值:
                    T:返回的及时创建好的对,类型是Object类型
                        Object obj = new Person();
         */
        //public Person()
        Object obj1 = con1.newInstance();//此方法相当于 new Person();
        System.out.println(obj1);//com.llz.demo03Reflect.Person@4554617c  Person{name='null', age=0, sex='null'}

        //public Person(String name, int age, String sex)
        Object obj2 = con2.newInstance("张三", 18, "女");//此方法相当于 new Person("张三",18,"女");
        System.out.println(obj2);//Person{name='张三', age=18, sex='女'}

        /*
            private Person(String name, int age)
            私有的方法是没有权限使用的,要使用私有构造方法会抛出IllegalAccessException:非法访问异常
            解决:
                可以使用Constructor的父类AccessibleObject类中的方法解决
                java.lang.reflect.AccessibleObject类
                    void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。
                        值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                        值为 false 则指示反射的对象应该实施 Java 语言访问检查。
            注意:
                暴力反射不推荐,破坏了类的封装性(私有,不让外界使用)
         */
        con3.setAccessible(true);//取消java语言访问检查==>暴力反射
        Object obj3 = con3.newInstance("李四",20);
        System.out.println(obj3);//Person{name='李四', age=20, sex='null'}
    }
}

使用反射技术获取构造方法并实例化对象的简化方式(重点)

/*
    使用反射技术获取构造方法并实例化对象的简化方式(重点)
    java.lang.Class类
        T newInstance() 创建此 Class 对象所表示的类的一个新实例。
    实现步骤:
        1.获取Person类class文件对象
        2.使用class文件对象中的方法newInstance实例好对象
    注意:
        1.类中必须有空参数构造方法
        2.空参数构造方法修饰符是public,不能是private
 */
public class Demo05Constructor {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //1.获取Person类class文件对象
        Class clazz = Class.forName("com.llz.demo03Reflect.Person");
        //2.使用class文件对象中的方法newInstance实例好对象
        Object obj = clazz.newInstance();//多态
        System.out.println(obj);//Person{name='null', age=0, sex='null'}
        Person p = (Person)obj;
        System.out.println(p.getName()+"\t"+p.getAge());
    }
}

使用反射技术获取类中的成员方法,并运行(重点)

/*
    使用反射技术获取类中的成员方法,并运行获取到成员方法(重点)
    实现步骤:
        1.获取Person类的class文件对象
        2.使用class文件对象中的方法getMethod|getMethods获取类中(公共)的成员方法
        3.使用成员方法Method类中的方法invoke,运行获取到的成员方法
 */
public class Demo06Method {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //1.获取Person类的class文件对象
        Class clazz = Class.forName("com.llz.demo03Reflect.Person");

        //2.使用class文件对象中的方法getMethod|getMethods获取类中(公共)的成员方法
        /*
            Class类中的方法:获取多个成员方法
                Method[] getMethods()  获取本类和父类|接口继承的,所有公共成员方法
                Method[] getDeclaredMethods()  获取声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
            Method类中有一个方法getName,获取方法的名称
                String getName() 以 String 形式返回此 Method 对象表示的方法名称。
         */
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("------------------------");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method.getName());
        }
        System.out.println("-------------------------");
        /*
            Class类中的方法:获取指定的成员方法
                 Method getMethod(String name, Class<?>... parameterTypes) 获取指定公共成员方法。
                 Method getDeclaredMethod(String name, Class<?>... parameterTypes)  指定已声明方法(包括公共、保护、默认(包)访问和私有方法)。
                 参数:
                    String name:方法的名称
                    Class<?>... parameterTypes:方法参数的class文件对象
            注意:
                获取指定的成员方法不存在,会抛出NoSuchMethodException:没有这个方法异常
         */
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);//public java.lang.String com.llz.demo03Reflect.Person.getName()

        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);
        System.out.println(setNameMethod);

        //private void method()
        Method privateMethod = clazz.getDeclaredMethod("method");
        System.out.println(privateMethod);

        //3.使用成员方法Method类中的方法invoke,运行获取到的成员方法
        /*
            java.lang.reflect.Method类
                Object invoke(Object obj, Object... args)  运行成员方法
                参数:
                    Object obj:方法的运行需要对象的支持,运行哪个类的方法,就需要传递哪个类的对象
                        运行Person类中的方法,就需要传递Person对象(快速的方式获取)
                    Object... args:运行方法传递的实际参数
                返回值:
                    Object:方法的返回值
                        方法的返回值类型不是void,返回的就是方法的返回值
                        方法的返回值类型是void,返回null(没有返回值)
         */
        Object obj = clazz.newInstance();
        //public String getName()
        Object v1 = getNameMethod.invoke(obj);//就相当于使用getName方法获取name的值
        System.out.println("v1:"+v1);//v1:null name的默认值

        //public void setName(String name)
        Object v2 = setNameMethod.invoke(obj, "柳岩");//就相当于使用setName方法给name赋值
        System.out.println("v2:"+v2);//v2:null 方法没有返回值

        v1 = getNameMethod.invoke(obj);//就相当于使用getName方法获取name的值
        System.out.println("v1:"+v1);//v1:柳岩

        /*
            private void method()
            私有方法没有权限运行,会抛出非法访问异常:IllegalAccessException
            使用暴力反射解决:使用Method的父类AccessibleObject中的方法setAccessible取消java的语言访问检查
         */
        privateMethod.setAccessible(true);
        privateMethod.invoke(obj);//Person类的私有method方法!
    }
}

使用反射技术获取实现类所实现的所有接口(了解)

/*
    使用反射技术获取实现类所实现的所有接口:学习动态代理的时候使用
    class类中的方法:
        Class<?>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。
 */
public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取接口实现类AandBImpl的class文件对象
        Class clazz = Class.forName("com.llz.demo04Reflect.AandBImpl");
        //使用反射技术获取实现类所实现的所有接口
        Class[] interfaces = clazz.getInterfaces();
        for (Class in : interfaces) {
            /*
                interface com.llz.demo04Reflect.A
                interface com.llz.demo04Reflect.B
             */
            System.out.println(in);
        }
    }
}

注解

注解概述

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

注解的作用:

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

​ 2.编译检查@Override @FunctionalInterface

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

自定义注解(重点)

定义没有属性的注解
/*
    自定义注解:定义一个没有属性的注解
    格式:
       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;
}

定义只有一个属性的注解,属性名叫value

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

定义枚举(了解)

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

使用自定义注解(重点)

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

    @MyAnnotation01
    @MyAnnotation03(value = 10)
    public UseMyAnnotation(String name) {
        this.name = name;
    }

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

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

例子——Book注解

/*
    练习:
        定义一个注解:Book
        包含属性:String value() 书名
        包含属性:double price() 价格,默认值为 100
        包含属性:String[] authors() 多位作者
        分别在类上,方法上,变量上使用Book注解
 */
public @interface Book {
    //书名
    String value();
    //价格,默认值为 100
    double price() default 100;
    //多位作者
    String[] authors();
}


@Book(value = "java编程思想",authors = "老外")
public class UseBook {
    @Book(value = "红楼梦",price = 88.8,authors ={"曹雪芹","吴承恩"} )
    private String name;

    @Book(value = "水浒传",authors = "施耐庵")
    public String getName() {
        return name;
    }

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

元注解(重点)

/*
   元注解: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(value = {ElementType.METHOD,ElementType.FIELD})
@Target({ElementType.METHOD,ElementType.FIELD})
//声明自定义注解的声明周期:在.java文件中,.class文件中和内存中都有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    //书名
    String value();
    //价格,默认值为 100
    double price() default 100;
    //多位作者
    String[] authors();
}

注解解析(使用)

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

    /*
        解析类上的注解
        实现步骤:
            1.获取当前类Demo01ParseAnnotaion的class文件对象
            2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
            3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取Book注解
            4.使用注解名.属性名(),获取属性的值
     */
    @Test
    public void show01(){
        //1.获取当前类Demo01ParseAnnotaion的class文件对象
        Class clazz = Demo01ParseAnnotaion.class;
        //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 show02() throws ClassNotFoundException {
        //1.获取当前类的class文件对象
        Class clazz = Class.forName("com.llz.demo08Annotaion.Demo01ParseAnnotaion");
        //2.使用class文件对象中的方法getMethods获取类中所有的成员方法,返回一个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()));
            }
        }
    }
}

注解和反射的综合案例(重点)

//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方法");
    }

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

    @MyTest
    public void show04(){
        System.out.println("show04方法");
    }
}
/*
    注解和反射综合练习题
    需求:
        模拟Junit测试的@Test
        添加了@Test注解的方法,可以运行
        没有添加@Test注解的方法,不可以运行
    分析:
        1.定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
        2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解修饰
        3.获取测试类的class文件对象
        4.使用class文件对象中的方法newInstance实例化对象
        5.使用class文件对象中的方法getDeclaredMethods,获取类中所有的成员方法,返回一个Method数组
        6.遍历数组,获取每一个Method对象
        7.使用Method对象中的方法isAnnotationPresent判断Method对象上是否有指定的MyTest注解
        8.如果Method对象上有MyTest注解,使用invoke运行方法
 */
public class Demo01Test {
    public static void main(String[] args) throws Exception {
        //3.获取测试类的class文件对象
        Class clazz = Class.forName("com.llz.demo09Test.DemoMyTest");
        //4.使用class文件对象中的方法newInstance实例化对象
        Object obj = clazz.newInstance();
        //5.使用class文件对象中的方法getDeclaredMethods,获取类中所有的成员方法,返回一个Method数组
        Method[] methods = clazz.getDeclaredMethods();
        //6.遍历数组,获取每一个Method对象
        for (Method method : methods) {
            //7.使用Method对象中的方法isAnnotationPresent判断Method对象上是否有指定的MyTest注解
            if(method.isAnnotationPresent(MyTest.class)){
                //8.如果Method对象上有MyTest注解,使用invoke运行方法
                method.invoke(obj);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值