【反射】Java反射 全方位知识体系(附 应用场景 + 《八股文常考面试题》)
文章目录Java反射一、基础概念1. 定义2. 核心原理二、核心类库三、基本操作1. 获取 Class 对象的三种方式2. 实例化对象3. 访问字段4. 调用方法5. 操作构造器四、高级特性1. 反射与泛型2. 反射与注解3. 动态代理五、应用场景1. 框架开发2. 注解处理3. 动态扩展4. 调试与工具六、优缺点分析1. 优点2. 缺点七、注意事项与最佳实践1. 性能优化2. 安全问题3. 代码规范4. 版本兼容性Java反射核心应用场景一、框架开发最核心场景1. Spring IoC控制反转容器2. Spring AOP面向切面编程3. MyBatis ORM映射二、注解处理1. 单元测试框架JUnit2. 自定义注解实现业务逻辑三、动态扩展与插件化1. Java SPIService Provider Interface机制2. 插件化开发四、动态代理五、调试与工具类1. IDE代码补全与类结构查看2. 反射工具库六、实际业务场景1. 动态加载配置类2. 对象拷贝BeanUtils3. JSON序列化/反序列化总结反射的核心价值Java反射-八股文常考面试题一、基础概念篇1. 什么是Java反射2. 反射的核心原理是什么3. 获取Class对象的三种方式高频二、核心操作篇1. 如何通过反射实例化对象2. 如何访问/修改字段Field3. 如何调用方法Method4. setAccessible(true)的作用与风险三、应用场景篇1. 反射在框架中的应用高频2. 动态代理与反射的关系3. 注解处理如JUnit Test四、优缺点与性能篇1. 反射的优缺点2. 反射为什么慢如何优化五、进阶原理篇1. 泛型擦除后如何通过反射获取泛型信息2. JDK动态代理 vs CGLIB动态代理高频3. Java 9模块系统对反射的影响Java反射一、基础概念1. 定义Java反射是指在运行时动态获取类的元信息如类的结构、方法、字段、构造器等并能动态操作类或对象的能力如实例化对象、调用方法、修改字段值。它是Java动态性的核心体现。2. 核心原理Class对象反射的入口。每个类被加载后JVM会自动生成一个java.lang.Class对象该对象包含了类的完整元数据。类加载机制反射依赖于JVM的类加载过程加载→链接→初始化通过Class对象可逆向访问类的结构信息。二、核心类库反射主要依赖java.lang和java.lang.reflect包下的类核心类如下类名作用说明java.lang.Class反射的入口类代表类的元数据可获取类的构造器、方法、字段等信息。Constructor代表类的构造方法可用于实例化对象。Method代表类的方法可用于动态调用方法。Field代表类的字段成员变量可用于动态获取或修改字段值。Modifier工具类用于解析类、方法、字段的修饰符如public、static、final。Array工具类用于动态创建和访问数组。ParameterizedType代表参数化类型如ListString可获取泛型的实际类型参数。Annotation代表注解可通过反射获取类、方法、字段上的注解信息。三、基本操作1. 获取 Class 对象的三种方式类名.class编译时确定最安全如String.class。对象.getClass()通过实例获取如hello.getClass()。Class.forName(“全限定类名”)动态加载需处理ClassNotFoundException如Class.forName(java.util.ArrayList)。2. 实例化对象通过Class.newInstance()调用无参构造器Java 9后过时推荐用Constructor。通过Constructor.newInstance(Object... initargs)可调用有参构造器需先获取Constructor对象。3. 访问字段获取字段getField(String name)获取public字段包括父类。getDeclaredField(String name)获取所有声明的字段包括私有不包括父类。操作字段值get(Object obj)获取字段值。set(Object obj, Object value)设置字段值。私有字段需调用setAccessible(true)打破封装。4. 调用方法获取方法getMethod(String name, Class?... parameterTypes)获取public方法包括父类。getDeclaredMethod(String name, Class?... parameterTypes)获取所有声明的方法包括私有不包括父类。调用方法invoke(Object obj, Object... args)执行方法静态方法obj传null。私有方法需调用setAccessible(true)。5. 操作构造器getConstructor(Class?... parameterTypes)获取public构造器。getDeclaredConstructor(Class?... parameterTypes)获取所有构造器包括私有。四、高级特性1. 反射与泛型由于Java泛型在编译时会类型擦除运行时需通过反射获取泛型信息Field.getGenericType()返回字段的泛型类型如ParameterizedType。ParameterizedType.getActualTypeArguments()获取泛型的实际类型参数如ListString中的String。2. 反射与注解可通过反射获取类、方法、字段上的注解getAnnotation(ClassT annotationClass)获取指定类型的注解。getAnnotations()获取所有注解包括继承的。getDeclaredAnnotations()获取直接声明的注解不包括继承的。3. 动态代理反射是动态代理的基础核心类Proxy用于创建代理对象。InvocationHandler处理代理方法的调用逻辑。示例Proxy.newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h)。五、应用场景1. 框架开发Spring IoC通过反射实例化Bean读取配置文件中的类名并动态加载。Spring AOP基于动态代理反射实现实现方法拦截。MyBatis通过反射映射SQL结果到Java对象调用Mapper接口方法。2. 注解处理JUnit通过反射识别Test注解并执行测试方法。自定义注解结合反射实现权限校验、日志记录等功能。3. 动态扩展SPI机制ServiceLoader通过反射加载配置文件中定义的实现类。插件化开发动态加载外部Jar包中的类并调用其方法。4. 调试与工具IDE通过反射提供代码补全、类结构查看等功能。反射工具类如Apache Commons Lang的FieldUtils、MethodUtils。六、优缺点分析1. 优点动态性运行时才确定类和方法提高代码灵活性。通用性可编写通用代码处理不同类如框架的通用工具。解耦减少硬编码便于扩展和维护。2. 缺点性能开销比直接调用慢涉及动态类型解析、安全检查频繁调用需缓存反射对象。安全风险可访问私有成员破坏封装性需合理控制权限。可读性差反射代码晦涩难懂调试和维护成本高。七、注意事项与最佳实践1. 性能优化缓存Class、Constructor、Method、Field对象避免重复获取。尽量减少setAccessible(true)的使用或仅在初始化时调用一次。2. 安全问题Java 9模块系统中若模块未opens给其他模块setAccessible会抛出InaccessibleObjectException需在module-info.java中声明opens。合理使用SecurityManagerJava 17后默认禁用限制反射权限。3. 代码规范避免过度使用反射能用直接调用则不用反射。处理反射异常如IllegalAccessException、InvocationTargetException避免吞异常。4. 版本兼容性关注Java版本对反射的调整如Java 9模块系统、Java 16对非法反射访问的警告升级为错误。Java反射核心应用场景反射的核心价值在于运行时动态性与解耦能力以下是其最经典、最常用的落地场景一、框架开发最核心场景1. Spring IoC控制反转容器作用通过反射动态实例化Bean并管理依赖注入避免硬编码。原理读取XML配置/注解如Component、Bean中的类全限定名。调用Class.forName()加载类通过Constructor.newInstance()实例化对象。通过反射调用setter方法或直接注入字段Field.set()完成依赖装配。示例!-- Spring XML配置 --beaniduserServiceclasscom.example.UserServicepropertynameuserDaorefuserDao//beanSpring内部通过反射解析上述配置动态创建UserService并注入UserDao。2. Spring AOP面向切面编程作用基于动态代理反射实现实现方法拦截用于日志、事务、权限控制等横切关注点。原理JDK动态代理通过Proxy.newProxyInstance()创建代理对象InvocationHandler.invoke()内部用反射调用目标方法。CGLIB动态代理通过字节码生成子类重写方法时用反射调用父类原方法。3. MyBatis ORM映射作用将SQL查询结果自动映射到Java对象无需手动set字段。原理通过反射获取实体类的所有字段Class.getDeclaredFields()。根据字段名匹配SQL结果集的列名。调用Field.set()将列值注入到对象字段私有字段需setAccessible(true)。示例// MyBatis Mapper接口Select(SELECT id, name FROM user WHERE id #{id})UserselectUserById(intid);MyBatis内部通过反射调用该接口方法并将结果映射为User对象。二、注解处理1. 单元测试框架JUnit作用自动识别并执行带Test注解的方法。原理扫描测试类通过反射获取所有方法Class.getDeclaredMethods()。检查方法是否标注TestMethod.isAnnotationPresent(Test.class)。对标注方法通过Method.invoke()执行测试。2. 自定义注解实现业务逻辑场景权限校验、日志记录、参数校验等。示例自定义RequirePermission注解结合反射实现接口权限控制// 自定义注解Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceRequirePermission{Stringvalue();// 所需权限}// 切面/拦截器中通过反射校验publicvoidcheckPermission(Methodmethod){if(method.isAnnotationPresent(RequirePermission.class)){StringrequiredPermmethod.getAnnotation(RequirePermission.class).value();// 校验当前用户是否拥有requiredPerm权限}}三、动态扩展与插件化1. Java SPIService Provider Interface机制作用动态加载外部实现类实现框架的可扩展性。原理在META-INF/services目录下定义接口文件内容为实现类全限定名。ServiceLoader通过反射读取文件调用Class.forName()加载实现类并实例化。示例JDBC驱动加载、Dubbo扩展点加载均基于SPI。2. 插件化开发场景IDE插件、应用市场插件、模块化系统。原理动态加载外部Jar包URLClassLoader。通过反射获取插件类调用约定的接口方法如Plugin.execute()。四、动态代理作用在不修改原代码的情况下对方法进行增强如日志、监控、事务。分类JDK动态代理基于接口通过Proxy和InvocationHandler实现核心是反射。CGLIB动态代理基于继承通过字节码生成子类内部也依赖反射调用父类方法。JDK动态代理示例// 目标接口publicinterfaceUserService{voidaddUser();}// 目标实现类publicclassUserServiceImplimplementsUserService{publicvoidaddUser(){System.out.println(添加用户);}}// 调用处理器publicclassLogHandlerimplementsInvocationHandler{privateObjecttarget;publicLogHandler(Objecttarget){this.targettarget;}publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println(前置日志);Objectresultmethod.invoke(target,args);// 反射调用目标方法System.out.println(后置日志);returnresult;}}// 创建代理对象UserServiceproxy(UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),UserServiceImpl.class.getInterfaces(),newLogHandler(newUserServiceImpl()));proxy.addUser();// 调用代理方法五、调试与工具类1. IDE代码补全与类结构查看原理IDE通过反射加载项目类获取类的方法、字段、构造器等信息实时展示给开发者。2. 反射工具库场景简化反射操作避免重复代码。示例Apache Commons Lang的FieldUtils、MethodUtils封装了字段/方法的获取、设置、调用等操作。Spring的ReflectionUtils提供findField()、invokeMethod()等便捷方法。六、实际业务场景1. 动态加载配置类场景根据配置文件动态切换数据源、策略类等。示例StringstrategyClassconfig.getProperty(payment.strategy);// 从配置读取类名PaymentStrategystrategy(PaymentStrategy)Class.forName(strategyClass).newInstance();strategy.pay();// 动态调用策略方法2. 对象拷贝BeanUtils原理通过反射获取源对象的所有字段将值复制到目标对象的对应字段。示例Spring的BeanUtils.copyProperties(source, target)、Apache Commons BeanUtils的BeanUtils.copyProperties()。3. JSON序列化/反序列化原理序列化通过反射获取对象的所有字段将字段名和值转换为JSON。反序列化通过反射实例化对象根据JSON键名匹配字段并注入值。示例Jackson、Gson等JSON库的核心实现均依赖反射。总结反射的核心价值价值点说明动态性运行时才确定类、方法、字段无需编译期硬编码。解耦减少类之间的直接依赖提高代码扩展性如框架可插拔、策略动态切换。通用性编写通用代码处理不同类如ORM映射、对象拷贝、JSON序列化。Java反射-八股文常考面试题一、基础概念篇1. 什么是Java反射Java反射是指在运行时动态获取类的元信息如构造器、方法、字段、注解等并能动态操作类或对象的能力实例化对象、调用方法、修改字段值。它是Java动态性的核心体现。2. 反射的核心原理是什么每个类被JVM加载后会自动生成一个java.lang.Class对象该对象包含类的完整元数据结构、方法、字段等。反射通过Class对象逆向访问类的信息无需在编译期确定具体类。3. 获取Class对象的三种方式高频方式示例特点类名.classString.class编译期确定最安全对象.getClass()hello.getClass()通过实例获取Class.forName(“全限定名”)Class.forName(java.util.ArrayList)动态加载需处理异常二、核心操作篇1. 如何通过反射实例化对象方式1Class.newInstance()Java 9过时调用无参构造器。方式2Constructor.newInstance(Object... args)可调用有参构造器推荐。ConstructorUserconstructorUser.class.getDeclaredConstructor(String.class,int.class);constructor.setAccessible(true);// 若构造器私有需打破封装Useruserconstructor.newInstance(张三,25);2. 如何访问/修改字段Field获取字段getField(String name)获取public字段含父类。getDeclaredField(String name)获取所有声明字段含私有不含父类。操作字段get(Object obj)获取字段值。set(Object obj, Object value)设置字段值。私有字段需调用setAccessible(true)。3. 如何调用方法Method获取方法getMethod(String name, Class?... paramTypes)获取public方法含父类。getDeclaredMethod(String name, Class?... paramTypes)获取所有声明方法含私有不含父类。调用方法invoke(Object obj, Object... args)执行方法静态方法obj传null。私有方法需调用setAccessible(true)。4.setAccessible(true)的作用与风险作用打破Java的访问修饰符限制可访问私有成员构造器、方法、字段。风险破坏封装性可能导致对象状态不一致。Java 9模块系统中若模块未opens给其他模块会抛出InaccessibleObjectException。三、应用场景篇1. 反射在框架中的应用高频Spring IoC读取配置文件/注解中的类名通过反射实例化Bean并管理依赖。Spring AOP基于动态代理反射实现实现方法拦截如日志、事务。MyBatis通过反射将SQL结果映射到Java对象调用Mapper接口方法。2. 动态代理与反射的关系动态代理的核心是反射JDK动态代理通过Proxy.newProxyInstance()创建代理对象内部依赖InvocationHandler的invoke()方法通过反射调用目标方法。3. 注解处理如JUnitTestJUnit通过反射扫描测试类识别Test注解的方法然后通过反射调用这些方法执行测试。四、优缺点与性能篇1. 反射的优缺点优点缺点动态性运行时确定类和方法灵活性高性能开销比直接调用慢通用性可编写通用代码处理不同类安全风险可访问私有成员破坏封装解耦减少硬编码便于扩展可读性差代码晦涩维护成本高2. 反射为什么慢如何优化慢的原因运行时动态类型解析、安全检查如访问修饰符校验。每次调用反射方法都需重新查找元数据。优化方式缓存Class、Constructor、Method、Field对象避免重复获取。尽量减少setAccessible(true)的使用或仅在初始化时调用一次。五、进阶原理篇1. 泛型擦除后如何通过反射获取泛型信息Java泛型在编译期会类型擦除但可通过以下方式获取Field.getGenericType()返回字段的泛型类型如ParameterizedType。ParameterizedType.getActualTypeArguments()获取泛型的实际类型参数如ListString中的String。2. JDK动态代理 vs CGLIB动态代理高频维度JDK动态代理CGLIB动态代理实现方式基于反射要求目标类实现接口基于继承通过字节码生成子类限制只能代理实现接口的类可代理普通类不能是final类性能略低反射调用略高字节码生成Spring默认选择目标类实现接口时使用目标类未实现接口时使用3. Java 9模块系统对反射的影响模块系统Project Jigsaw引入module-info.java若模块未通过opens关键字将包开放给其他模块反射调用setAccessible(true)会抛出InaccessibleObjectException。解决方式在module-info.java中声明opens 包名 to 目标模块;。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2445096.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!