Java核心知识体系1:泛型机制详解

Java核心知识体系2:注解机制详解

Java核心知识体系3:异常机制详解

Java核心知识体系4:AOP原理和切面应用

1 介绍

无论是那种语言体系,反射都是必不可少的一个技术特征。从Java体系来说,很多常用的技术框架或多或少都使用到了反射技术,比如Spring、MyBatis、RocketMQ、FastJson 等等。反射技术强大而必要,在大多数框架中起到举足轻重的作用。所以,反射也是Java必不可少的核心技术之一。

接下来我们来看看反射的一些技术要点:

  1. 反射的概念(即什么是反射)?
  2. 反射的作用(它帮我们解决了哪些问题)?
  3. 反射的实现原理?
  4. 如何使用反射?

    下面我就针对以上的疑问,一一来讲解。

1.1 反射是什么?

Java反射(Reflection)是Java语言的一个核心特性,它允许运行中的Java代码对自身进行自我检查,甚至修改自身的组件。具体来说,反射机制提供了在运行状态中,对于任意一个类,都能够了解这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法在Java中就叫做反射。

一句话总结:反射就是在运行时才具体知晓要操作的类是什么结构,并在运行时获取类的完整构造,并调用对应的方法、属性等。

Java的反射主要包括以下三个部分:

  • 类的加载:Java的类在需要使用时才会被加载到JVM中。这个过程是由类加载器(ClassLoader)完成的。类加载器首先检查这个类是否已经被加载过,如果还没有加载,那么就会从磁盘上加载类的字节码并创建一个Class对象。
  • 获取类的信息:当一个对象被创建后,我们可以使用反射来获取这个对象的Class对象。通过这个Class对象,我们可以获取到这个类的所有属性和方法。
  • 方法的调用:通过反射,我们可以动态的调用一个对象的方法。即使这个方法是一个私有的方法,也能够通过反射来调用。

1.2 为什么要用反射?

Java Reflection功能非常强大,并且非常有用,比如:

  • 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
  • 获取任意对象的属性,并且能改变对象的属性
  • 调用任意对象的方法
  • 判断任意一个对象所属的类
  • 实例化任意一个类的对象
  • 通过反射我们可以实现动态装配,降低代码的耦合度,实现动态代理等。

具体的应用场景:

  • 框架设计:许多框架,如Spring,Hibernate等,都大量使用了反射来实现对象的自动装配,动态代理等功能。
  • 单元测试:单元测试框架(如JUnit)会使用反射来调用被注解的方法。
  • 插件化:为了实现插件化,可以通过反射加载不同的插件。
  • 对象序列化与反序列化:在对象进行序列化和反序列化的时候,会使用反射获取到对象的所有属性和方法。

2 反射的使用

在Java中,Class类与java.lang.reflect类库配合对反射技术进行了完整的支持。在反射的Package中,我们经常使用功能类如下:

  • Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象
  • Field类表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)
  • Method类表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private)

下面将对这几个类进行详细介绍。

2.1 反射创建类对象

一般情况下我们通过反射创建类对象主要有两种方式:

  • 通过 Class 对象的 newInstance() 方法

  • 通过 Constructor 对象的 newInstance() 方法

  • 通过 Class 对象的 newInstance() 方法实现

Class clz = Class.forName("com.ad.reflection.TestRefle");
TestRefle tr= (TestRefle)clz.newInstance();
  • 通过 Constructor 对象的 newInstance() 方法实现
Class clz = Class.forName("com.ad.reflection.TestRefle");
Constructor constructor = clz.getConstructor();
TestRefle tr= (TestRefle)constructor.newInstance();

这边需要注意,通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。

下面的代码演示的是通过 Constructor 调用有参构造方法进行了类对象初始化:

Class clz = Class.forName("com.ad.reflection.TestRefle");
Constructor constructor = clz.getConstructor(String.class);
TestRefle tr= (TestRefle)constructor.newInstance("提供一个String参数");

接下来我们继续,通过具体的API获取详细的类信息:类信息、方法信息、属性信息等。

2.2 获取Class类对象

 // 获取Class对象的三种方式
根据类名: Class mailClass = MailInfo.class;
根据对象: Class mailClass = new MailInfo().getClass();
根据全限定类名: Class mailClass = Class.forName("com.ad.MailInfo"); // 根据对象获取信息和实例对象
获取全限定类名: mailClass.getName();
获取类名: mailClass.getSimpleName();
实例化: userClass.getDeclaredConstructor().newInstance();

更加详细Class类获取参考如下:

