提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:小车车🚗准备出发。。。。。
今天咋们来搂一搂反射是啥?有啥用?各位请上车,系好安全带。。。。
提示:路途颠簸系好安全带!!!!
一、反射是什么?
当我们写Java程序时,通常情况下,我们会在编译时确定所有的类、方法和属性。但是有些时候,我们希望在程序运行时动态地获取和操作类的信息,这就是反射。
反射是指在运行时动态地获取类的信息并操作类的成员。
在Java中,每个类都有一个对应的Class
对象,这个对象包含了类的信息,如类的名称、属性、方法等。反射机制允许我们在运行时获取这个Class
对象,进而获取和操作类的信息。
⚠️这里给大家提个小问题:为什么Java只能在程序运行阶段才能动态获取类的信息并操作类的成员呢?
原因如下:
- 类加载: 在 Java 程序运行时,JVM 负责加载类的字节码文件。这个过程称为类加载。类加载器将字节码加载到内存中,并创建对应的
Class
对象,该对象包含了类的信息。 - 实例化对象: 通过
Class
对象,可以实例化类的对象。反射允许在运行时创建类的对象,而不需要在编译时知道类的确切类型。 - 动态获取信息: 通过
Class
对象,可以获取类的各种信息,包括类的名称、构造方法、字段、方法等。这使得程序在运行时能够根据需要动态地获取和操作类的信息。
总而言之,当一个类(.class文件)被classloader加载到内存后,它会将.class文件里面的内容加载到Metaspace中,并且会生成一个类对象。这个类对象存储在Metaspace中,它充当了访问类的结构和内容的入口,包括静态字段、方法等。
有兴趣的同学可以去学学JVM的相关知识。
二、反射有啥好处?
对于干干净净的小白同学,先绕道去第三部分了解一下反射的常用方法再来品味好吃的🌰,会更有感觉哦。
反射的好处咱们就举个有反射和没反射的例子对比说明一下子。
首先,定义一个支付接口 Mtwm
:
// Mtwm.java
public interface Mtwm {
void payOnline();
}
然后,实现两个支付方式类,分别是 WeChat
和 AliPay
:
// WeChat.java
public class WeChat implements Mtwm {
@Override
public void payOnline() {
System.out.println("我已经点了外卖,正在使用微信支付");
}
}
public class AliPay implements Mtwm {
@Override
public void payOnline() {
System.out.println("我已经点了外卖,正在使用支付宝支付");
}
}
1. 没反射
接下来,创建一个测试类 Test
,用于模拟前台选择支付方式:
// Test.java
public class Test {
public static void main(String[] args) {
String str = "微信"; // 模拟前台支付方式选择
if ("微信".equals(str)) {
pay(new WeChat());
}
if ("支付宝".equals(str)) {
pay(new AliPay());
}
}
public static void pay(Mtwm paymentMethod) {
paymentMethod.payOnline();
}
}
以上是使用多态的方式,已经不错了哦。但在实际应用中,若是我又增加了一个 “银行卡支付” 诸君又该如何应对 ? 我举手🙋,我回答。
无非就两步嘛:
1、创建一个新类,implement Mtwm,重写payOnline方法
2、在多加一个if的判断分支。
但是当我们引入反射机制,会发生什么神奇的事情呢?
2. 有反射
// Demo.java
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws Exception {
// 模拟前台支付方式选择
String str = "com.zhaoss.test01.AliPay"; // 实际上是支付类的全限定路径
// 利用反射
Class<?> cls = Class.forName(str);
Object instance = cls.newInstance();
// 获取 payOnline 方法并调用
Method method = cls.getMethod("payOnline");
method.invoke(instance);
}
}
各位有没有发现,通过使用反射,可以在不修改测试类的情况下引入新的支付方式。这样我们就只要创建一个新类(implement Mtwm,重写payOnline方法),然后提供新支付方式的实现类并传入其全路径名,而无需修改测试类。是不是很哇塞!!!
三、反射的常用方法
哥哥们,原谅我懒。我把输出写到注释里了,不好看的话可以直接搞到本地跑一下子。
1. 获取 Class 对象:
通过对象的 getClass()
方法或者类名的 .class
字面量可以获取类的 Class
对象。
public class Example {
public static void main(String[] args) {
// 通过对象的getClass()方法获取Class对象
String str = "Hello, Reflect!";
Class<?> classObj1 = str.getClass();
System.out.println(classObj1.getName()); // 输出:java.lang.String
// 通过类名的.class字面量获取Class对象
Class<?> classObj2 = String.class;
System.out.println(classObj2.getName()); // 输出:java.lang.String
}
}
2. 获取类的构造方法:
通过 getConstructors()
方法获取类的所有公共构造方法,通过 getDeclaredConstructors()
获取所有构造方法(包括私有的)。
import java.lang.reflect.Constructor;
public class Example {
public Example(String message) {
System.out.println("Constructor with message: " + message);
}
public static void main(String[] args) throws Exception {
Class<?> clazz = Example.class;
// 获取所有公共构造方法
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("Public Constructor: " + constructor);
//输出:Public Constructor: public SystemStudy.Example(java.lang.String)
}
// 获取所有构造方法(包括私有的)
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : allConstructors) {
System.out.println("All Constructor: " + constructor);
//输出:
//All Constructor: public SystemStudy.Example(java.lang.String)
//All Constructor: private SystemStudy.Example(java.lang.String,java.lang.Integer)
}
}
}
3. 获取类的字段信息:
通过 getFields()
方法获取类的所有公共字段,通过 getDeclaredFields()
获取所有字段(包括私有的)。
import java.lang.reflect.Field;
public class Example {
public int publicField;
private String privateField;
public static void main(String[] args) {
Class<?> clazz = Example.class;
// 获取所有公共字段
Field[] fields = clazz.getFields();
for (Field field : fields) {
//输出:Public Field: public int SystemStudy.Example.publicField
System.out.println("Public Field: " + field);
}
// 获取所有字段(包括私有的)
Field[] allFields = clazz.getDeclaredFields();
for (Field field : allFields) {
//输出:
//All Field: public int SystemStudy.Example.publicField
//All Field: private java.lang.String SystemStudy.Example.privateField
System.out.println("All Field: " + field);
}
}
}
4. 获取类的方法信息:
通过 getMethods()
方法获取类的所有公共方法,通过 getDeclaredMethods()
获取所有方法(包括私有的)。
这里还有好多的Object包含的方法(wait、equals、hashCode等),太多了,我就写了两个关键的。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Example {
public void publicMethod() {
System.out.println("Public Method");
}
private void privateMethod() {
System.out.println("Private Method");
}
public static void main(String[] args) {
Class<?> clazz = Example.class;
// 获取所有公共方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//输出:Public Method: public void SystemStudy.Example.publicMethod()
System.out.println("Public Method: " + method);
}
// 获取所有方法(包括私有的)
Method[] allMethods = clazz.getDeclaredMethods();
for (Method method : allMethods) {
//输出
//All Method: public void SystemStudy.Example.publicMethod()
//All Method: private void SystemStudy.Example.privateMethod()
System.out.println("All Method: " + method);
}
}
}
5. 创建对象和调用方法:
通过 newInstance()
方法创建类的实例,并通过 invoke()
方法调用类的方法。
import java.lang.reflect.Method;
public class Example {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
public static void main(String[] args) throws Exception {
Class<?> clazz = Example.class;
// 创建类的实例
Object obj = clazz.newInstance();
// 获取方法并调用
Method method = clazz.getMethod("sayHello", String.class);
method.invoke(obj, "Reflect"); // 输出:Hello, Reflect!
}
}
这些是反射中常用的方法,通过这些方法,可以在运行时获取并操作类的信息,使得代码更加灵活和动态。还有很多方法啊,我就不挨个列举了。
四、总结
好啦,以上就是本次对于反射的讲解。安全到站,下车!!!对于我的驾驶技术苟同的兄弟姐妹们,点点赞👍。祝各位身体健康、吃嘛嘛香!!!