• Java反射知识总结、Java内存分析、通过反射动态创建对象、操作泛型、注解


    =NEW 2022-09-09=

    Java反射Reflect

    • 反射(Reflect)是在运行时动态的访问类与对象的技术
    • 反射是JDK1.2版本后的高级特性,隶属于java.lang.reflect
    • 大多数Java框架都基于反射实现参数配置。动态注入等特性

    概念

    Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并且直接操作任意对象的内部属性和方法:

    Class c = Class.forName(java.lang.String)
    
    • 1

    加载完类之后,在堆内存的方法区中产生了一个Class类型的对象(一个类只有Class对象),这个对象就包含了完整类的结构信息,通过这个对象来看到类的结构,所以称之为“反射”。

    在这里插入图片描述

    Java反射机制提供的功能

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

    **优点:**可以实现动态创建对象和编译,体现出很大的灵活性

    **缺点:**对性能有影响,使用反射基本上是一种解释操作,可以告诉jvm我们的需求,但是这类操作总是慢于直接执行相同的操作。

    主要api

    • java.lang.Class:代表一个类

    • java.lang.reflect.Method:代表类的方法

    • java.lang.reflect.Field:代表类的成员变量

    • java.lang.reflect.Constructor:代表类的构造器

      以上方法返回值类型都是一个Class类,此类是java反射的源头,,所以也可以看作是通过对象求出类的名称。

    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
            //通过反射来获取类的class对象
            Class c1 = Class.forName("包名.User");
            System.out.println(c1);
    
            System.out.println(c1.getAnnotations());//获取类的注解
            System.out.println(c1.getConstructors());//获取类的方法
    
            //创建多个对象,通过hashcode方法判断是否是同一个类
            Class c2 = Class.forName("注解和反射.反射.User");
            Class c3 = Class.forName("注解和反射.反射.User");
            //一个类在内存中只有一个Class对象
            //一个类被加载之后,整个类都会被封装在Class对象之中
            System.out.println(c1.hashCode());
            System.out.println(c2.hashCode());
            System.out.println(c3.hashCode());
            //发现三个对象的类都是一样的
        }
    }
    //测试类
    class User{
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    获取Class类的方式

    1. 方式一:若已知具体的类,通过类的class属性获取,该方法最可靠。程序性能更高

      Class c3 = Student.class;

    2. 方式二:已知某个类的实例,调用该实例的getClass()方法获取Class对象

      Class c1 = person.getClass();

    3. 方式三:已知一个类的全类名。且该类在类的路径,可通过Class类的静态方法forName()获取,可以抛出ClassNotFoundException异常

      Class c2 = Class.forName(“包名.类名”);

    4. 方式四:内置基本数据类型可以直接类名.Type

      Class c4 = Integer.TYPE;

    //测试Class类的创建方式有哪些
    public class Test02 {
        public static void main(String[] args) throws ClassNotFoundException {
            Person person = new Student();
            System.out.println("这个人是" + person.name);
    
            //方式一:通过对象获取
            Class c1 = person.getClass();
            //方式二:用forname获取
            Class c2 = Class.forName("包名.Student");
            //方式三:通过类名.class获取
            Class c3 = Student.class;
    
            //判断是否是同一个类
            System.out.println(c1.hashCode());
            System.out.println(c2.hashCode());
            System.out.println(c3.hashCode());
    
            //方式四:基本内置类型的包装类都有一个Type属性
            Class c4 = Integer.TYPE;//输出的是int类
            System.out.println(c4);
    
            //获取父类类型的方法
            Class c5 = c1.getSuperclass();
            System.out.println(c5);
        }
    }
    //测试类
    class Person{
    }
    
    class Student extends Person{
        public Student (){
            this.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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    Java内存分析

    在这里插入图片描述

    堆内存:

    • 存放new的对象和数组
    • 可以被所有线程共享,不会存放别的对象的引用

    栈内存:

    • 存放基本变量类型(会包含这个基本类型的具体数值)
    • 引用对象的变量(会存放这个引用在堆内存的具体地址)

    方法区:

    • 可以被所有线程共享
    • 包含所有的class和static变量

    类加载过程

    在这里插入图片描述

    当程序使用某类时,如果该类还未被加载到内存中,则系统会通过如下三步来对类进行初始化

    类的加载(Load):将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成。

    类的链接(Link):将类的二进制数据合并到JRE中。
    验证:确保加载的类的信息符合JVM规范,没有安全方面的问题
    准备:正式为类变量(static)分配内存并设置类变量的默认初始值的阶段,这些内存都将在方法区中进行分配
    解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程

    类的初始化(Initialize):JVM负责对类进行初始化。
    执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类的所有类变量的赋值动作和静态代码块中的语句合并产生的(类构造器是构造类信息的,表示构造该类对象的构造器)
    当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
    虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步

    public class Test04 {
        public static void main(String[] args) {
            A a = new A();
            System.out.println(A.m);//输出m是100
        }
    }
    /*
    * 1.加载内存,会产生一个类对应的class对象
    * 2.链接,链接结束后m=0
    * 3.初始化
    * (){
    * //将静态代码块合并
    *   System.out.println("A类静态代码块初始化");
         m = 300;
         int m = 100;
         //所以m是先等于300再等于100,最后是100
    * }*/
    class A{
    
    
        static {
            System.out.println("A类静态代码块初始化");
            m = 300;
        }
        static int m = 100;
    
    
        public A(){
            System.out.println("A类的构造器初始化");
        }
    }
    
    • 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

    在这里插入图片描述

    类什么时候发生初始化

    类的主动引用(一定会发生类的初始化)

    1. 当虚拟机启动时,先初始化main方法所在的类
    2. new一个类对象
    3. 调用类的静态成员(除了final常量)和静态方法
    4. 使用java.lang.reflect包的方法对类进行反射调用
    5. 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

    类的被动引用(不会发生类的初始化)

    1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化,如:当通过子类引用父类的静态变量,不会导致子类初始化
    2. 通过数组定义类的引用,不会触发此类的初始化
    3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
    public class Test05 {
        //1.1当虚拟机启动时,先初始化main方法所在的类
        static {
            System.out.println("主类被加载");
        }
    
        public static void main(String[] args) throws ClassNotFoundException {
            //1.2new一个类对象
            //Son son = new Son();
    
            //1.3调用类的静态成员(除了final常量)和静态方法
            //System.out.println(Son.m);
    
            //1.4使用java.lang.reflect包的方法对类进行反射调用
            //Class.forName("注解和反射.反射.Test05");
    
            //2.1当通过子类引用父类的静态变量,不会导致子类初始化
            //System.out.println(Son.b);
    
            //2.2通过数组定义类的引用,不会触发此类的初始化
            //Son[] arr = new Son[5];
    
            //2.3引用常量不会触发此类的初始化
            //System.out.println(Son.M);
        }
    }
    
    class Father{
        static int b = 2;
    
        static {
            System.out.println("父类被加载");
        }
    }
    
    class Son extends Father{
        static {
            System.out.println("子类被加载");
            m = 300;
        }
    
        static int m = 100;
        static final int M = 1;
    
    }
    
    • 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

    类加载器的作用

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    双亲委派机制

    当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。
    Java中提供如下四种类型的加载器,每一种加载器都有指定的加载对象,具体如下
    Bootstrap ClassLoader(启动类加载器) :主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
    Extention ClassLoader(扩展类加载器):主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
    Application ClassLoader(应用程序类加载器) :主要负责加载当前应用的classpath下的所有类
    User ClassLoader(用户自定义类加载器) : 用户自定义的类加载器,可加载指定路径的class文件
    这四种类加载器存在如下关系,当进行类加载的时候,虽然用户自定义类不会由bootstrap classloader或是extension classloader加载(由类加载器的加载范围决定),但是代码实现还是会一直委托到bootstrap classloader, 上层无法加载,再由下层是否可以加载,如果都无法加载,就会触发findclass,抛出classNotFoundException.

    获取类运行时的结构

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class Test06 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
            //第一种方式
            Class c1 = Class.forName("注解和反射.反射.User");
    
            //第二种方式
            User user = new User();
            c1 = user.getClass();
    
            //获取类的名字
            System.out.println(c1.getName());//获得包名加类名
            System.out.println(c1.getSimpleName());//获得类名
    
            System.out.println("=============================");
            //获得类的属性
            //只能获取类的public属性
            Field[] fields = c1.getFields();
            for (int i = 0; i < fields.length; i++) {
                System.out.println(fields[i]);
            }
            //可以获取类的全部属性
            fields = c1.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                System.out.println(fields[i]);
            }
            //获取指定属性
            Field name = c1.getField("name");
            System.out.println(name);
    
            System.out.println("=============================");
            //获取类的方法
            //获取本类以及父类的全部public方法
            Method[] methods = c1.getMethods();
            for (Method method : methods) {
                System.out.println("第一种方法" + method);
            }
            //只获得本类的所有方法
            Method[] declaredMethods = c1.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                System.out.println("第二种方法" + declaredMethod);
            }
            //获得指定方法
            Method getName = c1.getMethod("getName", null);
            System.out.println(getName);
            Method setName = c1.getMethod("setName", String.class);
            System.out.println(setName);
    
            System.out.println("=============================");
            //获得指定构造器
            //只能获取类的public构造器
            Constructor[] constructors = c1.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
            //能获取类的全部构造器
            Constructor[] declaredConstructors = c1.getDeclaredConstructors();
            for (Constructor declaredConstructor : declaredConstructors) {
                System.out.println(declaredConstructor);
            }
            //获取指定的构造器
            Constructor declaredConstructor = c1.getDeclaredConstructor(String.class);
            System.out.println(declaredConstructor);
        }
    }
    
    • 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

    通过反射动态创建对象

    1.创建类的对象:调用Class对象的**newInstance()**方法

    1. 类必须有一个无参数的构造器
    2. 类的构造器的访问权限需要足够

    2.若没有无参构造器如何创建对象

    1. 通过Class类的getDeclaredConstructor(参数列表)取得本类的指定形参类型的构造器
    2. 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数
    3. 通过Constructor实例化对象

    3.调用指定的方法

    1. 通过Class类的getDeclaredMethod(参数列表)方法获取一个Method对象,并设置此方法操作时所需要的参数类型
    2. 之后使用Object invoke(类的对象,传进去的参数)进行调用,并向方法中传递要设置的obj对象的参数信息
    3. 如果方法为private,则需要在invoke方法调用之前,先用方法对象调用setAccessible(true)方法再进行激活

    4.调用指定的属性

    1. 通过Class类的getField(参数列表)方法获取一个Field对象,并设置此属性名称
    2. 之后使用Object set(类的对象,传进去的参数)进行调用,并向方法中传递要设置的obj对象的参数信息
    3. 如果方法为private,则需要在set方法调用之前,先用方法对象调用setAccessible(true)方法再进行设置
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    //通过反射,动态创建对象
    public class Test07 {
        public static void main(String[] args) throws Exception{
            //获取Class对象
            Class c1 = Class.forName("注解和反射.反射.User");
    
            //1.构造一个对象(通过无参构造器)
            User user = (User) c1.newInstance();//本质上是调用类的无参构造器
            System.out.println(user);
            System.out.println("==============================");
    
            //2.通过构造器构造对象
            Constructor constructor = c1.getDeclaredConstructor(String.class);
            User user2 = (User)constructor.newInstance("你好");//括号里是参数
            System.out.println(user2);
            System.out.println("==============================");
    
            //3.通过反射调用普通方法
            User user3 = (User) c1.newInstance();
            //3.1获取一个方法
            Method setName = c1.getDeclaredMethod("setName", String.class);
            //3.2invoke:激活的意思
            //参数列表:(对象,"方法的值")
            setName.invoke(user3,"小明");//参数要包括user类的对象
    
            //4.通过反射操作属性
            System.out.println("==============================");
            User user4 = (User) c1.newInstance();
            Field name = c1.getField("name");//获取指定属性
            //不能直接操作私有属性,可以选择关闭程序的安全检测,属性或方法的setAccessible(true)。若为true则属性和方法可以被随意调用,相反则不可以
            name.setAccessible(true);
            //参数列表:(对象,"方法的值")
            name.set(user4,"小王");
            System.out.println(user4.getName());
    
    
        }
    }
    
    • 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

    性能分析与对比

    测试用例:

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        test1();
        test2();
        test3();
    }
    
    public static void test1() {
        TestUser user = new TestUser();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次: " + (endTime - startTime) + "ms");
    }
    
    public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        TestUser user = new TestUser();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
    
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次: " + (endTime - startTime) + "ms");
    }
    
    public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        TestUser user = new TestUser();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式-关闭权限检测-执行10亿次: " + (endTime - startTime) + "ms");
    }
    
    • 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

    执行结果:

    普通方式执行10亿次: 3ms
    反射方式执行10亿次: 1238ms
    反射方式-关闭权限检测-执行10亿次: 834ms
    
    • 1
    • 2
    • 3

    反射操作泛型

    在这里插入图片描述

    • ParameterizedType:表示一种参数化类型,比如Collect
    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
    • TypeVariable:是各种类型变量的公共的接口
    • WildcardType:代表一种通配符类型的表达式
    import java.lang.reflect.*;
    import java.util.List;
    import java.util.Map;
    
    public class Test08 {
        public void test01(Map<String,User> map, List<User> list){
            System.out.println("test01");
        }
        public Map<String,User> test02(){
            System.out.println("test02");
            return null;
        }
    
        public static void main(String[] args) throws NoSuchMethodException {
            
            //先获取方法
            Method method = Test08.class.getMethod("test01", Map.class, List.class);
            //获取泛型的参数类型
            Type[] genericParameterTypes = method.getGenericParameterTypes();
            for (Type genericParameterType : genericParameterTypes) {
                //打印出类型,一个map一个list
                System.out.println("#" + genericParameterType);
                //判断泛型是否为结构化的参数类型
                if(genericParameterType instanceof ParameterizedType){
                    //是的话就进行强制转换,转化出来再通过getActualTypeArguments获得真实参数信息
                    Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                    //获取完再打印出来
                    for (Type actualTypeArgument : actualTypeArguments) {
                        System.out.println(actualTypeArgument);
                    }
                }
            }
    
            //原理同上
            method = Test08.class.getMethod("test02",null);
            Type genericReturnType = method.getGenericReturnType();
            if(genericReturnType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
    
        }
    }
    
    • 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

    反射操作注解

    在这里插入图片描述

    import java.lang.annotation.*;
    import java.lang.reflect.Field;
    
    //练习反射操作注解
    public class Test09 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
            Class c1 = Class.forName("注解和反射.反射.Student2");
    
            //通过反射获取注解
            Annotation[] annotations = c1.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
    
            //获取TableJava注解中的value的值
            //通过强制转换来获取注解对象
            TableJava tablejava = (TableJava)c1.getAnnotation(TableJava.class);
            String value = tablejava.value();
            System.out.println(value);
    
            //获取类的指定注解
            //1,先获取类的属性
            Field name = c1.getDeclaredField("name");
            //2,再获取属性里面注解的对象
            FieldJava annotation = name.getAnnotation(FieldJava.class);
            //3,通过获取的注解对象调用出其中的元素
            System.out.println(annotation.columname());
            System.out.println(annotation.length());
            System.out.println(annotation.type());
        }
    }
    
    @TableJava("学生")
    class Student2{
        @FieldJava(columname = "卡号",type = "类型",length = 10)
        private int id;
        @FieldJava(columname = "年龄",type = "类型",length = 10)
        private String age;
        @FieldJava(columname = "姓名",type = "类型",length = 10)
        private String name;
    
        public Student2() {
        }
    
        public Student2(int id, String age, String name) {
            this.id = id;
            this.age = age;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Student2{" +
                    "id=" + id +
                    ", age='" + age + '\'' +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    //类名的注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface TableJava{
        String value();
    }
    
    //属性的注解
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface FieldJava{
        String columname();
        String type();
        int length();
    }
    
    • 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

    =OLD 2021-07-28=

    反射实现案例:

    MathOperation.java

    package reflect;
    
    /**
     * 四则运算接口
     */
    public interface MathOperation {
        float operate(int a,int b);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Addition.java

    package reflect;
    
    public class Addition implements MathOperation{
        @Override
        public float operate(int a,int b){
            System.out.println("执行加法运算:");
            return a+b;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Subtraction.java

    package reflect;
    
    public class Subtraction implements MathOperation{
        @Override
        public float operate(int a,int b){
            System.out.println("执行减法运算:");
            return a-b;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ReflectSample.java

    package reflect;
    
    import java.util.Scanner;
    
    public class ReflectSample {
        /**
         * 传统的创建对象方式
         */
        public static void case1(){
            Scanner sc = new Scanner(System.in);
            System.out.println("输入计算类名:");
            String op = sc.next();
            System.out.println("输入a的值:");
            int a = sc.nextInt();
            System.out.println("输入b的值:");
            int b = sc.nextInt();
            MathOperation mo = null;
    
            if(op.equals("Addition")){
                mo = new Addition();
            }else if(op.equals("Subtraction")){
                mo = new Subtraction();
            }else {
                System.out.println("无效的计算类");
                return;
            }
            float result = mo.operate(a,b);
            System.out.println(result);
        }
    
        /**
         * 用反射机制灵活创建对象
         */
        public static void case2() {
            Scanner sc = new Scanner(System.in);
            System.out.println("输入计算类名:");
            String op = sc.next();
            System.out.println("输入a的值:");
            int a = sc.nextInt();
            System.out.println("输入b的值:");
            int b = sc.nextInt();
            MathOperation mathoperation = null;
    
            try {
                //newInstance()对这个类进行实例化
                mathoperation = (MathOperation) Class.forName("reflect."+op).newInstance();
            } catch (Exception e) {
                System.out.println("无效的计算类");
                return;
            }
            float result = mathoperation.operate(a,b);
            System.out.println(result);
        }
    
        public static void main(String[] args) {
    //        case1();
            case2();
        }
    }
    
    • 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

    在不修改源代码的情况动态增加功能。当我们需要增加新功能乘法时,不需要修改反射机制代码。

    反射的核心类

    • Class类
    • Constructor构造方法类
    • Method方法类
    • Field成员变量类

    员工类信息:

    reflect.entity.Employee.java

    package reflect.entity;
    
    public class Employee {
        static {
            System.out.println("Employee已被加载到jvm中并初始化");
        }
        private Integer no;
        public String ename;
        private Float salary;
        private String dname;
    
        public Employee() {
            System.out.println("Employee的默认的构造方法已被执行");
        }
    
        public Employee(Integer no, String ename, float salary, String dname) {
            this.no = no;
            this.ename = ename;
            this.salary = salary;
            this.dname = dname;
            System.out.println("带参构造方法已被执行");
        }
    
        public Integer getNo() {
            return no;
        }
    
        public void setNo(Integer no) {
            this.no = no;
        }
    
        public String getEname() {
            return ename;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public float getSalary() {
            return salary;
        }
    
        public void setSalary(float salary) {
            this.salary = salary;
        }
    
        public String getDname() {
            return dname;
        }
    
        public void setDname(String dname) {
            this.dname = dname;
        }
    
        @Override
        public String toString() {
            return "Employee{" +
                    "no=" + no +
                    ", ename='" + ename + '\'' +
                    ", salary=" + salary +
                    ", dname='" + dname + '\'' +
                    '}';
        }
    
        public Employee updateSalary(Float val){
            this.salary = this.salary+val;
            System.out.println(this.ename+"调薪至"+this.salary);
            return this;
        }
    }
    
    • 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

    Class类

    • Class是JVM中代表"类和接口"的类
    • Class对象具体包含了某个特定类的接口信息
    • 通过Class对象可获取对应类的构造方法/方法/成员变量

    一个类在内存中只有一个Class对象,一个类被加载后,整个类结构会被封在在Class中

    Class类核心方法

    方法用途
    Class.forName()静态方法,用于获取指定的Class对象
    classObj.newInstance()用于默认构造方法创建新的对象
    classObj.getConstructor()获取指定的public修饰构造方法Constructor对象
    classObj.getMethid()获取指定的public修饰方法Method对象
    classObj.getField()获取指定的public修饰成员变量Fileld对象

    Class类对象实例化

    reflect.ClassSample.java

    package reflect;
    
    import reflect.entity.Employee;
    
    public class ClassSample {
        public static void main(String[] args) {
            //Class.forName将指定的类加载在jvm中,并返回对应的Class对象
            try {
                Class employeeClass = Class.forName("reflect.entity.Employee");
                System.out.println("Employee已被加载到jvm");
                Employee emp= (Employee) employeeClass.newInstance();	//使用默认构造方法实例化类
                System.out.println(emp);
            } catch (ClassNotFoundException e) {   //ClassNotFoundException表示Class类不存在
                e.printStackTrace();
            } catch (IllegalAccessException e) {  //IllegalAccessException表示非法访问异常,当在作用域外访问对象方法或成员变量时抛出
                e.printStackTrace();
            } catch (InstantiationException e) {  //InstantiationException表示实例化异常,
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Constructor构造方法类

    • Constructor是Java类中的构造方法的抽象
    • 该对象包含了某个具体构造方法的声明
    • 通过Constructor对象调用带参构造方法调用对象

    Constructor类核心方法

    方法用途
    classObj.getConstructor()获取指定的public修饰的构造方法对象
    constructorObj.newInstance()通过对应的构造方法创建对象

    Constructor带参构造方法创建对象

    reflect.ConstructorSample.java

      
    import java.lang.reflect.InvocationTargetException;
    
    public class ConstructorSample {
        public static void main(String[] args) {
            try {
                Class employeeClass = Class.forName("reflect.entity.Employee");
                Constructor constructor = employeeClass.getConstructor(Integer.class, String.class, float.class, String.class);
                Employee employee = (Employee) constructor.newInstance(new Object[]{
                        001, "张三", 3000, "研发部"
                });
                System.out.println(employee);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {   //表示没有找到与之对应格式的方法
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {  //当被调用的方法内部抛出了异常而没捕获的时候
                e.printStackTrace();
            }
        }
    }
    
    • 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

    Method方法类

    • Method对象指代某个类中的方法的描述
    • Method对象使用classObj.getMethod()方法获取
    • 通过Method对象调用指定对象的对应方法

    Method类的核心方法

    方法用途
    classObj.getMethod()获取指定的method修饰的方法对象
    methodObj.invoke()调用指定对象的对应方法

    利用Method执行方法

    reflect.MethodSample.java

    package reflect;
    
    import reflect.entity.Employee;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class MethodSample {
        public static void main(String[] args) {
    
            try {
                Class employeeClass = Class.forName("reflect.entity.Employee");
                Constructor constructor = employeeClass.getConstructor(Integer.class,String.class,float.class,String.class);
                Employee employee = (Employee) constructor.newInstance(new Object[]{
                        001,"李四",2000f,"营销部"
                });
                //传入方法名和参数类型
                Method updateSalaryMethod =  employeeClass.getMethod("updateSalary", Float.class);
                //updateSalary返回Employee类型强制转换接收,invoke执行传入要执行的对象和对象数组参数
                Employee employee1 = (Employee)updateSalaryMethod.invoke(employee,new Object[]{1000f});
    
                System.out.println(employee1);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 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

    Field成员变量

    • Field对应某个具体类中的成员变量的声明
    • 其对象使用classObj.getField()方法获取
    • 通过Field对象可为某对象成员变量赋值/取值

    Field类核心方法

    classObj.getField()获取指定public修饰的成员变量对象
    fieldObj.set()为某对象指定成员变量赋值
    fieldObj.get()获取某对象指定成员变量数值

    利用Field进行赋值/取值

    reflect.Field.java

    package reflect;
    
    import reflect.entity.Employee;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    
    public class FieldSample {
        public static void main(String[] args) {
            try {
                Class employeeClass = Class.forName("com.reflect.entity.Employee");
                Constructor constructor = employeeClass.getConstructor(Integer.class, String.class, float.class, String.class);
                Employee employee = (Employee) constructor.newInstance(new Object[]{
                        100, "王五", 2000f, "经营部"
                });
                Field enameField = employeeClass.getField("ename");
                enameField.set(employee,"李雷");
                String ename = (String) enameField.get(employee);
                System.out.println("ename:"+ename);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {//没有找到对应成员变量
                e.printStackTrace();
            }
        }
    }
    
    • 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

    getDeclared系列方法说明

    • getDeclaredConstructor(s)|Method(s)|Field(s)获取对应对象
    • getConstructor(s)|Method(s)|Field(s)只能获取public对象
    • 访问非作用域内构造方法、方法、成员变量会抛出异常

    利用getDeclared系列方法查看类内部结构

    reflect.GetDeclareSample.java

    package reflect;
    
    import reflect.entity.Employee;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class GetDeclareSample {
        public static void main(String[] args) {
            try {
                Class employeeClass = Class.forName("reflect.entity.Employee");
                Constructor constructor = employeeClass.getConstructor(Integer.class,String.class,float.class,String.class);
                Employee employee = (Employee) constructor.newInstance(new Object[]{
                        001,"李四",2000f,"营销部"
                });
                //获取当前类的所有成员变量
                Field[] fields = employeeClass.getDeclaredFields();
                for(Field field:fields) {
    //                System.out.println(field.getName());
                    //field.getModifiers()==1 public修饰, ==2 private修饰
                    if(field.getModifiers() == 1){
                        Object val = field.get(employee);
                        System.out.println(field.getName()+":"+val);
                    }else if(field.getModifiers() == 2){
                        String methodName = "get"+field.getName().substring(0,1).toUpperCase()+field.getName().substring(1);
                        Method getMethod = employeeClass.getMethod(methodName);
                        Object object = getMethod.invoke(employee);
                        System.out.println(field.getName()+":"+object);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 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
  • 相关阅读:
    局部特征匹配(LoFTR) & 基于全局匹配的光流学习(GMFlow)
    HT4832/ HT4831 -33mW免输出电容立体声耳机放大器
    【手把手】教你玩转SpringCloud Alibaba之Nacos Config深入
    【前端笔试】知识点总结
    2022年09月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试
    JavaScript实用库
    易基因|RNA m6A甲基化测序(MeRIP-seq)技术介绍
    C语言洛谷题目分享(10)最厉害的学生和明明的随机数
    基于springboot的鲜花销售商城网站
    ESP32网络开发实例-Web服务器控制GPIO
  • 原文地址:https://blog.csdn.net/a2272062968/article/details/126786494