• 15. Java反射和注解


    Java —— 反射和注解

    1. 反射

    动态语言:变量的类型和属性可以在运行时动态确定,而不需要在编译时指定
    常见动态语言:Python,JavaScript,Ruby,PHP,Perl;常见静态语言(C,C++,C#,Go,Java)
    Java:Java并不算是严格意义上的动态语言,从反射角度来说属于半动态语言(能通过反射机制实现了部分动态编程的能力)
    Java反射机制:Java编程语言提供的一种强大的特性,它允许程序在运行时动态地获取类的信息,并通过该信息操作类的成员变量、方法和构造函数

    Java 的反射机制:在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法

    Java反射常用API
    Class 类:反射的核心类,可以获取类的属性,方法等信息
    Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值
    Method类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法
    Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法
    获取Class对象的3种方法
               调用某个对象的getClass()方法
                 ❶ 类名 对象名 = new 类名();
                 ❷ Class 类对象名 = 对象名.getClass();
               使用Class类中的forName()静态方法(安全且性能好)
                 ❸ Class 类对象名 = Class.forName(“类的完全限定名”);
    高版本Java创建:var 类对象名 = 类名(需要创建类对象的类).class;
            Class<类型(类名)> 类对象名 = 类名(需要创建类对象的类.class;

    • 对应类的常用方法
      在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
    • 对应代码
    package reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 反射机制
     */
    
    public class TestDemo {
        public static void main(String[] args) throws NoSuchMethodException,
                InvocationTargetException, IllegalAccessException,
                InstantiationException, NoSuchFieldException {
            // 创建类对象
            var classes = Information.class;
            System.out.println("类名:" + classes.getSimpleName());
    
            // 根据类获取当前类的完全限定名
            String className = classes.getName();
            System.out.println("当前类的完全限定名:" + className);
    
            // 根据获取当前类的包名
            String packageName = classes.getPackageName();
            System.out.println("包名:" + packageName);
    
            // 根据类获取当前普通方法名
            List<String> list = new ArrayList<>();
            Method[] methods = classes.getMethods();
            for (Method method : methods
            ) {
                list.add(method.getName());
            }
            System.out.println("public方法个数:" + list.size());
            System.out.println("public方法:" + list);
    
    
            // 获取构造器(无参构造器)
            Constructor<Information> constructor = classes.getConstructor();
            // 利用无参构造器创建实例
            Object obj = constructor.newInstance();
    
            // 获取指定的普通方法,带参数
            Method publicMethod = classes.getMethod("publicMethod", String.class, int.class);
    
            // 获取指定私有方法
            Method privateMethod = classes.getDeclaredMethod("privateMethod", String.class);
            System.out.println("获取指定私有方法:" + privateMethod);
    
            // 强行开启私有方法访问权限
            privateMethod.setAccessible(true);
    
            // 获取所有普通public属性
            Field[] fields = classes.getFields();
            System.out.println("普通属性:" + Arrays.toString(fields));
            // 获取所有属性
            Field[] declaredFields = classes.getDeclaredFields();
            System.out.println("所有属性:" + Arrays.toString(declaredFields));
    
            // 获取属性名
            for (Field field:declaredFields
                 ) {
                String name = field.getName();
                System.out.print(name + ", ");
            }
            System.out.println();
    
    
            // 访问单个私有属性
            Field name = classes.getDeclaredField("name");
            // 强行打开权限
            name.setAccessible(true);
            System.out.println("访问单个私有属性:名称:" + name.getName() + "   类型:" +
                    name.getType() + "  权限修饰符(二进制位标识的修饰符信息):" + name.getModifiers());
    
    
            // 私有方法调用
            privateMethod.invoke(obj, "零零");
            // 方法调用
            publicMethod.invoke(obj, "夏鸥", 22);
    
            System.out.println("无参构造器创建的实例:" + obj);
    
            // 获取有参构造器
            Constructor<Information> constructors = classes.getConstructor(String.class, int.class, String.class);
            // 有参构造器创建实例
            Object object = constructors.newInstance("王菲", 20, "2020002");
    
            // 获取指定的普通方法
            Method classesMethod = classes.getMethod("publicMethod");
            // 方法调用
            classesMethod.invoke(object);
    
            System.out.println("有参构造器创建的实例:" + object);
        }
    }
    
    class Information{
        private String name = "赫敏";
        private int age = 20;
        public String num = "2020001";
    
        public Information() {
        }
    
        public Information(String name, int age, String num) {
            this.name = name;
            this.age = age;
            this.num = num;
        }
    
        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 getNum() {
            return num;
        }
    
        public void setNum(String num) {
            this.num = num;
        }
    
        @Override
        public String toString() {
            return "TestDemo{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", num='" + num + '\'' +
                    '}';
        }
    
        private void privateMethod() {
            System.out.println(name + " 私有方法");
        }
    
        private void privateMethod(String name) {
            this.name = name;
            System.out.println(name + "  私有方法");
        }
    
        public void publicMethod() {
            System.out.println(name + " 普通方法");
        }
    
        public void publicMethod(String name, int age) {
            this.name = name;
            this.age = age;
            System.out.println(name + age + " 普通方法");
        }
    }
    
    • 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
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 应用实例
    package reflect;
    
    import java.io.File;
    import java.lang.reflect.InvocationTargetException;
    import java.net.URISyntaxException;
    import java.net.URL;
    
    /**
     * 要求:实例化与当前类在统一和包中的所有类
     */
    public class Instantiations {
        public static void main(String[] args) throws ClassNotFoundException, URISyntaxException, NoSuchMethodException,
                InvocationTargetException, InstantiationException, IllegalAccessException {
            /* 1. 获取当前包中的所有类名 */
            // 获取包名
            String packageName = Instantiations.class.getPackageName();
            // 获取当前绝对路径资源  ".":表示当前路径
            URL path = Instantiations.class.getResource(".");
            // 获取路径下的所有文件
            File file = new File(path.toURI());
            // 将所需文件过滤存储到数组(所有类)
            File[] files = file.listFiles(f -> f.getName().endsWith(".class"));
    
            // 获取到
            for (File sub:files
                 ) {
                String name = sub.getName();
                // 获取类名
                String className = name.replace(".class", "");
                System.out.println("实例化类:" + packageName + "." + className);
                Class<?> classes = Class.forName(packageName + "." + className);
                // 通过无参构造器创建实例
                Object obj = classes.getConstructor().newInstance();
                System.out.println(obj);
            }
        }
    }
    
    • 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

    2. 注解

    注解:元数据 / 注解(注解Annotation是一个接口),一种提供程序中元素信息和数据的途径和方法
    作用:在不改变程序主体逻辑的情况下,为程序员提供额外的元数据信息(标记一段代码的功能、作用范围、参数要求等信息)

    4种标准元注解:元注解的作用是负责注解其他注解
           ❶ @Target 注解修饰范围 / 修饰的目标元素类型
           ❷ @Retention:注解生命周期 / 保留级别 / 被保留的时间长短
           ❸ @Documented注解是否会被包含在Java文档中生成
           ❹ @Inherited注解是否可以被继承
      @Target元素类型
           ElementType.TYPE:类、接口或枚举类型
           ElementType.FIELD:字段或属性
           ElementType.METHOD:方法
           ElementType.PARAMETER:方法参数
           ElementType.CONSTRUCTOR:构造函数
           ElementType.LOCAL_VARIABLE:局部变量
           ElementType.ANNOTATION_TYPE:注解类型
           ElementType.PACKAGE:包
      @Retention的三个值
           RetentionPolicy.SOURCE:注解仅存在于源代码中,编译时会被丢弃
           RetentionPolicy.CLASS:注解存在于源码和编译后的字节码文件中,但在运行时会被丢弃(默认值)
           RetentionPolicy.RUNTIME:注解在运行时保留在字节码文件中,可以通过反射机制读取
      @Documented和@Inherited
           一个注解被@Documented修饰,那么它的信息将会被包含在生成的文档中
           一个注解被@Inherited修饰,表示该注解可以被子类继承

    • 基本原则:不直接干扰程序代码的运行
      Java 注解是一种元数据,它可以提供对程序元素(如类、方法、字段等)的额外信息或配置,并且不会直接影响程序的运行逻辑,只是一种机制,用于存储和传递额外的信息
    • 注解分类
      在这里插入图片描述

    • 声明注解及变长参数

    package reflect.annotations;
    
    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 AutoRunMethod {
        // 注解的参数(当注解下的参数只有一个时,一般用value)
        int value() default 1;
        String name() default "张三";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 使用注解
    package reflect;
    
    import reflect.annotations.AutoRunClass;
    import reflect.annotations.AutoRunMethod;
    
    import java.io.File;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.URISyntaxException;
    import java.util.Arrays;
    
    public class Reflect {
        public static void main(String[] args) {
            // 加载包下被注解的类被注解的方法
            try {
                String packName = Reflect.class.getPackage().getName();
                File dir = new File(Reflect.class.getResource(".").toURI());
                File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
                for (File sub:subs
                ) {
                    String className = sub.getName().replace(".class","");
                    Class classes = Class.forName(packName + "." + className);
                    if (classes.isAnnotationPresent(AutoRunClass.class)){
                        System.out.println("实例化:" + className);
                        Constructor constructor = classes.getConstructor();
                        Object obj = constructor.newInstance();
                        System.out.println(obj);
    
                        // 方法名获取
                        Method[] methods = classes.getDeclaredMethods();
                        for (Method method:methods
                             ) {
                            // 判断该方法是否被注解
                            if (method.isAnnotationPresent(AutoRunMethod.class)){
                                System.out.println("调用方法:" + method.getName() + "()");
    
                                // 获取注解参数
                                AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
                                // 获取值(定义时将其认为参数,调用时看作方法)
                                int value = arm.value();
                                String name = arm.name();
                                System.out.println("注解参数的值:" + value + name);
    
                                // 根据注解参数的值调用方法
                                for (int i = 0; i < value; i++) {
                                    // 方法调用
                                    method.invoke(obj);
                                }
                            }
                        }
                    }
                }
            } catch (URISyntaxException | ClassNotFoundException |
                     NoSuchMethodException | InstantiationException |
                     IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
    
        }
    }
    
    // 注解类
    @AutoRunClass
    class Student {
        public String address = "贵阳市花溪区";
        private String name = "赵涛";
        private char gender = '男';
        private int age = 15;
    
        public Student() {
        }
    
        public Student(String name, char gender, int age) {
            this.name = name;
            this.gender = gender;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public char getGender() {
            return gender;
        }
    
        public void setGender(char gender) {
            this.gender = gender;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        // 注解方法
        @AutoRunMethod(3)
        public void sayHi() {
            System.out.println(name + ":你好");
        }
    
        @AutoRunMethod(value = 2, name = "李四")
        public void sleep() {
            System.out.println(name + ":睡觉");
        }
    
        @AutoRunMethod(name = "王五")
        public void watchTV() {
            System.out.println(name + ":看电视");
        }
    
        public void playGame() {
            System.out.println(name + ":玩游戏");
        }
    
        public void say(String info) {
            System.out.println(name + "说:" + info);
        }
    
        public void say(String info, int count) {
            System.out.println(name + "说了" + count + "次," + info);
        }
    
        public void study() {
            System.out.println(name + "学习");
        }
    
        @AutoRunMethod
        public void doHomework() {
            System.out.println(name + ":做作业");
        }
    
        // **********************普通方法(参数变长)只是Java编译器认可(最终为数组)***************************
        // 变长参数只能在方的最后一个参数,且一个方法只能有一个变长参数
        public void doSome(String... s) {
            System.out.println("参数个数:" + s.length);
            System.out.println("参数:" + Arrays.toString(s));
        }
    
        private void privateMethod() {
            System.out.println("这是一个私有方法");
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "address='" + address + '\'' +
                    ", name='" + name + '\'' +
                    ", gender=" + gender +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 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
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 变长参数只能在方的最后一个参数,且一个方法只能有一个变长参数
    • 注解格式:
            类型 参数名() [default 默认值] 注:不指定默认值时使用注解必须传递对应参数
    • 注解传参机制:
      当注解仅有一个参数,且参数名为value时,直接传入参数(不需要参数名)
      当注解仅有一个参数,且参数名不为value时,正常使用注解传参语法:参数名=参数值
      多个参数传参使用参数名进行传参,传参顺序可以与注解定义时参数顺序不一致
      有多个参数时,即使一个注解的参数名为value,在实际使用时参数名也不可以忽略
  • 相关阅读:
    Linux系统下安装Java环境
    Mac电脑重置网络命令
    深入理解.Net中的线程同步之构造模式(二)内核模式1.内核模式构造物Event事件
    Docker V24 及 Docker Compose V2 的安装及使用
    ElasticSearch入门笔记
    计算机毕业设计 基于java的高校竞赛和考级查询系统的设计与实现 Java实战项目 附源码+文档+视频讲解
    SpringCloud:自定义skywalking链路追踪
    并发bug之源(二)-有序性
    leetcode - 42. Trapping Rain Water
    Java爬虫-爬取疫苗批次信息
  • 原文地址:https://blog.csdn.net/qq_58801558/article/details/133802831