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

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

什么是反射

反射是框架的灵魂

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

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

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

  1. 任何数据类型(包括基本数据类型)都有一个"静态"的class属性
  2. Object(对象) ------> getClass();
  3. 通过Class类的静态方法:forName(String className)(常用)
java 复制代码
 		//方法一
        Class<CarEntity> 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文件!

请放大看下图。。。。

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

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

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

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

java 复制代码
	//一个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;
    }

好!开始测试!

java 复制代码
    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());
        }
    }

看看结果是什么

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

好,再来一个

java 复制代码
        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);
        }

结果:

java 复制代码
获取所有的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方法,我们就能够"非法"地获取到这个类的编写者本来不愿意公布出来的属性!

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

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

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

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

java 复制代码
    public CarEntity() {

    }

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

java 复制代码
 //获取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);
java 复制代码
获取SetName方法
获取getName方法
张三

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

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

  1. 通过注解,我们在项目启动的时候可以获取所有打了注解的类或方法
  2. 通过反射,我们可以获取类的所有信息或方法的所有信息
  3. 通过反射,我们可以在方法的前后加上事务回滚相关的代码,然后通过上面例子中的invoke方法调用目标方法
  4. 这个过程我不需要知道你这些类或方法是干嘛的,你的一切与我无关

框架就是这样诞生的,更多的细节请看我的其他博客,关于静态代理和动态代理。

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

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

相关推荐
y***13642 分钟前
【wiki知识库】07.用户管理后端SpringBoot部分
spring boot·后端·状态模式
CryptoPP4 分钟前
使用 KLineChart 这个轻量级的前端图表库
服务器·开发语言·前端·windows·后端·golang
小坏讲微服务24 分钟前
Spring Cloud Alibaba 整合 Scala 教程完整使用
java·开发语言·分布式·spring cloud·sentinel·scala·后端开发
过客随尘30 分钟前
Spring AOP以及事务详解(一)
spring boot·后端
老鼠只爱大米34 分钟前
Java设计模式之外观模式(Facade)详解
java·设计模式·外观模式·facade·java设计模式
vx_dmxq21139 分钟前
【微信小程序学习交流平台】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·微信小程序·小程序·idea
武子康39 分钟前
大数据-167 ELK Elastic Stack(ELK) 实战:架构要点、索引与排错清单
大数据·后端·elasticsearch
9号达人40 分钟前
优惠系统演进:从"实时结算"到"所见即所得",前端传参真的鸡肋吗?
java·后端·面试
wei_shuo1 小时前
openEuler 底座赋能:openGauss 数据库部署与性能实战评测
后端
用户4098170215101 小时前
Python 的基本类型
后端