• 【Java】反射


    反射的作用是什么?

    每一个类都会在堆中有一个Class类型的对象,记录了这个类的属性,方法,构造器等信息。
    利用反射机制,可以根据这个Class对象,创建实例对象,获取属性和方法。

    反射的优势是什么?

    在没有反射机制的时候,我们同样可以创建对象和调用对象的属性和方法,那为什么还要有反射?

    在没有反射机制的时候,我们虽然可以创建对象和调用对象的属性和方法。但是这样做的条件是这个对象的全路径名和调用的方法与属性都是写死在代码中的,如果后期有修改或者想要替换类的路径和调用的方法名,就必须修改源代码,不符合开闭原则。

    但是利用反射,我们可以把相关类,属性,方法的名称写在配置文件中,方便后期的修改和维护。

    因此反射的优点就是:

    • 可以动态的创建和使用对象,更加灵活,是框架的核心

    什么是Class对象?

    • Class 对象记录了每个类的属性和方法各种信息,存放在堆中,只有一个
    • Class 对象不是new出来的,是类加载的时候,系统自动创建的,是通过classLoader的loadClass方法创建的,只会加载一次

    什么时候会进行类加载?

    以下情况会导致类的初始化,而类的初始化要先进行类的加载
    image.png

    静态加载和动态加载的区别?

    • 静态加载: 编译时加载相关的类,如果没有则报错,依赖性太强
    • 动态加载: 运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性

    类加载的过程?

    如何获取Class对象?

    image.png

            //1.Class.forName 往往通过配置文件读取到
    
            String classpath="learn.Car";
    
            Class<?> cls = Class.forName(classpath);
    
            System.out.println(cls);
    
            System.out.println(cls.hashCode());
    
            //2.类名.class 多用于参数传递
    
            Class<Car> cls2 = Car.class;
    
            System.out.println(cls2);
    
            System.out.println(cls2.hashCode());
    
            //3.通过对象名来获取 对象名.getclas
    
            Car car=new Car();
    
            Class cls3=car.getClass();
    
            System.out.println(cls3);
    
            System.out.println(cls3.hashCode());
    
            //4.通过类加载器(4种),来获取
    
            //   4.1,先得到类加载器
    
            ClassLoader classLoader = car.getClass().getClassLoader();
    
            //    4.2,通过类加载器得到类对象
    
            Class<?> cls4= classLoader.loadClass(classpath);
    
            System.out.println(cls4);
    
            System.out.println(cls4.hashCode());
    
            //5.基本数据类型也可以 int.class
    
            Class<Integer> integerClass = int.class;
    
            System.out.println(integerClass);//int
    
            //6.基本数据类型的包装类 Integer.TYPE
            Class<Integer> type = Integer.TYPE;
    
            System.out.println(type);//int
    
            System.out.println(integerClass.hashCode());
    
            System.out.println(type.hashCode());//Integer.TYPE和int.class二者是一样的,底层通过拆箱和装箱是同一个
    

    哪些类型有Class对象?

    image.png

    Class<String> stringClass = String.class;//外部类
    
    Class<Serializable> serializableClass = Serializable.class;//接口类
    
    Class<int[]> aClass = int[].class;//数组类
    
    Class<Deprecated> deprecatedClass = Deprecated.class;//注解类
    
    Class<Thread.State> stateClass = Thread.State.class;//枚举类
    
    Class<Integer> integerClass = int.class;//基本数据类型类
    
    Class<Integer> integerClass1 = Integer.class;//基本数据类型包装类类
    
    Class<Void> voidClass = void.class;//void 类
    
    Class<Class> classClass = Class.class;//Class类,也是一种外部类
    

    如何根据Class对象创建一个具体的实例对象?

    image.png

    Person类

    public class Person {  
        //名字  
        private String name;  
        //年龄  
        private Integer age;  
      
        public void talk(){  
            System.out.println("你好");  
        }  
        public void talk(String message){  
            System.out.println(message);  
        }  
      
        @Override  
        public String toString() {  
            return "Person{" +  
                    "name='" + name + '\'' +  
                    ", age=" + age +  
                    '}';  
        }  
        public Person(){  
      
        }  
        public Person(String name, Integer age) {  
            this.name = name;  
            this.age = age;  
        }  
      
        public String getName() {  
            return name;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public Integer getAge() {  
            return age;  
        }  
      
        public void setAge(Integer age) {  
            this.age = age;  
        }  
    }
    
    1. 根据class对象,直接newInstance具体实例对象
    @Test  
    public void test2() throws Exception {  
        Class<?> aClass = Class.forName("ref.Person");  
        Object o = aClass.newInstance();  
    }
    

    直接newInstance,无法传递参数,也就是说智能获得无参的构造函数,因此要确保函数中存在无参构造函数。

    1. 根据class对象,获取Constructor,然后再利用Constructor创建具体实例对象
    @Test  
    public void test3() throws Exception {  
        Class<?> aClass = Class.forName("ref.Person");  
        Constructor<?> constructor = aClass.getConstructor();  
        Object o = constructor.newInstance();  
        Constructor<?> constructor2 = aClass.getConstructor(String.class, Integer.class);  
        Object o2 = constructor2.newInstance("小亮", 10);  
    }
    
    

    实例对象的具体运行类型?

    实例对象的运行类型是其真实的类型,因此我们可以将object向下转型,然后可以调用更多的方法

        @Test
        public void test2() throws Exception {
            Class<?> aClass = Class.forName("ref.Person");
            System.out.println(aClass);//class ref.Person
            Object o = aClass.newInstance();
            System.out.println(o.getClass());//class ref.Person
            System.out.println(o.getClass()==aClass);//true
            Person person=(Person) o;
            person.talk();
        }
    

    如何调用方法?

    method.invoke(object,paramters)

    @Test
    public void test3() throws Exception {
        Class<?> aClass = Class.forName("ref.Person");
        Constructor<?> constructor = aClass.getConstructor();
        Person person =(Person) constructor.newInstance();
        Method talk = aClass.getMethod("talk");
        talk.invoke(person);
    }
    @Test
    public void test4() throws Exception {
        Class<?> aClass = Class.forName("ref.Person");
        Constructor<?> constructor = aClass.getConstructor();
        Person person =(Person) constructor.newInstance();
        Method talk = aClass.getMethod("talk",String.class);
        talk.invoke(person,"再见");
    }
    

    如何获取方法的相关参数

    image.png

    如何调用属性?

        public void test5() throws Exception {
            Class<?> aClass = Class.forName("ref.Person");
            Constructor<?> constructor = aClass.getConstructor();
            Person person =(Person) constructor.newInstance();
            Field age = aClass.getField("age");
            age.set(person,14);
            System.out.println(person);
        }
    
    

    image.png

    如果方法或者属性是private,会怎么样?

    会报错,找不到这个方法或者属性
    image.png

    解决方法:

        @Test
        public void test5() throws Exception {
            Class<?> aClass = Class.forName("ref.Person");
            Constructor<?> constructor = aClass.getConstructor();
            Person person =(Person) constructor.newInstance();
            Field age = aClass.getDeclaredField("age");//declared
            age.setAccessible(true);
            age.set(person,14);
            System.out.println(person);
        }
    

    反射爆破是什么意思?

        private Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    

    通过私有的构造器,创建对象

        @Test
        public void test6() throws Exception {
            Class<?> aClass = Class.forName("ref.Person");
            // Constructor constructor = aClass.getConstructor(String.class,Integer.class);//报错
            Constructor<?> constructor = aClass.getDeclaredConstructor(String.class,Integer.class);//不报错
            constructor.setAccessible(true);//【爆破】
            Person person =(Person) constructor.newInstance("天天",12);
    
        }
    

    反射的缺点是什么?

    • 反射是解释执行的,因此性能会受到一定的影响
      • 但是对性能的影响,只有再访问达到百万级别以上,才会有感知,因此不必过度担心
    • 安全风险,反射可以访问私有的成员和方法
    public class Reflection02 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            m1();//传统
            m2();//反射
        }
        //传统方法来调用 hi
        public static void m1() {
            Cat cat = new Cat();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 90; i++) {
                cat.hi();
            }
            long end = System.currentTimeMillis();
            System.out.println("m1() 耗时=" + (end - start));
        }
        //反射机制调用方法 hi
        public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Class cls = Class.forName("com.hspedu.Cat");
            Object o = cls.newInstance();
            Method hi = cls.getMethod("hi");
            long start = System.currentTimeMillis();
            for (int i = 0; i < 900000000; i++) {
                hi.invoke(o);//反射调用方法
            }
            long end = System.currentTimeMillis();
            System.out.println("m2() 耗时=" + (end - start));
        }
    
    }
    

    image.png

    如何优化反射的访问速度?

    setAccessible(true);//在反射调用方法或属性时,访问检查的开关

        //反射调用优化 + 关闭访问检查
        public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Class cls = Class.forName("com.hspedu.Cat");
            Object o = cls.newInstance();
            Method hi = cls.getMethod("hi");
            hi.setAccessible(true);//在反射调用方法时,取消访问检查
            long start = System.currentTimeMillis();
            for (int i = 0; i < 900000000; i++) {
                hi.invoke(o);//反射调用方法
            }
            long end = System.currentTimeMillis();
            System.out.println("m3() 耗时=" + (end - start));
        }
    

    image.png

    当调用方法的时候,反射底层也做了优化,当调用超过16次,就会动态生成类,来正常调用方法

        @Test
        public void test2() throws Exception{
            Class<?> aClass = Class.forName("ref.Person");
            Method talk = aClass.getMethod("talk");
            long t0 = System.nanoTime();
            for (int i = 0; i < 16; i++) {
                talk.invoke(null);
            }
            long t1 = System.nanoTime();
            System.out.println("花费:"+(t1-t0));
            long t2 = System.nanoTime();
            for (int i = 0; i < 16; i++) {
                talk.invoke(null);
            }
            long t3 = System.nanoTime();
            System.out.println("花费:"+(t3-t2));
            /**
             * 你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好花费:536700
             * 你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好花费:80600
             */
        }
    
  • 相关阅读:
    V90伺服 EPOS模式下回原(详细配置+SCL源代码)
    项目实战(一) 瑞吉外卖
    使用spring注解时@Service注解,在注入时为null的问题
    Vitepress搭建组件库文档(下)—— 组件 Demo
    Windows 进程的创建和终止
    600+ 道 Java面试题及答案整理(建议收藏)
    麒麟KYLINOS中制作Ghost镜像文件
    Dapr 和 Spring Cloud 对比分析
    作用域与作用域链--面试题
    Python学习六
  • 原文地址:https://blog.csdn.net/weixin_50799082/article/details/139851438