深入剖析Java中的反射,由浅入深,层层剥离!

2024-02-02 09:12

本文主要是介绍深入剖析Java中的反射,由浅入深,层层剥离!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在开头

之前更新了不少Java的基础知识,比如Java的类、对象、基础类型、关键字、序列化、泛型、值传递等等,今天要上点深度了,来聊一聊Java中的 反射 !

所谓反射,就是在运行时分析、检查和操作类、接口、方法、属性的行为!

简单感受一下反射

在开始详解反射之前,我们先通过一段代码,简单的感受一下反射的魅力!首先,我们已经写了一个Person类,类中有age和name属性,并提供了set/get方法。这时候分别通过正常的实例化对象调用,和反射调用去操作对象的属性值!

【代码示例1】

	//Java正射实现Person person = new Person();person.setName("JavaBuild-正射");System.out.println(person.getName());//Java反射实现//获取类的class对象Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");//通过class获取类的元数据-setName/getName方法Method setName = classa.getMethod("setName", String.class);Method getName = classa.getMethod("getName");//获取默认空构造方法Constructor constructor = classa.getConstructor();//通过newInstance()进行实例化Object o = constructor.newInstance();//通过invoke方法进行方法调用setName.invoke(o,"JavaBuild-反射");System.out.println(getName.invoke(o));

以上为Java正射与反射的对比实现代码。从代码中我们可以看到,正常通过实例化对象后再调用相应方法的正射方式比通过反射的方式代码量要少很多,并且逻辑更加清晰明确。

使用场景

反射被称之为框架的灵魂,在Java中被大量使用在框架、动态代理、注解等场景下;

  • 开发框架:Spring、SpringBoot、Mybatis、Hibernate等框架中使用了反射
  • 动态代理:在面向切面编程中,需要拦截特定的方法,就会选择动态代理的方式,而动态代理的底层技术就是反射。
  • 注解:注解本身只是起到一个标记符的作用,它需要利用反射机制,根据标记符去执行特定的行为。比如@Component注解就声明了一个类为 Spring Bean ,通过@Value注解读取配置文件中的值,这些的底层都是通过反射来实现需求。

实现过程

我们基于上述代码示例1中的反射进行分步分析,彻底搞明白反射的实现与使用!
【步骤一】
通过如下这句代码获取反射类的Class对象

Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");

可别小看这简简单单的一行代码,这里其实涉及到3个知识点,我们来扩展一下。

知识点一:什么是Class对象?
这里提到的Class和之间了解的用来标识类的class关键字并不一样!Class也是一个类,存放在java.lang包中,它的作用是:编译时生成一个类的Class对象,这个对象中包含了类的结构信息,如类名、继承父类、实现的接口、方法、属性等等,Class对象保存在编译后的.class文件中。

在我们new一个对象或者引用静态成员变量时,会将Class对象加载到JVM中,JVM解释器去分析Class对象获取到类的结构信息,创建我们需要的实例对象或者提供静态成员变量的引用值。

因为类的Class对象时存储在class文件中,所以不管实例化多少个对象,都有且仅有一个Class对象!因为Class类对象本身的特性,所以它可以在运行时操作类,这对反射来说无疑是天赐良缘!

知识点二:获取Class对象的几种方式?

1、在已知具体类的情况下

Class person= Person.class;

2、通过Class.forName()传入类的全路径获取,代码示例1中采用该方式

Class classa = Class.forName("com.javabuild.server.pojo.Person");

3、对象实例instance.getClass()获取

Person person = new Person();
Class aClass = person.getClass();

4、通过类加载器xxxClassLoader.loadClass()传入类路径获取

Class aClass = ClassLoader.getSystemClassLoader().loadClass("com.javabuild.server.pojo.Person");

知识点三:java.lang.reflect包中常用的反射类
在java.lang.reflect包中存着几个反射常用的类,大概的罗列如下,注意,Class类其实是放在java.lang中的。

Class:代表一个类或接口,包含了类的结构信息(如名称、构造函数、方法、字段等)。通过 Class 对象,可以获取类的元数据并操作类的实例。
Constructor:代表类的构造方法,用于创建类的实例。
Method:代表类的方法,可以通过它调用类的实例方法。
Field:代表类的字段,可以获取或修改字段的值。
Modifier:包含方法、字段和类的访问修饰符(如 public、private 等)。

