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

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

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

什么是反射

反射是框架的灵魂

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

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

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

  1. 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  2. Object(对象) ——> getClass();
  3. 通过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的注解式事务是怎么实现的?? 现在我们大概可以猜猜了(只是猜想):

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

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

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


相关文章
|
1月前
|
消息中间件 存储 缓存
大厂面试高频:Kafka 工作原理 ( 详细图解 )
本文详细解析了 Kafka 的核心架构和实现原理,消息中间件是亿级互联网架构的基石,大厂面试高频,非常重要,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:Kafka 工作原理 ( 详细图解 )
|
10天前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
2天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
1天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
27天前
|
Java 程序员
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
小米,29岁程序员,分享了一次面试经历,详细解析了Java中&和&&的区别及应用场景,展示了扎实的基础知识和良好的应变能力,最终成功获得Offer。
66 14
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
1月前
|
Java 编译器 程序员
Java面试高频题:用最优解法算出2乘以8!
本文探讨了面试中一个看似简单的数学问题——如何高效计算2×8。从直接使用乘法、位运算优化、编译器优化、加法实现到大整数场景下的处理,全面解析了不同方法的原理和适用场景,帮助读者深入理解计算效率优化的重要性。
36 6
|
1月前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。