提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、反射是什么?
- 二、反射有啥好处?
- 1. 没反射
- 2. 有反射
- 三、反射的常用方法
- 1. 获取 Class 对象:
- 2. 获取类的构造方法:
- 3. 获取类的字段信息:
- 4. 获取类的方法信息:
- 5. 创建对象和调用方法:
- 四、总结
前言
提示:小车车🚗准备出发。。。。。
今天咋们来搂一搂反射是啥?有啥用?各位请上车,系好安全带。。。。
提示:路途颠簸系好安全带!!!!
一、反射是什么?
当我们写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 {@Overridepublic void payOnline() {System.out.println("我已经点了外卖,正在使用微信支付");}
}public class AliPay implements Mtwm {@Overridepublic 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.publicFieldSystem.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.privateFieldSystem.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!}
}
这些是反射中常用的方法,通过这些方法,可以在运行时获取并操作类的信息,使得代码更加灵活和动态。还有很多方法啊,我就不挨个列举了。
四、总结
好啦,以上就是本次对于反射的讲解。安全到站,下车!!!对于我的驾驶技术苟同的兄弟姐妹们,点点赞👍。祝各位身体健康、吃嘛嘛香!!!