百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

Java之反射_java反射如何实现

wptr33 2025-02-21 15:38 17 浏览

前言:

在 Java语言中,反射是一种强大而优秀的机制,通过反射,我们可以在运行时检查和修改类、接口、字段和方法的信息,甚至动态地创建对象、调用方法和访问私有成员。

可以毫不夸张地说,没有反射,很多优秀的框架不复存在,没有这些优秀的框架(比如Spring),Java可能会逊色很多,因此,这篇文章,我们一起来深入探讨Java反射以及其背后的原理。

什么是反射?

  1. Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  2. 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

Java反射最核心的类位于JDK源码 java.lang.reflect包下,比如Class、Constructor、Field 和 Method等,他们提供了对类和对象运行时信息进行检查和操作的方法。JDK源码截图如下:

反射的原理

反射的原理主要可以从下面 4个点来阐述:

  • 类加载:当 Java程序运行时,类加载器会根据类的名称查找并加载类的字节码文件,然后将字节码文件转换为可执行的 Java类,并将其存储在运行时数据区域的方法区中。
  • 创建 Class对象:在类加载过程中,Java虚拟机会自动创建对应的Class对象,Class对象包含了类的元数据信息,并提供了访问和操作类的接口。
  • 获取 Class对象:Class对象通过多种方式获取,最常见的方式有 3种: 类的 .class属性、类实例的 getClass()方法、Class.forName()。
  • 访问和操作:通过Class对象获取类的字段、方法、构造函数等信息,使用Field类和Method类来访问和操作字段和方法,甚至可以调用私有的字段和方法。

发射的特点

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

如何使用反射?

了解了Java反射原理,我们再通过一个例子来演示如何使用它,如下示例,通过反射给 Person类中的 greet()方法传入一个 name变量,并打印结果:

public class ReflectionDemo {


    class Person{
        private void getName(String name){
            System.out.println("Hi,My City name is " + name +"~");
        }
    }

    //通过反射获取person中getName属性
    public static void main(String[] args) throws Exception {
        //获取类的Class对象
        Class clazz = Person.class;
        //获取类的名称
        String className = clazz.getName();
        System.out.println("Class name" +className);
        //获取类的修饰符
        int modifiers = clazz.getModifiers();
        System.out.println("Modifiers" +modifiers);
        //获取类的方法信息
        Method[] methods = clazz.getDeclaredMethods();
        System.out.println("Methods");
        for (Method method : methods) {
            System.out.println("Method name" +method.getName());
        }
        //创建类的实例
        Object obj = clazz.getDeclaredConstructor().newInstance();
        //调用类的方法
        Method getMethod = clazz.getDeclaredMethod("getName", String.class);
        getMethod.setAccessible(true);
        getMethod.invoke(obj,"nanjing");
    }

}

过程分析:

  • 首先,在示例代码通过获Person.class取了 Person的Class对象;
  • 然后,使用clazz.getName()获取了类的名称,通过clazz.getModifiers()获取了类的修饰符,并打印输出;
  • 接下来,通过clazz.getDeclaredMethods()获取类的所有方法,并依次打印输出方法的名称;
  • 接着,通过clazz.getDeclaredConstructor().newInstance()方法创建了 Person 的实例;
  • 再接着,使用clazz.getDeclaredMethod()方法获取了 greet()方法的引用。为了调用私有方法,我们需要调用setAccessible(true)来设置方法的可访问性。
  • 最后,使用Method.invoke()方法调用了 greet()方法,传递参数name = Java。

运行结果如下图:

反射优缺点

上面内容的讲解已经侧面反映出了Java反射的一些优点,这里再详细的总结下反射的优缺点:

优点:

  • 动态性:反射允许我们在运行时动态地获取和操作类的信息,而不需要在编译时确定。这为编写灵活的、可扩展的代码提供了便利。
  • 灵活性:通过反射,我们可以绕过访问修饰符的限制,访问和修改私有成员、调用私有方法等。这为我们在特殊情况下进行一些高级操作提供了可能。
  • 框架开发:反射在开发框架和库时非常有用。通过反射,框架可以动态地加载和实例化类,解析注解,处理回调等。这为框架提供了更大的灵活性和可扩展性。
  • 调试和探索:反射使得我们可以在运行时探索代码背后的信息,例如获取类的结构、方法、字段等。这对于调试和理解复杂的代码非常有帮助。

