无论是那种语言体系,反射都是必不可少的一个技术特征。从Java体系来说,很多常用的技术框架或多或少都使用到了反射技术,比如Spring、MyBatis、RocketMQ、FastJson 等等。反射技术强大而必要,在大多数框架中起到举足轻重的作用。所以,反射也是Java必不可少的核心技术之一。
接下来我们来看看反射的一些技术要点:
Java反射(Reflection)是Java语言的一个核心特性,它允许运行中的Java代码对自身进行自我检查,甚至修改自身的组件。具体来说,反射机制提供了在运行状态中,对于任意一个类,都能够了解这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法在Java中就叫做反射。
一句话总结:反射就是在运行时才具体知晓要操作的类是什么结构,并在运行时获取类的完整构造,并调用对应的方法、属性等。
Java的反射主要包括以下三个部分:
Java Reflection功能非常强大,并且非常有用,比如:
具体的应用场景:
在Java中,Class类与java.lang.reflect类库配合对反射技术进行了完整的支持。在反射的Package中,我们经常使用功能类如下:
下面将对这几个类进行详细介绍。
一般情况下我们通过反射创建类对象主要有两种方式:
Class clz = Class.forName("com.ad.reflection.TestRefle");TestRefle tr= (TestRefle)clz.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获取详细的类信息:类信息、方法信息、属性信息等。
// 获取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对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。 |
Field[] fields = _class.getDeclaredFields();
更加详细成员变量获取参考如下:
方法 | 用途 |
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获取所有的公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象(public和非public) |
getDeclaredFields() | 获得所有属性对象(public和非public) |
Method[] methods = _class.getDeclaredMethods();
更加详细方法获取参考如下:
方法 | 用途 |
getMethod(String name, Class...<?> paramerterTypes) | 获得某个公有的方法对象 |
getMethods() | 获取所有的公有的方法对象 |
getDeclaredMethod(String name, Class...<?> paramerterTypes) | 获得对应类下某个方法(public和非public) |
getDeclaredMethods() | 获得对应类下所有方法(public和非public) |
Constructor[] constructors = _class.getDeclaredConstructors();
更加详细构造函数获取参考如下:
方法 | 用途 |
getConstructor(Class...<?> paramerterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获取该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> paramerterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获取该类的所有构造方法 |
这样通过反射就可以做在运行时获取类的完整构造,并获得类信息了。
类名 | 用途 |
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(即类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造函数 |
通过上面的几个示例我们基本了解了反射的使用,但这仅仅是使用,我们还需深入理解反射背后的底层实现原理。
1、编写完Java项目之后,java文件都会被编译成一个.class文件
2、这些class文件在程序运行时会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中自动产生一个Class对象。
3、通过Class对象获取 Field(属性)、Method(方法)、Construcor(构造函数)
我们平时通过new的形式创建对象,本质上就是通过Class来创建个新对象
通过上面的流程我们可以看出反射的优势:
我们的程序在运行时,可能不一定会用到所有我们编写和构建的类,这样避免启动时间太长并且浪费大量无用的机器资源。
取而代之的是动态的加载一些类,这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载。
完整的调用流程,图片来自网上,比较模糊,后续再补一个
以下案例来自百度文心一言大模型自动生成,已调试通过。
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 反射技术能帮助你更好的理解底层调用的原理,也有助于设计更加 轻巧、高内聚、低耦合 的业务框架。