news 2026/5/14 15:20:31

Java基础全套教程(十)—— 反射机制详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java基础全套教程(十)—— 反射机制详解

Java基础全套教程(十)—— 反射机制详解

前言

反射是Java语言极具特色的核心特性,也是Java实现动态编程的基石。日常开发中使用的Spring框架、MyBatis框架、JUnit单元测试、动态代理等核心功能,底层全部依赖反射机制实现。

普通Java代码属于静态编码:编译阶段就固定了类、对象、方法的调用关系,运行时无法修改。而反射打破了编译期的限制,让程序在运行阶段可以动态解析类结构、创建对象、操作属性与方法。本节课将从原理、核心载体、实现方式、实操开发、性能优化、优缺点与实战应用,全方位详解反射机制。

一、反射机制核心概念

1.1 什么是反射

反射(Reflection)是Java提供的运行时自省与操作机制:程序在运行状态下,可以针对任意已加载的类,获取其全部内部结构信息,同时可以动态创建该类的实例、调用任意成员方法、修改任意成员变量

通俗理解:常规编码是“提前知道类,直接使用类”;反射是“运行时看透未知类,根据类的结构信息动态操作类”,哪怕是类中私有修饰的属性和方法,也可以通过反射突破权限限制进行操作。

1.2 反射的两大核心作用

反射的所有应用场景,都基于两大核心能力延伸而来:

  1. RTTI(运行时类型识别):程序运行过程中,动态识别对象真实类型、解析类的修饰符、构造器、属性、方法、父类、接口、注解等全部元信息。

  2. DC(动态组件装配):无需编译期绑定代码逻辑,运行时动态加载类、创建对象、调用方法、赋值属性,实现代码解耦与灵活扩展。

1.3 反射的核心价值

反射让Java从“静态固化语言”具备了“动态扩展能力”,核心价值体现在两点:

  • 低耦合高灵活:代码无需硬编码绑定具体类和方法,可通过配置、参数动态适配不同类,大幅提升代码通用性。

  • 框架底层支撑:几乎所有Java主流框架的核心功能都依赖反射实现,是进阶开发、源码阅读的必备知识点。

二、反射核心原理——Class对象

2.1 Class对象的本质

反射机制的唯一核心载体是java.lang.Class类,所有反射操作都必须基于Class对象完成。

当JVM加载一个类时,会在**方法区(元空间)**自动生成一个专属的Class对象,这个对象相当于当前类的“完整说明书”,存储了类的所有结构数据:构造方法、成员变量、成员方法、继承关系、注解信息等。

2.2 Class对象核心特性

  • 一个类在JVM整个生命周期中,有且仅有一个Class对象,无论实例化多少个对象,最终指向的Class对象完全一致。

  • 基本数据类型、数组、接口、枚举,都会对应专属的Class对象。

  • Class对象是反射的入口,没有获取Class对象,就无法执行任何反射操作。

2.3 类的加载与反射的关联

常规对象创建流程:编译生成class字节码文件 → JVM加载字节码 → 生成Class对象 → new创建实例对象。

反射对象创建流程:编译无需绑定类 → 运行时加载字节码 → 获取Class对象 → 动态创建实例、操作成员。

三、获取Class对象的三种方式(必备基础)

获取Class对象是所有反射操作的前提,Java提供三种标准方式,适配不同开发场景,下文统一使用全新的User实体类作为测试载体,全程独立案例,无原版重复代码。

3.1 通用测试实体类