缺点:

  • 性能开销:相比于直接调用代码,使用反射会带来更高的性能开销。反射涉及到动态查找、方法调用等操作,这些操作比直接调用代码更加耗时。因此,在对性能要求较高的场景下,过度使用反射可能导致性能下降。
  • 安全性和稳定性:反射打破了封装性和类型安全性,通过反射,我们可以绕过访问修饰符的限制,调用私有方法等,这可能导致代码的不稳定性和安全隐患。因此,使用反射时需要格外小心,确保代码的正确性和稳定性。

为什么需要反射?

反射机制在 Java中的作用不言而喻,下面列举了反射机制的一些常见场景和原因:

运行时类型检查:反射机制允许在运行时获取类的信息,包括字段、方法和构造方法等。因此,在进行运行时类型检查,以确保代码在处理不同类型的对象时能够正确地进行操作。

动态创建对象:通过反射,可以在运行时动态地创建对象,而不需要在编译时知道具体的类名。这对于某些需要根据条件或配置来创建对象的情况非常有用,例如工厂模式或依赖注入框架。

访问和修改私有成员:反射机制可以绕过访问权限限制,访问和修改类的私有字段和方法。虽然这破坏了封装性原则,但在某些特定情况下,这种能力可以帮助我们进行一些特殊操作,例如单元测试、调试或框架的内部实现。

动态调用方法:反射机制允许我们在运行时动态地调用类的方法,甚至可以根据运行时的条件来选择不同的方法。这对于实现插件化系统、处理回调函数或实现动态代理等功能非常有用。

框架和库的实现:许多Java框架和库在其实现中广泛使用了反射机制。它们利用反射来自动发现和加载类、实现依赖注入、处理注解、配置文件解析和动态代理等。反射机制使得这些框架和库更加灵活和扩展。

常用框架

很多优秀的框架内部都使用了Java反射,这里重点讲解下给 Java打下半壁江山的 Spring生态(Spring Framework,Spring MVC,SpringBoot, SpringCloud...),以 Spring Framework为例:

  • 依赖注入(Dependency Injection) : 依赖注入,可以把程序员主动创建对象的事情交给 Spring管理,大大提升了对象创建的灵活性。当我们在配置文件或用注解定义 Bean时,Spring会使用反射来动态地实例化对象,并将依赖的其他对象注入到这些实例中。
  • 自动装配(Autowired) : 当 Spring容器启动时,它会扫描应用程序中的所有类,并使用反射来查找和识别带有 @Autowired注解的字段、方法或构造函数。再自动将 Bean注入到需要的位置,实现对象之间的自动连接。
  • AOP(Aspect-Oriented Programming) : AOP 利用了动态代理和反射机制。通过定义切面(Aspect)和切点(Pointcut),Spring可以在运行时使用反射来创建代理对象,从而实现横切关注点(cross-cutting concerns)的功能,如日志记录、事务管理等。
  • 动态代理(Dynamic Proxy) : Spring利用 Java反射机制动态地创建代理对象,并在代理对象中添加额外的逻辑,从而实现对目标对象的增强。

总结

java反射,我们可以更好的理解一些优秀框架运行机制的同时,也能更好的运用框架到我们的项目中。

相关推荐

redis的八种使用场景

前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...

基于Redis的3种分布式ID生成策略

在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...

基于OpenWrt系统路由器的模式切换与网页设计

摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...

这篇文章教你看明白 nginx-ingress 控制器

主机nginx一般nginx做主机反向代理(网关)有以下配置...

如何用redis实现注册中心

一句话总结使用Redis实现注册中心:服务注册...

爱可可老师24小时热门分享(2020.5.10)

No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...

Apportable:拯救程序员,IOS一秒变安卓

摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...

JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透

以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...

3月26日更新 快速施法自动施法可独立设置

2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...

Redis 是如何提供服务的

在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...

lua _G、_VERSION使用

到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...

China's top diplomat to chair third China-Pacific Island countries foreign ministers' meeting

BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...

移动工作交流工具Lua推出Insights数据分析产品

Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...

Redis 7新武器:用Redis Stack实现向量搜索的极限压测

当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...

Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求

重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...