反射讲解(有图有真相)

发布时间:2023年12月29日

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:小车车🚗准备出发。。。。。

今天咋们来搂一搂反射是啥?有啥用?各位请上车,系好安全带。。。。


提示:路途颠簸系好安全带!!!!

一、反射是什么?

当我们写Java程序时,通常情况下,我们会在编译时确定所有的类、方法和属性。但是有些时候,我们希望在程序运行时动态地获取和操作类的信息,这就是反射。

反射是指在运行时动态地获取类的信息并操作类的成员。

在Java中,每个类都有一个对应的Class对象,这个对象包含了类的信息,如类的名称、属性、方法等。反射机制允许我们在运行时获取这个Class对象,进而获取和操作类的信息。

??这里给大家提个小问题:为什么Java只能在程序运行阶段才能动态获取类的信息并操作类的成员呢?

原因如下:

  1. 类加载: 在 Java 程序运行时,JVM 负责加载类的字节码文件。这个过程称为类加载。类加载器将字节码加载到内存中,并创建对应的 Class 对象,该对象包含了类的信息。
  2. 实例化对象: 通过 Class 对象,可以实例化类的对象。反射允许在运行时创建类的对象,而不需要在编译时知道类的确切类型。
  3. 动态获取信息: 通过 Class 对象,可以获取类的各种信息,包括类的名称、构造方法、字段、方法等。这使得程序在运行时能够根据需要动态地获取和操作类的信息。

总而言之,当一个类(.class文件)被classloader加载到内存后,它会将.class文件里面的内容加载到Metaspace中,并且会生成一个类对象。这个类对象存储在Metaspace中,它充当了访问类的结构和内容的入口,包括静态字段、方法等。
在这里插入图片描述
有兴趣的同学可以去学学JVM的相关知识。

二、反射有啥好处?

对于干干净净的小白同学,先绕道去第三部分了解一下反射的常用方法再来品味好吃的🌰,会更有感觉哦。
反射的好处咱们就举个有反射和没反射的例子对比说明一下子。
首先,定义一个支付接口 Mtwm

// Mtwm.java
public interface Mtwm {
    void payOnline();
}

然后,实现两个支付方式类,分别是 WeChatAliPay

// 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!
    }
}

这些是反射中常用的方法,通过这些方法,可以在运行时获取并操作类的信息,使得代码更加灵活和动态。还有很多方法啊,我就不挨个列举了。

四、总结

好啦,以上就是本次对于反射的讲解。安全到站,下车!!!对于我的驾驶技术苟同的兄弟姐妹们,点点赞👍。祝各位身体健康、吃嘛嘛香!!!

文章来源:https://blog.csdn.net/weixin_54574094/article/details/135291248
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。