方法 用途
forName() (1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。 (2)为了产生Class引用,forName()立即就进行了初始化。
Object-getClass() 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName() 取全限定的类名(包括包名),即类的完整名字。 getSimpleName() 获取类名(不包括包名)
getCanonicalName() 获取全限定的类名(包括包名)
isInterface() 判断Class对象是否是表示一个接口
getInterfaces() 返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss() 返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance() 返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。

2.3 获取类的成员变量的信息

Field[] fields = _class.getDeclaredFields();

更加详细成员变量获取参考如下:

方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获取所有的公有的属性对象
getDeclaredField(String name) 获得某个属性对象(public和非public)
getDeclaredFields() 获得所有属性对象(public和非public)

2.4 获得类方法

Method[] methods = _class.getDeclaredMethods();

更加详细方法获取参考如下:

方法 用途
getMethod(String name, Class...<?> paramerterTypes) 获得某个公有的方法对象
getMethods() 获取所有的公有的方法对象
getDeclaredMethod(String name, Class...<?> paramerterTypes) 获得对应类下某个方法(public和非public)
getDeclaredMethods() 获得对应类下所有方法(public和非public)

2.5 获得构造函数

Constructor[] constructors = _class.getDeclaredConstructors();

更加详细构造函数获取参考如下:

方法 用途
getConstructor(Class...<?> paramerterTypes) 获得该类中与参数类型匹配的公有构造方法
getConstructors() 获取该类的所有公有构造方法
getDeclaredConstructor(Class...<?> paramerterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获取该类的所有构造方法

这样通过反射就可以做在运行时获取类的完整构造,并获得类信息了。

类名 用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(即类的属性)
Method类 代表类的方法
Constructor类 代表类的构造函数

通过上面的几个示例我们基本了解了反射的使用,但这仅仅是使用,我们还需深入理解反射背后的底层实现原理。

3 反射原理分析

3.1 反射的调用流程

1、编写完Java项目之后,java文件都会被编译成一个.class文件

2、这些class文件在程序运行时会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中自动产生一个Class对象。

3、通过Class对象获取 Field(属性)、Method(方法)、Construcor(构造函数)

我们平时通过new的形式创建对象,本质上就是通过Class来创建个新对象



通过上面的流程我们可以看出反射的优势:

  • 动态装配

我们的程序在运行时,可能不一定会用到所有我们编写和构建的类,这样避免启动时间太长并且浪费大量无用的机器资源。

取而代之的是动态的加载一些类,这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载。

  • 降低耦合

    如果你在使用new时明确的指定类名,那这就是典型的硬编码实现,而在使用反射的时候,可以只传入类名参数,就可以生成对象,降低了耦合度,使得程序更具灵性。

完整的调用流程,图片来自网上,比较模糊,后续再补一个

3.2 反射的应用场景

  • 框架设计:许多框架,如Spring,Hibernate,mybatis,dubbo,rocketmq等,都大量使用了反射来实现对象的自动装配,动态代理等功能。
  • 单元测试:单元测试框架(如JUnit)会使用反射来调用被注解的方法。
  • 插件化:为了实现插件化,可以通过反射加载不同的插件。
  • 对象序列化与反序列化:在对象进行序列化和反序列化的时候,会使用反射获取到对象的所有属性和方法。
  • 动态配置、动态代理:通过反射去读取配置,以及代理请求

4 反射经典案例解析

以下案例来自百度文心一言大模型自动生成,已调试通过。

import java.lang.reflect.Method;  

public class ReflectionExample {
public static void main(String[] args) {
try {
// 获取目标类的Class对象
Class<?> targetClass = Class.forName("java.util.ArrayList"); // 获取目标类的所有公共方法
Method[] methods = targetClass.getMethods(); // 遍历所有方法并打印方法名
for (Method method : methods) {
System.out.println(method.getName());
} // 获取特定方法,比如添加元素的add方法
Method addMethod = targetClass.getMethod("add", Object.class); // 创建目标类的实例对象
Object targetObject = targetClass.newInstance(); // 调用add方法添加元素
addMethod.invoke(targetObject, "Hello, World!"); // 获取目标类的所有属性(字段)并打印属性名
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

这个案例展示了如何使用反射来获取目标类的Class对象,获取并打印目标类的所有公共方法,获取特定方法,创建目标类的实例对象,调用目标类的方法,以及获取并打印目标类的所有属性(字段)。

总结

无论是那种语言体系(C#、Java等等),反射都是必不可少的一个技术特征。而从Java体系来说,很多常用的技术框架或多或少都使用到了反射技术,比如Spring、MyBatis、RocketMQ、FastJson 等等。

学习好Java 反射技术能帮助你更好的理解底层调用的原理,也有助于设计更加 轻巧、高内聚、低耦合 的业务框架。

Java核心知识体系5:反射机制详解的更多相关文章

  1. Java核心知识1:泛型机制详解

    1 理解泛型的本质 JDK 1.5开始引入Java泛型(generics)这个特性,该特性提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,即给类型指定一个参 ...

  2. Java 反射机制详解(下)

    续:Java 反射机制详解(上) 三.怎么使用反射 想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属 ...

  3. Java 反射机制详解(上)

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

  4. 转 Java虚拟机5:Java垃圾回收(GC)机制详解

    转 Java虚拟机5:Java垃圾回收(GC)机制详解 Java虚拟机5:Java垃圾回收(GC)机制详解 哪些内存需要回收? 哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无 ...

  5. Java核心知识体系3:异常机制详解

    1 什么是异常 异常是指程序在运行过程中发生的,由于外部问题导致的运行异常事件,如:文件找不到.网络连接失败.空指针.非法参数等. 异常是一个事件,它发生在程序运行期间,且中断程序的运行. Java ...

  6. Java中反射机制详解

    序言 在学习java基础时,由于学的不扎实,讲的实用性不强,就觉得没用,很多重要的知识就那样一笔带过了,像这个马上要讲的反射机制一样,当时学的时候就忽略了,到后来学习的知识中,很多东西动不动就用反射, ...

  7. Java反射机制详解

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

  8. java反射机制详解 及 Method.invoke解释

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

  9. Java 反射机制详解

    动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化.比如众所周知的ECMAScript(JavaScript)便是一个动态语言.除此之外如Ru ...

  10. Java反射机制详解(3) -java的反射和代理实现IOC模式 模拟spring

    IOC(Inverse of Control) 可翻译为“控制反转”,但大多数人都习惯将它称为“依赖注入”.在Spring中,通过IOC可以将实现类.参数信息等配置在其对应的配置文件中,那么当 需要更 ...

随机推荐

  1. 1 opencv-python图像读写模块

    这个分类记录自己学习opencv的随笔文档,方便以后查询和复习.python-opencv环境配置网上教程很多,此处就不做赘述了,该文档记录opencv最基础的图像读写和显示,工具是jupyter n ...

  2. Linux相关概念及操作

    目录 linux的文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录"/",然后在此目录下再创建其他的目录. 1./bin 是Binary的缩写,这个目录存放着最经常使 ...

  3. Hugging News #0717: 开源大模型榜单更新、音频 Transformers 课程完成发布!

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...

  4. 2023年郑州轻工业大学校赛邀请赛clk

    需要总结的地方挺多的,首先是题目一次通过率有待提高,对于一些特别的样例还要加以分析,算法熟练的不高,不能清晰的看出在哪道题考什么算法,就比如兔子爱吃萝卜那道题,就是一个背包问题,比较基础,但是我们团队 ...

  5. pe文件对齐

    PE中规定了三类对齐:数据在内存中的对齐. 数据在文件中的对齐.资源文件资源数据的对齐. 1.内存对齐 由于windows操作系统对内存属性的设置以也为单位,所以通常情况下,节在内存中的对齐单位必须至 ...

  6. 【MAUI Blazor踩坑日记】4.只在特定平台上引用包

    其实这个并不是MAUI特有的问题,只是在MAUI中可能会遇到. 例如某个包只在Windows上有用,但打包的时候,安卓.ios也会把这个包计算在内 所以需要在不是特定平台时把它排除. 万幸从微软文档找 ...

  7. 源码解析Collections.sort ——从一个逃过单测的 bug 说起

    本文从一个小明写的bug 开始,讲bug的发现.排查定位,并由此展开对涉及的算法进行图解分析和源码分析. 事情挺曲折的,因为小明的代码是有单测的,让小明更加笃定自己写的没问题.所以在排查的时候,也经历 ...

  8. Linux 概念:u-boot

    U-Boot介绍 参考:https://baike.baidu.com/item/U-Boot/10377075 参考:https://u-boot.readthedocs.io/en/latest/ ...

  9. 2021-7-6 VUE笔记

    v-cloak:使用的display:none: 直到编译完成后开始显示: v-text和插值表达式,非必要响应式用v-text会比较好,使用插值表达式要加上v-cloak; v-html:不推荐使用 ...

  10. C# 中的 数组[]、ArrayList、List

    C# 中的 数组[].ArrayList.List 数组 在 C# 中,数组实际上是对象,而不只是如在 C 和 C++ 中的连续内存的可寻址区域. 属性: 数组可以是一维.多维或交错的. 创建数组实例 ...