• Java反射、注解、枚举


    1.反射

    一、什么是反射?

    Java反射是Java被视为动态语言的很重要的一个特性。
    Java通过反射的Api可以做到获取运行时类的任何信息。包括其类、属性、方法、注解、父类等任何信息。并且可以直接操作任意对象的内部属性及方法。

    二、反射能用来做什么?

    1. 在运行时判断任意一个对象所属的类。
    2. 在运行时构造任意一个类的对象。
    3. 在运行时判断任意一个类所具有的成员变量和方法。
    4. 在运行时获取泛型信息。
    5. 在运行时调用任意一个对象成员变量和方法。
    6. 在运行时处理注解。
    7. 生成动态代理。

    三、获取class实例的方式。

    获取Class实例的方式

    • 方式一:调用运行时类的属性。 .class Class clazz1 = Person.class;
    • 方式二:通过运行时类的对象 调用getClass这个方法 Class aClass = person.getClass();
    • 方式三:调用Class的静态方法 Class.forName(String classPath)
    • 方式四:使用类的加载器:ClassLoader

    Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类。

    四、通过反射创建运行时类的对象。

    newInstance():调用此方法创建对应的运行时类的对象。内部调用了运行时类的空参构造器。

    要想此方法能够正常的创建运行时类的对象,要求:

    1. 运行时类必须提供空参构造器。
    2. 空参构造器的访问权限得够(通常设置为public的)

    五、类加载器

    系统类加载器:加载自己创建的一些类。
    扩展类加载器:加载Java的lib下得扩展类。
    引导类加载器:主要负责加载java的核心类库,无法加载自定义类的。

    六、随机创建运行时类对象,体验动态性

    	@Test
        public void testNewInstance1() throws Exception {
    
            int num = new Random().nextInt(3);
            String classPath = "";
    
            switch (num) {
                case 0: classPath = "java.util.Date"; break;
                case 1: classPath = "java.lang.Object"; break;
                case 2: classPath = "com.starcpdk.Person"; break;
            }
    
            Object obj = getInstance(classPath);
            System.out.println(obj);
    
    
        }
    
        /**
         * 创建一个指定类的对象
         * @param classPath 指定类的全类名
         * @return
         * @throws Exception
         */
        public Object getInstance(String classPath) throws Exception {
            Class<?> clazz = Class.forName(classPath);
            return clazz.newInstance();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    七、创建对象的几种方式

    1. new + 构造器
    2. 调用方法获取实例。XXX、XXXs、XXXFactory、XXXBuilder等。
    3. clone方法克隆对象。
    4. 反射方式创建对象。调用newInstance()使用类或者构造器调用。

    八、clazz获取一些结构。

    1. 获取构造器。
    // 获取当前运行时类中声明为public的构造器
    Constructor<?>[] constructors = personClass.getConstructors();
    // 获取当前运行时类的所有构造器
    Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
    
    • 1
    • 2
    • 3
    • 4
    1. 获取运行时类的父类
    // 获取运行时类的父类
    Class<? super Person> superclass = personClass.getSuperclass();
    
    // 获取运行时类的带泛型的父类
    Type genericSuperclass = personClass.getGenericSuperclass();
    
    // 获取运行时类的带泛型的父类
    Type genericSuperclass = personClass.getGenericSuperclass();
    // 获取父类的泛型类型
    ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    System.out.println(((Class)actualTypeArguments[0]).getName());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 获取运行时类实现的接口
    // 获取运行时类实现的接口
    Class<?>[] interfaces = personClass.getInterfaces();
    // 获取运行时类的父类实现的接口。
    Class<?>[] interfaces1 = personClass.getSuperclass().getInterfaces();
    
    • 1
    • 2
    • 3
    • 4
    1. 获取当前运行时类 所在的包
    Package aPackage = personClass.getPackage();
    
    • 1
    1. 获取运行时类 声明的注解
    Annotation[] annotations = personClass.getAnnotations();
    
    • 1
    1. 获取运行时类的方法
    // 获取当前运行时类及其所以父类中声明为public的所有方法
    Method[] methods = personClass.getMethods();
    // 获取当前运行时类中的所有方法。(不包含父类中声明的方法)
    Method[] declaredMethods = personClass.getDeclaredMethods();
    
    @Test
    public void test2() throws Exception {
        Class<Person> personClass = Person.class;
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
    
            // 4. 获取方法名
            System.out.print("方法名是:" + declaredMethod.getName() + ":\t");
    
            // 只能获取Runtime的生命周期的注解
            // 1. 获取方法声明的注解
            Annotation[] annotations = declaredMethod.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.print("注解是:" + annotation + "\t");
            }
    
            // 2、获取权限修饰符
            int modifiers = declaredMethod.getModifiers();
            System.out.print("权限修饰符是:" + Modifier.toString(modifiers) + "\t");
    
            // 3.获取返回值类型
            Class<?> returnType = declaredMethod.getReturnType();
            System.out.print("返回值类型是:" + returnType.getName() + "\t");
    
            // 5. 获取形参列表
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            if (!(parameterTypes == null || parameterTypes.length == 0)) {
                for (Class<?> parameterType : parameterTypes) {
                    System.out.print("形参类型:" + parameterType.getName() + " args_" + " ");
                }
            }
    
            // 6. 抛出的异常
            Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.print("异常:" + exceptionType.getName());
            }
    
            System.out.println();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    1. 获取运行时类的属性结构
    // getFields() :获取当前运行时类及其父类声明为public访问权限的属性
    Field[] fields = personClass.getFields();
    // getDeclaredFields():获取当前运行时类中所有的属性。(不包含父类的属性)
    Field[] declaredFields = personClass.getDeclaredFields();
    
    // 权限修饰符 数据类型  变量名
    @Test
    public void test1() {
    
        Class<Person> personClass = Person.class;
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            // 权限修饰符
            int modifiers = declaredField.getModifiers();
    //            System.out.println(modifiers);
            System.out.print(Modifier.toString(modifiers) + "\t");
    
            // 数据类型
            Class<?> type = declaredField.getType();
            System.out.print(type.getName() + "\t");
    
            // 变量名
            String name = declaredField.getName();
            System.out.println(name);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. 使用运行时类中的一些属性或方法
    package com.starcpdk.java2;
    
    /**
     * @Author 姚云峰
     * @Email
     * @Date 2022/10/27 14:02
     * @Version 1.0
     */
    
    import com.starcpdk.test.Person;
    import org.junit.jupiter.api.Test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 调用运行类中指定的结构:属性、方法、构造器
     */
    public class ReflectionTest {
    
        /**
         * 获取
         */
        @Test
        public void testField() throws Exception {
    
            // 获取指定的属性
            Class<Person> personClass = Person.class;
    
            // 创建运行时类的对象。
            Person person = personClass.newInstance();
    
            // 获取指定属性 , 要求运行时类中的属性声明为public的。
            // 通常不采用此方式
            Field id = personClass.getField("id");
    
            // 设置当前属性的值
            // 参数1:指明设置哪儿个对象的属性, 参数2:指明将此属性值设置为多少
            id.set(person , 1234);
    
            // 获取当前属性的值
            // 参数1:获取哪儿个对象的当前属性的值
            int pId = (int) id.get(person);
            System.out.println(pId);
        }
    
    
        /**
         * 操作运行时类中的属性。
         * @throws Exception
         */
        @Test
        public void testField1() throws Exception {
            Class<Person> personClass = Person.class;
    
            // 创建运行时类的对象。
            Person person = personClass.newInstance();
    
            // 获取指定变量名的属性
            Field age = personClass.getDeclaredField("age");
            // 保证当前属性是可访问的。
            age.setAccessible(true);
            // 获取、设置指定对象的此属性的值。
            age.set(person , 12);
    
            Object o = age.get(person);
            System.out.println(o);
        }
    
        /**
         * 操作运行时类中的方法
         * 参数1:指明获取方法的名称。
         * 参数2:指明获取方法的形参列表。
         */
        @Test
        public void testMethod() throws Exception {
            Class<Person> personClass = Person.class;
            Person person = personClass.newInstance();
    
            Method show = personClass.getDeclaredMethod("show", String.class);
            /**
             * invoke()
             * 参数1:方法的调用者。
             * 参数2:给方法形参赋值的实参。
             * invoke()的返回值就是对应类中调用的方法的返回值。
             */
            show.setAccessible(true);
            Object hahah = show.invoke(person, "哈哈哈哈");
            System.out.println(hahah);
    
    
            Method showDesc = personClass.getDeclaredMethod("showDesc");
            showDesc.setAccessible(true);
            // 如果调用的方法没有=返回值 , 则返回null
            Object invoke = showDesc.invoke(Person.class);
            System.out.println(invoke);
    
        }
    
        /**
         * 调用运行时类中指定的构造器。
         *
         * 参数:指明构造器的参数列表
         *
         * @throws Exception
         */
        @Test
        public void testConstructor() throws Exception {
            Class<Person> personClass = Person.class;
            Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
            declaredConstructor.setAccessible(true);
            Person tom = declaredConstructor.newInstance("Tom");
    
            System.out.println(tom);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    2. 枚举

    一、什么是枚举

    类中对象的个数是确定的,有限个。
    枚举类中的属性的修饰符是 private final 的。
    枚举类中的对象的修饰符是 public static final 的。

    二、枚举类常用方法

    • values(): 返回一个数组,即枚举类中的所有对象
    • valueOf(String str) 找指定名的对象 , 如果提供的str,在枚举类对象中没有,则抛异常。IllegalArgumentException
    • toString() 默认返回枚举类对象名称

    三、例子

    如何自定义枚举

    /**
     * @Author 姚云峰
     * @Email
     * @Date 2022/8/20 16:02
     * @Version 1.0
     * 

    *

    * 枚举类的使用 */ public class SeasonTest { } /** * 自定义枚举实现 */ class Season { private final String seasonName; private final String seasonDescription; private Season(String seasonName, String seasonDescription) { this.seasonName = seasonName; this.seasonDescription = seasonDescription; } public static final Season SPRING = new Season("春天" , "春暖花开"); public static final Season SUMMER = new Season("夏天" , "夏日炎炎"); public static final Season AUTUMN = new Season("秋天" , "秋高气爽"); public static final Season WINTER = new Season("冬天" , "冰天雪地"); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    /**
     * @Author 姚云峰
     * @Email
     * @Date 2022/8/20 16:16
     * @Version 1.0
     *
     * 使用enum关键字创建枚举类
     *
     * 常用方法:
     *  values(): 返回一个数组,即枚举类中的所有对象
     *  valueOf(String str)  找指定名的对象 , 如果提供的str,在枚举类对象中没有,则抛异常。IllegalArgumentException
     *  toString()   默认返回枚举类对象名称
     *
     *
     */
    public class SeasonTestEnum {
        public static void main(String[] args) {
            System.out.println(Season1.SPRING);
        }
    }
    
    interface Info{
        void show();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    enum枚举类。

    // 枚举类实现接口。
    // 可以统一重写接口方法
    // 也可以每个枚举值都重写接口方法
    enum Season1 implements Info{
    
    
        // 提供当前类枚举对象
        SPRING("春天","春暖花开"){
            @Override
            public void show() {
                System.out.println("春天在哪儿里");
            }
        },
        SUMMER("夏天","夏日炎炎") {
            @Override
            public void show() {
                System.out.println("夏宁");
            }
        },
        AUTUMN("秋天","秋高气爽") {
            @Override
            public void show() {
                System.out.println("秋天不回来");
            }
        },
        WINTER("冬天","冰天雪地") {
            @Override
            public void show() {
                System.out.println("大约在冬季");
            }
        };
    
        private final String seasonName;
        private final String seasonDescription;
    
        private Season1(String seasonName, String seasonDescription) {
            this.seasonName = seasonName;
            this.seasonDescription = seasonDescription;
        }
    
        // @Override
        // public void show() {
        //     System.out.println("这是一个季节。。。。。。");
        // }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    注解

    一、什么是注解,怎么定义

    使用@interface关键字声明。
    内部定义成员,通常使用value表示
    可以指定成员的默认值,使用default定义默认值。
    如果自定义的注解没有成员,表明是一个标识作用。
    如果注解有成员,使用注解时需要指明成员的值。
    自定义注解必须配上注解的信息处理流程(反射的方式)才有意义。
    自定义注解通常都会指明两个元注解,即:@Retention \ @Target。

    @Repeatable(MyAnnotations.class)
    //@Inherited // 可继承注解。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE , TYPE_PARAMETER , TYPE_USE})
    public @interface MyAnnotation {
        String value() default "world";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    二、什么是元注解

    元注解:对现有注解进行解释说明的注解。
    @Retention:指定所修饰的Annotation的声明周期: SOURCE\CLASS(默认行为)\RUNTIME
    只有声明为RUNTIME声明的注解,才能通过反射获取。
    @Target:用于指定被修饰的Annotation能用于修饰哪儿些程序元素。
    @Documented:表示所修饰的注解在被javadoc解析时,保留下来。
    @Inherited:具有继承性,如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

    通过反射获取注解信息:

    Class<Student> studentClass = Student.class;
    Annotation[] annotations = studentClass.getAnnotations();
    
    • 1
    • 2
    • 可重复注解、类型注解

      • 可重复注解:
        • MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
        • MyAnnotation的Target和Retention和MyAnnotations相同。
    • 类型注解:

      • ElementType.TYPE_PARAMETER : 表示该注解能写在类型变量的声明语句中(如:泛型声明)
      • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。

    三、注解使用场景

    1. 生成文档相关注解
    2. 在编译时进行格式检查
      @Override:限定重写父类方法,该注解只能用于方法
      @Deprecate:用于标识锁修饰的元素(类、方法)已过时。通常是因为锁修饰的结构危险或存在更好的选择。
      @SuppressWarnings:抑制编译器警告。

    四、例子

    @Repeatable(MyAnnotations.class)
    //@Inherited // 可继承注解。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE , TYPE_PARAMETER , TYPE_USE})
    public @interface MyAnnotation {
        String value() default "world";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    public @interface MyAnnotations {
        MyAnnotation[] value();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    import org.junit.Test;
    
    import java.lang.annotation.Annotation;
    import java.util.ArrayList;
    
    
    /**
     *
     */
    public class AnnotationTest {
        @Test
        public void testAnnotation() {
            Class<Student> studentClass = Student.class;
            Annotation[] annotations = studentClass.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
    }
    
    @MyAnnotation(value = "hello")
    @MyAnnotation(value = "abc")
    // jdk8之前使用重复注解的方式
    //@MyAnnotations({@MyAnnotation(value = "hello") , @MyAnnotation(value = "abc")})
    
    
    class Person{
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void walk(){
            System.out.println("人走路");
        }
    }
    
    class Student extends Person {
        @Override
        public void walk() {
            super.walk();
        }
    }
    
    class Generic<@MyAnnotation T>{
        public void show() throws @MyAnnotation RuntimeException{
            ArrayList<@MyAnnotation String> objects = new ArrayList<>();
            int num = (@MyAnnotation int)10L;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  • 相关阅读:
    Java实用类-Enum(枚举)
    ubuntu18.04环境搭建
    python环境移植,制作可以移植的python环境
    Python二进制文件转换为文本文件
    数字孪生背后:谁在构建真实的数字世界?
    供应脂质体形成材料DSPE-PEG-Thiol,DSPE-PEG-SH
    Android 活动Activity
    OpenCV每日函数 结构分析和形状描述符(8) fitLine函数 拟合直线
    React之组件实例的三大属性之props
    开源的python 游戏开发库介绍
  • 原文地址:https://blog.csdn.net/weixin_44735933/article/details/127606419