java中的反射原理,为什么要使用反射以及反射使用场景(面试常问)

2023年 10月 13日 29.0k 0

java中的反射原理,为什么要使用反射以及反射使用场景

什么是反射

反射是框架的灵魂

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

在java中获取字节文件的方式有三种

  • 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  • Object(对象) ——> getClass();
  • 通过Class类的静态方法:forName(String className)(常用)
  •  		//方法一
            Class carEntityClass0 = CarEntity.class;
    
            //方法二
            CarEntity carEntity =new CarEntity();
            Class carEntityClass1 =carEntity.getClass();
    
            //方法三
            Class carEntityClass2 = Class.forName("com.example.demo3.Entity.CarEntity");
    
            //判断获取到同一个类的Class对象是否是同一个
            System.out.println(carEntityClass0 == carEntityClass1);
            System.out.println(carEntityClass1 == carEntityClass2);
            System.out.println(carEntityClass0 == carEntityClass2);
    
    

    上面的例子得到的结果,是三个true,由此我们得到了第一个定理:
    在运行期间,一个类,只有一个Class对象产生

    三种方式常用第三种,第一种需要导入类的包,依赖太强,不导包就抛编译错误。第二种对象都有了还要反射干什么。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法(框架中都是用的第三种)。

    好,现在我们得到了Class对象了,又有什么用呢,Class对象是什么呢,能做什么呢?

    在此之前我们先了解一下正常情况下我们new一个对象的时候,jvm底层做了什么事情。

    首先要搞明白一件事情,jvm能读懂我们的java代码吗?不能!

    那jvm是靠读取什么东西来运行程序的呢?.class文件!
    请放大看下图。。。。

    image.png

    也就是说,我们现在可以不通过JVM的编译直接获取到jvm运行时需要的Class对象!
    也就是说!我们是不是可以通过对Class对象进行修改而改变CarEntity这个类原本在jvm里运行的逻辑!从而达到一系列不可告人的目的呢?

    没错,我们可以,这就像同桌张三把作业给我让我帮忙交给老师,然后我直接把他的作业全部撕了然后告诉老师(JVM):张三这个崽种没做作业!(这是后面要讲的代理模式)。在当前的反射篇章我们可以理解为,我可以得到张三的作业的所有答案,然后我拿着自己用!

    好,例子来了,顺便我们熟悉一下Class对象的常用API,面试的时候就可以装逼了

    先看看我们的实体类是什么样子的

    	//一个public 属性
        public String name;
    
    	//一个private 属性
        private String price;
    
    	//一个public 构造方法
        public CarEntity(String name, String price) {
            this.name = name;
            this.price = price;
        }
        
    	//一个private 构造方法
        private CarEntity(String name){
            this.name = name;
        }
    
    
    	//以下全都是public 的GET,SET方法
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPrice() {
            return price;
        }
    
        public void setPrice(String price) {
            this.price = price;
        }
    
    

    好!开始测试!

        public static void main(String[] args) throws Exception {
            //获取CarEntity的Class对象
            Class carEntityClass = Class.forName("com.example.demo3.Entity.CarEntity");
    
            System.out.println("获取所有的Public的成员变量");
            Field[] field = carEntityClass.getFields();
            for (Field field1 : field) {
                System.out.println(field1.getName());
            }
            System.out.println("获取所有的的成员变量,不管你是Public,Private,Protected还是Default ");
            Field[] field01 = carEntityClass.getDeclaredFields();
            for (Field field1 : field01) {
                System.out.println(field1.getName());
            }
        }
    
    

    看看结果是什么

    获取所有的Public的成员变量
    name
    获取所有的的成员变量,不管你是Public,Private,Protected还是Default 
    name
    price
    
    

    好,再来一个

            System.out.println("获取所有的Public的构造方法");
            Constructor[] constructors = carEntityClass.getConstructors();
            for (Constructor constructor1 : constructors) {
                System.out.println(constructor1);
            }
            System.out.println("获取所有的的构造方法,不管你是Public,Private,Protected还是Default ");
            Constructor[] constructors01 = carEntityClass.getDeclaredConstructors();
            for (Constructor constructor1 : constructors01) {
                System.out.println(constructor1);
            }
    
    

    结果:

    获取所有的Public的构造方法
    public com.example.demo3.Entity.CarEntity(java.lang.String,java.lang.String)
    获取所有的的构造方法,不管你是Public,Private,Protected还是Default 
    public com.example.demo3.Entity.CarEntity(java.lang.String,java.lang.String)
    private com.example.demo3.Entity.CarEntity(java.lang.String)
    
    

    发现了没?我们现在只需要一个类的全路径,我们就可以掌握这个类的所有情况!

    上面的例子我们也发现了Class对象的APi的规律,只要加了Declared的Get方法,我们就能够“非法”地获取到这个类的编写者本来不愿意公布出来的属性!

    当然我们还可以获取到这个类的所有普通方法:

            System.out.println("获取所有的方法");
            Method[] methods = carEntityClass.getMethods();
            for (Method method : methods) {
                System.out.println(method.getName());
            }
    
    
    获取所有的方法
    getName
    setName
    getPrice
    setPrice
    wait
    wait
    wait
    equals
    toString
    hashCode
    getClass
    notify
    notifyAll
    
    

    我们再继续深入一点点,大家耐心看。

    我们先给我们的Car类补上刚刚忘掉的无参构造方法

    
        public CarEntity() {
    
        }
    
    

    然后开始我们的测试**(是干嘛呢?通过反射调用目标类的方法!)

     //获取CarEntity的Class对象
            Class carEntityClass = Class.forName("com.example.demo3.Entity.CarEntity");
            //通过Class对象获取到具体的CarEntity实例(需要无参构造方法!!!!)
            CarEntity carEntity = (CarEntity)carEntityClass.newInstance();
    
            System.out.println("获取SetName方法");
            //第一个参数:方法名称,第二个参数:方法形参的类型
            Method method = carEntityClass.getDeclaredMethod("setName",String.class);
            //第一个参数,对象类型carEntity,第二个参数是我这里调用方法时传的参数
            method.invoke(carEntity,"张三");
    
    
            System.out.println("获取getName方法");
            Method method2 = carEntityClass.getDeclaredMethod("getName",null);
            String name = (String) method2.invoke(carEntity,null);
            System.out.println(name);
    
    
    获取SetName方法
    获取getName方法
    张三
    
    

    我们现在居然只通过一个类的路径,获取到了这个类的所有信息,并且还能调用他的所有方法。

    现在是不是大概明白了,为什么一开始说反射是框架的灵魂。举个最简单的例子,Spring的注解式事务是怎么实现的?? 现在我们大概可以猜猜了(只是猜想):

  • 通过注解,我们在项目启动的时候可以获取所有打了注解的类或方法
  • 通过反射,我们可以获取类的所有信息或方法的所有信息
  • 通过反射,我们可以在方法的前后加上事务回滚相关的代码,然后通过上面例子中的invoke方法调用目标方法
  • 这个过程我不需要知道你这些类或方法是干嘛的,你的一切与我无关
  • 框架就是这样诞生的,更多的细节请看我的其他博客,关于静态代理和动态代理。

    好了 基本已经讲完,欢迎大家评论区指出不足,一起学习进步!

    大家看完了点个赞,码字不容易啊。。。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论