publicclassUser{// 私有成员变量privateStringusername;privateIntegerage;// 公共成员变量publicStringphone;// 无参构造方法publicUser(){}// 公共有参构造publicUser(Stringusername,Integerage){this.username=username;this.age=age;}// 私有有参构造privateUser(Stringphone){this.phone=phone;}// 普通公共方法publicvoidsayHello(){System.out.println("用户名:"+username+",年龄:"+age);}// 私有方法privatevoidupdateInfo(StringnewName){this.username=newName;System.out.println("用户名已更新为:"+newName);}// getter & setterpublicStringgetUsername(){returnusername;}publicvoidsetUsername(Stringusername){this.username=username;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.age=age;}}

3.2 三种获取Class对象的方式

方式一:实例对象.getClass() (运行时获取)

通过类的实例对象调用原生方法获取Class对象,必须先创建对象,适用于已有实例对象的场景。

publicclassGetClassByInstance{publicstaticvoidmain(String[]args){// 创建实例对象Useruser=newUser();// 调用getClass()获取Class对象Class<?extendsUser>clazz=user.getClass();// 打印类全限定名System.out.println("类全限定名:"+clazz.getName());}}

方式二:类名.class (编译期锁定,高效)

通过静态语法类名.class获取,无需创建对象,编译期即可识别,性能最优,适用于明确知道目标类的场景。

publicclassGetClassByStatic{publicstaticvoidmain(String[]args){// 静态语法获取Class对象Class<User>clazz=User.class;System.out.println("类名:"+clazz.getSimpleName());System.out.println("全限定类名:"+clazz.getName());}}

方式三:Class.forName() (动态加载,最常用)

通过Class类静态方法传入类全限定名动态加载类,无需提前知晓类,支持配置化加载,是框架开发中最常用的方式,需捕获编译异常。

publicclassGetClassByForName{publicstaticvoidmain(String[]args)throwsClassNotFoundException{// 传入类的全限定包名+类名Class<?>clazz=Class.forName("com.reflect.entity.User");System.out.println("加载的类:"+clazz.getName());}}

3.3 三种方式对比总结

  • 对象.getClass():依赖实例对象,仅运行时可用,无法提前预判类类型。

  • 类名.class:高效、无异常,编译期校验,适合固定类的反射操作。

  • Class.forName():支持动态配置、热加载,灵活性最高,框架核心首选方式。

四、反射核心实操操作

获取Class对象后,可通过Class类提供的API,实现对类中构造器、成员变量、成员方法的全部操作,核心区分两组API:

  • getXXX():仅获取public修饰的公共成员(包含父类公共成员)。

  • getDeclaredXXX():获取当前类所有权限成员(public、private、protected、默认权限,不含父类)。

4.1 反射操作构造方法

常用API说明

方法名功能描述
getConstructors()获取所有公共构造方法数组
getDeclaredConstructors()获取当前类所有权限构造方法数组
getConstructor(参数类型…)获取指定参数列表的公共构造方法
getDeclaredConstructor(参数类型…)获取指定参数列表的任意权限构造方法

实操代码:获取构造器并动态创建对象

importjava.lang.reflect.Constructor;publicclassReflectConstructorDemo{publicstaticvoidmain(String[]args)throwsException{Class<User>clazz=User.class;// 1.获取所有公共构造方法Constructor<?>[]publicConstructors=clazz.getConstructors();System.out.println("公共构造方法数量:"+publicConstructors.length);// 2.获取所有权限构造方法Constructor<?>[]allConstructors=clazz.getDeclaredConstructors();System.out.println("所有构造方法数量:"+allConstructors.length);// 3.通过公共有参构造创建对象Constructor<User>publicConstructor=clazz.getConstructor(String.class,Integer.class);Useruser1=publicConstructor.newInstance("张三",22);user1.sayHello();// 4.通过私有构造创建对象(需开启权限放行)Constructor<User>privateConstructor=clazz.getDeclaredConstructor(String.class);privateConstructor.setAccessible(true);// 关闭权限校验Useruser2=privateConstructor.newInstance("13800138000");System.out.println("私有构造创建对象手机号:"+user2.phone);}}

4.2 反射操作成员变量

常用API说明

方法名功能描述
getFields()获取所有公共成员变量数组
getDeclaredFields()获取当前类所有权限成员变量数组
getField(字段名)获取指定名称的公共成员变量
getDeclaredField(字段名)获取指定名称的任意权限成员变量

实操代码:获取、修改私有/公共字段值

importjava.lang.reflect.Field;publicclassReflectFieldDemo{publicstaticvoidmain(String[]args)throwsException{Class<User>clazz=User.class;Useruser=clazz.newInstance();// 1.操作公共字段FieldphoneField=clazz.getField("phone");phoneField.set(user,"13900139000");System.out.println("公共手机号:"+phoneField.get(user));// 2.操作私有字段FieldnameField=clazz.getDeclaredField("username");nameField.setAccessible(true);// 放行私有权限nameField.set(user,"李四");System.out.println("私有用户名:"+nameField.get(user));}}

4.3 反射操作成员方法

常用API说明

方法名功能描述
getMethods()获取所有公共方法(含父类)
getDeclaredMethods()获取当前类所有权限方法
getMethod(方法名,参数类型…)获取指定公共方法
getDeclaredMethod(方法名,参数类型…)获取指定任意权限方法

实操代码:调用公共、私有方法

importjava.lang.reflect.Method;publicclassReflectMethodDemo{publicstaticvoidmain(String[]args)throwsException{Class<User>clazz=User.class;Useruser=clazz.getDeclaredConstructor().newInstance();// 1.调用公共无参方法MethodhelloMethod=clazz.getMethod("sayHello");user.setUsername("王五");user.setAge(25);helloMethod.invoke(user);// 2.调用私有有参方法MethodupdateMethod=clazz.getDeclaredMethod("updateInfo",String.class);updateMethod.setAccessible(true);updateMethod.invoke(user,"王五更新");}}

4.4 获取类的拓展信息

通过Class对象还可以获取类的包名、父类、实现接口、类名等基础拓展信息,常用于通用工具类开发:

publicclassReflectClassInfoDemo{publicstaticvoidmain(String[]args){Class<User>clazz=User.class;// 获取简单类名System.out.println("简单类名:"+clazz.getSimpleName());// 获取全限定类名System.out.println("全限定类名:"+clazz.getName());// 获取包名System.out.println("包名:"+clazz.getPackage().getName());// 获取父类System.out.println("父类:"+clazz.getSuperclass().getSimpleName());// 获取实现的接口Class<?>[]interfaces=clazz.getInterfaces();for(Class<?>inter:interfaces){System.out.println("实现接口:"+inter.getSimpleName());}}}

五、关键方法:setAccessible() 权限放行

5.1 方法作用

setAccessible(boolean flag)是反射核心优化与权限放行方法,作用是开启/关闭Java权限安全校验

  • true:关闭安全校验,突破private、protected权限限制,可操作私有成员,同时减少校验开销、提升反射效率。

  • false(默认):开启安全校验,仅可操作public成员,私有成员直接报错。

5.2 使用注意事项

该方法仅对当前反射操作生效,不会修改类的原生权限,属于运行时临时放行,不会破坏代码编译期权限规则。

六、反射性能分析与优化

6.1 反射效率低的原因

反射性能远低于普通直接调用,核心原因:

  • 反射运行时需要动态解析字节码文件,解析类结构信息,消耗额外资源。

  • 默认开启权限安全校验,增加大量判断逻辑。

  • JVM无法对反射代码做编译期优化、指令重排。

6.2 性能测试对比(全新案例)

循环10万次执行赋值方法,对比普通调用与反射调用的耗时差异:

importjava.lang.reflect.Method;publicclassReflectPerformanceTest{publicstaticvoidmain(String[]args)throwsException{Useruser=newUser();intcount=100000;// 普通方法调用耗时longstart1=System.currentTimeMillis();for(inti=0;i<count;i++){user.setAge(20);}longend1=System.currentTimeMillis();System.out.println("普通调用耗时:"+(end1-start1)+"ms");// 反射方法调用耗时Class<User>clazz=User.class;MethodsetAgeMethod=clazz.getMethod("setAge",Integer.class);longstart2=System.currentTimeMillis();for(inti=0;i<count;i++){setAgeMethod.invoke(user,20);}longend2=System.currentTimeMillis();System.out.println("反射调用耗时:"+(end2-start2)+"ms");// 优化后反射耗时(关闭权限校验)setAgeMethod.setAccessible(true);longstart3=System.currentTimeMillis();for(inti=0;i<count;i++){setAgeMethod.invoke(user,20);}longend3=System.currentTimeMillis();System.out.println("优化后反射耗时:"+(end3-start3)+"ms");}}

6.3 反射性能优化方案

  1. 优先调用setAccessible(true)关闭安全校验,大幅提升效率。

  2. 缓存Class对象、Method、Field对象,避免循环中重复获取。

  3. 高频业务场景尽量少用反射,框架底层、通用工具类场景优先使用。

七、反射优缺点总结

7.1 优点

  • 动态灵活:运行时动态操作类,无需编译期绑定,适配配置化、通用化开发。

  • 突破权限限制:可操作类中私有成员,实现常规代码无法实现的功能。

  • 解耦性强:降低代码硬编码耦合度,提升代码复用性与扩展性。

  • 框架基石:支撑几乎所有主流Java框架的核心功能实现。

7.2 缺点

  • 性能较低:相较于直接调用,反射存在额外解析与校验开销。

  • 安全性降低:突破访问权限,破坏类的封装性,存在安全风险。

  • 代码可读性差:反射代码繁琐、逻辑隐晦,增加维护成本。

  • 编译期无法校验:类名、方法名、字段名错误仅在运行时报错,调试难度更高。

八、反射实战应用场景

反射不会用于普通业务代码开发,核心用于通用工具、框架底层、中间件开发,常见场景如下:

  1. Spring IoC容器:通过反射动态创建Bean对象、注入属性,实现依赖注入。

  2. ORM框架:MyBatis、Hibernate通过反射将数据库查询结果自动封装为实体对象。

  3. 单元测试:JUnit通过反射调用测试方法,无需手动创建对象调用。

  4. 动态代理:JDK动态代理底层依赖反射实现方法拦截与增强。

  5. 通用工具类:对象拷贝、属性对比、JSON解析实体封装等通用工具。

  6. 配置化加载:通过配置文件类路径,动态加载类并执行对应逻辑。

九、本章核心总结

  1. 反射是Java运行时动态解析、操作类结构的核心机制,赋予Java动态编程能力。

  2. 反射唯一核心入口是Class对象,一个类仅对应一个Class实例。

  3. 获取Class对象三种方式:对象.getClass()类名.classClass.forName()

  4. 反射核心操作:动态创建对象、操作构造器、读写字段、调用方法。

  5. setAccessible(true)可突破权限限制,同时优化反射性能。

  6. 反射灵活但性能较差、封装性弱,适合框架底层与通用工具开发,不适合高频业务场景。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/14 15:19:07

避坑指南:Python爬取立创商城LCSC价格时,如何应对动态加载与反爬?

实战避坑&#xff1a;Python爬取立创商城LCSC动态数据的进阶策略 当我们需要批量获取电子元件价格时&#xff0c;自动化爬取工具显得尤为重要。立创商城(LCSC)作为国内知名的电子元器件交易平台&#xff0c;其价格数据对采购决策具有重要参考价值。然而&#xff0c;与大多数现代…

作者头像 李华
网站建设 2026/5/14 15:18:08

【工具】TortoiseSVN 拉流,只保留指定目录,其他目录不要

你要的是“只保留指定目录、其他目录不要”&#xff0c;用 TortoiseSVN 的 Sparse Checkout&#xff08;稀疏检出&#xff09; 即可&#xff0c;有两种场景&#xff1a;还没拉过、已经全拉了想删掉多余目录。一、全新拉取&#xff08;推荐&#xff0c;最干净&#xff09;在本地…

作者头像 李华
网站建设 2026/5/14 15:17:51

沃尔玛调整企业岗:削减迁移约 1000 个,聚焦技术与 AI 资源整合

5 月 13 日&#xff0c;据《华尔街日报》消息&#xff0c;零售行业巨头沃尔玛正式公布企业岗位调整计划&#xff0c;将削减或迁移约 1000 个企业岗位&#xff0c;沃尔玛官方回应&#xff0c;这一举措的核心目的是整合公司在技术和人工智能领域的资源。当地时间周二&#xff0c;…

作者头像 李华
网站建设 2026/5/14 15:17:42

5个强大功能!Clipy:彻底改变你的macOS剪贴板使用体验

5个强大功能&#xff01;Clipy&#xff1a;彻底改变你的macOS剪贴板使用体验 【免费下载链接】Clipy Clipboard extension app for macOS. 项目地址: https://gitcode.com/gh_mirrors/cl/Clipy Clipy是一款专为macOS设计的免费剪贴板增强工具&#xff0c;它能帮你轻松管…

作者头像 李华
网站建设 2026/5/14 15:14:07

Godot游戏解包终极指南:3步提取.pck文件所有资源

Godot游戏解包终极指南&#xff1a;3步提取.pck文件所有资源 【免费下载链接】godot-unpacker godot .pck unpacker 项目地址: https://gitcode.com/gh_mirrors/go/godot-unpacker 你是否下载过Godot引擎开发的游戏&#xff0c;想要研究其中的美术资源、脚本代码或音效素…

作者头像 李华
网站建设 2026/5/14 15:12:05

关于CY类荧光染料的定制

CY 类荧光染料因其广的发射波长范围、光稳定性、高量子产率以及良好的生物相容性&#xff0c;备受科研工作者的青睐。但随着研究的不断深入与细化&#xff0c;标准商业化的 CY 类荧光染料在某些复杂应用场景下&#xff0c;逐渐暴露出适配性不足的问题。瑞禧小编现推出的 CY 类荧…

作者头像 李华