【步骤二】
通过Class对象获取构造方法

Constructor constructor = classa.getConstructor();

除了这种获取默认无参构造的方式,获取构造方法还可以通过如下这些方式:

getConstructor():返回反射类的特定 public 构造方法,可以传递参数,参数为构造方法参数对应 Class 对象;缺省的时候返回默认构造方法。
getDeclaredConstructor():返回反射类的特定构造方法,不限定于 public 的。
getConstructors():返回类的所有 public 构造方法。
getDeclaredConstructors():返回类的所有构造方法,不限定于 public 的。

【代码示例2】

Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");
Constructor constructor01 = classa.getConstructor();
System.out.println(constructor);
System.out.println("---------------");
Constructor<?>[] constructors = classa.getConstructors();
for (Constructor<?> constructor02 : constructors) {System.out.println(constructor02);
}
System.out.println("----------------");
Constructor declaredConstructor = classa.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);

【控制台输出】

public com.javabuild.server.pojo.Person()
---------------
public com.javabuild.server.pojo.Person()
public com.javabuild.server.pojo.Person(int)
public com.javabuild.server.pojo.Person(int,java.lang.String)
----------------
public com.javabuild.server.pojo.Person(int)

【步骤三】
通过 Constructor 对象初始化反射类对象

//通过newInstance()进行实例化
Object o = constructor.newInstance();

【步骤四】
获取要调用的方法的 Method 对象

//通过class获取类的元数据-setName/getName方法
Method setName = classa.getMethod("setName", String.class);
Method getName = classa.getMethod("getName");

【步骤五】
通过invoke方法进行方法调用

setName.invoke(o,"JavaBuild-反射");

以上即为反射使用的全过程!下面贴一下全量代码。

Person类

public class Person {private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}//无参构造public Person() {}//一个参数构造public Person(int age) {this.age = age;}//2个参数构造public Person(int age, String name) {this.age = age;this.name = name;}
}

反射测试类

public class Test {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//Java正射实现Person person = new Person();person.setName("JavaBuild-正射");System.out.println(person.getName());//Java反射实现//获取类的class对象Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");//通过class获取类的元数据-setName/getName方法Method setName = classa.getMethod("setName", String.class);Method getName = classa.getMethod("getName");//获取默认空构造方法Constructor constructor = classa.getConstructor();//通过newInstance()进行实例化Object o = constructor.newInstance();//通过invoke方法进行方法调用setName.invoke(o,"JavaBuild-反射");System.out.println(getName.invoke(o));//获取默认空构造方法Constructor constructor01 = classa.getConstructor();System.out.println(constructor);System.out.println("---------------");Constructor<?>[] constructors = classa.getConstructors();for (Constructor<?> constructor02 : constructors) {System.out.println(constructor02);}System.out.println("----------------");Constructor declaredConstructor = classa.getDeclaredConstructor(int.class);System.out.println(declaredConstructor);}
}

输出

JavaBuild-正射
JavaBuild-反射
public com.javabuild.server.pojo.Person()
---------------
public com.javabuild.server.pojo.Person()
public com.javabuild.server.pojo.Person(int)
public com.javabuild.server.pojo.Person(int,java.lang.String)
----------------
public com.javabuild.server.pojo.Person(int)

优缺点

基于上面的内容,我们可以对java中的反射做一个总结吧

优点
1、可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利。
缺点
1、破坏封装:由于反射允许访问私有字段和私有方法,所以可能会破坏封装而导致安全问题。
2、性能开销:由于反射涉及到动态解析,因此无法执行 Java 虚拟机优化,再加上反射的写法的确要复杂得多,所以性能要比“正射”差很多,在一些性能敏感的程序中应该避免使用反射。

这篇关于深入剖析Java中的反射,由浅入深,层层剥离!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/670219

相关文章

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

浅析Java中如何优雅地处理null值

《浅析Java中如何优雅地处理null值》这篇文章主要为大家详细介绍了如何结合Lambda表达式和Optional,让Java更优雅地处理null值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录场景 1:不为 null 则执行场景 2:不为 null 则返回,为 null 则返回特定值或抛出异常场景

SpringMVC获取请求参数的方法

《SpringMVC获取请求参数的方法》:本文主要介绍SpringMVC获取请求参数的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下... 目录1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、@RequestParam4、@

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT