当前位置: 主页 > JAVA语言

反射 java-java反射invoke

发布时间:2023-02-08 22:33   浏览次数:次   作者:佚名

java反射invoke_反射 java_java通过反射创建对象

反射

网上看java反射很模糊。 今天,我将通过我的理解来详细解释一下。 相信这篇文章会让你重拾学习的信心。

一句话:反射可以绕过jvm编译阶段。 可以动态添加代码。 例如,如果一个对象没有确定,可以在运行时动态确定。 还可以(部分)调用未完全实现的对象的方法。

很抽象? 等我们通过下面的例子讲解完之后,再看看这个概念反射 java,顿时豁然开朗! ! !

有反射则有正字法,正字法是代码中预先新建的对象()

如果我需要实例化一个学生对象,代码将如下所示。

Students user = new Students();

某天突然冒出一个场景需求,就是需要实例化teacher和school两个对象。 在代码编译阶段,对象的创建不确定,需要动态创建。 代码是这样的:


//动态创建实体
public  T getClass(String param) {
    Object T = null;
    if (param.equals("student")) {
        T = new Students();
    } else if (param.equals("teacher")) {
        T = new Teacher();
    } else if (param.equals("school")) {
        T = new School();
    }
    return (T) T;
}

通过传入参数param来决定使用哪个实体。 您可以通过在项目运行时动态传入参数来决定使用哪个实体。 看看这个是不是有点像设计模式中的工厂模式。 是的,这也是应用场景之一。

java通过反射创建对象_java反射invoke_反射 java

然后,通过这个例子,简单总结一下

简要总结

在代码运行之前,我们不确定以后会使用哪种数据结构。 我们只在程序运行时决定使用哪个数据类,而反射可以在程序运行过程中动态获取类信息和调用类方法。 通过反射构造类实例,代码最终会演化成下面这样。

public  T getPoJo(String className) throws Exception {
    Class clazz = Class.forName(className);
    return (T) clazz.newInstance();
}

接下来,让我们深入了解一下,看看如何使用吧!

反射的基本使用

Java 反射有 4 个主要组件:

在网上找了一张图片放在下面。 如果使用反射,这四个核心类是分不开的。 只有了解它们提供什么信息,它们的功能是什么,才能方便地使用它们。

java反射invoke_java通过反射创建对象_反射 java

当我们学习反射的基本使用时,我将使用一个学生类作为模板来说明。 首先,让我们熟悉一下这个类的基本组成部分:属性、构造函数和方法

public class Students {
    public String name;
    public int age;
    private double weight; // 体重只有自己知道
    
    public Students() {}
    
    public Students(String name, int age) {
        this.name = name;
        this.age = age;

java反射invoke_反射 java_java通过反射创建对象

} public void getInfo() { System.out.print("["+ name + " 的年龄是:" + age + "]"); } }

反射的用法有很多,常用的功能如下:

获取类的Class对象

在 Java 中,每个类都有自己的 Class 对象。 当我们编写.java文件,用javac编译后,会生成一个字节码文件.class,里面包含了字节码文件中的class。 所有的信息,比如属性、构造方法、方法……当字节码文件被加载到虚拟机中执行时,会在内存中生成一个Class对象,其中包含了该类的所有内部信息。 此信息何时可用。

获取Class对象的方式有3种:

Class clazz = Students.class;

Students sp = new Students();
Class clazz = sp.getClass();

Class clazz = Class.forName("com.bean.Students");

构造函数类的实例化对象

通过反射构造类的实例有两种方式:

Class clazz = Class.forName("com.bean.Students");
Students stu = (Students) clazz.newInstance();
stu.getInfo();
// [null 的年龄是:0]

即使Students明确定义了构造函数,在newInstance()创建的实例中,所有属性值都是对应类型的初始值,因为newInstance()构造的实例会调用默认的无参数构造函数。

Class clazz = Class.forName("com.bean.Students");
Constructor constructor = clazz.getConstructor(String.class, int.class);
constructor.setAccessible(true);

java反射invoke_反射 java_java通过反射创建对象

Students stu = (SmallPineapple) constructor.newInstance("苏世", 25); stu.getInfo(); // [苏世 的年龄是:25]

通过 Class 对象调用 newInstance() 将采用默认的无参数构造方法。 如果要通过显式构造方法构造实例,需要提前从Class中调用getConstructor()方法获取对应的构造函数,通过构造函数实例化对象。

这些API是开发中最常遇到的,当然还有很多重载的方法。 由于本文篇幅较长,如果每个方法都一一讲解,我们也记不住了,所以在使用的时候再归类一下。 在里面搜索就够了

获取一个类的所有信息

java反射invoke_反射 java_java通过反射创建对象

这里我就不细说了。 大家在使用的时候可以根据图片进行查询,因为我们主要讲的是面试,面试不会问这么细的问题。

通过反射调用方法

通过反射获取到一个Method类对象后,调用invoke方法即可执行。

可以如下通过反射实例化一个对象,然后获取Method方法对象,调用invoke()指定Students的getInfo()方法。

Class clazz = Class.forName("com.bean.Students");
Constructor constructor = clazz.getConstructor(String.class, int.class);
constructor.setAccessible(true);
Students st = (Students) constructor.newInstance("苏世", 25);
Method method = clazz.getMethod("getInfo");
if (method != null) {
    method.invoke(st, null);
}
// [苏世的年龄是:25]

反射的应用场景

通过上面的解释,我们可以回忆总结一下:

以下是反射的三种常见应用场景:

java通过反射创建对象_java反射invoke_反射 java

让我们一一解释以上场景

Spring的IOC容器

在Spring中,经常会写一个上下文配置文件applicationContext.xml,里面包含了bean的配置。 程序启动时,会读取xml文件,解析出所有的标签,将对象实例化到IOC容器中。



    
        
        
    

定义完以上文件后,通过ClassPathXmlApplicationContext加载配置文件。 程序启动时,Spring会将配置文件中的所有bean实例化,并放入IOC容器中。 IOC 容器本质上是一个工厂。 通过这个factory传入标签的id属性来获取对应的实例。

public class Main {
    public static void main(String[] args) {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Students stud = (Students) ac.getBean("stu");
        stud.getInfo(); // [苏世年龄是:25]
    }
}

Spring实例化对象的过程经过简化后反射 java,可以理解为对象反射实例化的步骤:

反射+抽象工厂模式

反射 java_java反射invoke_java通过反射创建对象

上面已经提到了代码示例,大家可以回去看看,增加印象

反射+抽象工厂的核心思想是:

在运行时,传入不同子类的全限定名,得到不同的Class对象,调用newInstance()方法返回不同的子类。 细心的读者会发现这里提到了子类的概念,所以反射+抽象工厂模式一般用于继承或者接口实现关系。

JDBC加载数据库驱动类

在导入第三方库的时候,JVM不会主动去加载外部导入的类,而是等到真正使用的时候再加载需要的类。 这样的话,我们可以在获取数据库连接Name的时候传入驱动类的完整定义,给JVM加载类。

public class DBConnectionUtil {
    /** 指定数据库的驱动类 */
    private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
    
    public static Connection getConnection() {
        Connection conn = null;
        // 加载驱动类
        Class.forName(DRIVER_CLASS_NAME);
        // 获取数据库连接对象
        conn = DriverManager.getConnection("jdbc:mysql://···", "root", "root");
        return conn;
    }
}

我们在开发SpringBoot项目的时候,经常会遇到这个类,不过可能会成为一种习惯,也就不太在意了。 这里我给大家展示一下常用的application.yml中的数据库配置。 我想你应该突然意识到这一点。 .

java通过反射创建对象_反射 java_java反射invoke

这里的driver-class-name是不是感觉和我们一开始加载的类很像? 这是因为MySQL版本不同,驱动类不同。 这体现了使用反射的好处:无需修改源码,加载配置文件即可。 即可完成驱动类的替换。

的优点和缺点

不过,有得必有失。 一项技术不可能只有优点没有缺点。 反射也有两个相对隐蔽的缺点:

反思总结