反射 java-java反射invoke
反射
网上看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来决定使用哪个实体。 您可以通过在项目运行时动态传入参数来决定使用哪个实体。 看看这个是不是有点像设计模式中的工厂模式。 是的,这也是应用场景之一。
然后,通过这个例子,简单总结一下
简要总结
在代码运行之前,我们不确定以后会使用哪种数据结构。 我们只在程序运行时决定使用哪个数据类,而反射可以在程序运行过程中动态获取类信息和调用类方法。 通过反射构造类实例,代码最终会演化成下面这样。
public T getPoJo(String className) throws Exception {
Class clazz = Class.forName(className);
return (T) clazz.newInstance();
}
接下来,让我们深入了解一下,看看如何使用吧!
反射的基本使用
Java 反射有 4 个主要组件:
在网上找了一张图片放在下面。 如果使用反射,这四个核心类是分不开的。 只有了解它们提供什么信息,它们的功能是什么,才能方便地使用它们。
当我们学习反射的基本使用时,我将使用一个学生类作为模板来说明。 首先,让我们熟悉一下这个类的基本组成部分:属性、构造函数和方法
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;
}
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);
Students stu = (SmallPineapple) constructor.newInstance("苏世", 25);
stu.getInfo();
// [苏世 的年龄是:25]
通过 Class 对象调用 newInstance() 将采用默认的无参数构造方法。 如果要通过显式构造方法构造实例,需要提前从Class中调用getConstructor()方法获取对应的构造函数,通过构造函数实例化对象。
这些API是开发中最常遇到的,当然还有很多重载的方法。 由于本文篇幅较长,如果每个方法都一一讲解,我们也记不住了,所以在使用的时候再归类一下。 在里面搜索就够了
获取一个类的所有信息
这里我就不细说了。 大家在使用的时候可以根据图片进行查询,因为我们主要讲的是面试,面试不会问这么细的问题。
通过反射调用方法
通过反射获取到一个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]
反射的应用场景
通过上面的解释,我们可以回忆总结一下:
以下是反射的三种常见应用场景:
让我们一一解释以上场景
Spring的IOC容器
在Spring中,经常会写一个上下文配置文件applicationContext.xml,里面包含了bean的配置。 程序启动时,会读取xml文件,解析出所有的标签,将对象实例化到IOC容器中。
定义完以上文件后,通过ClassPathXmlApplicationContext加载配置文件。 程序启动时,Spring会将配置文件中的所有bean实例化,并放入IOC容器中。 IOC 容器本质上是一个工厂。 通过这个factory传入
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,可以理解为对象反射实例化的步骤:
反射+抽象工厂模式
上面已经提到了代码示例,大家可以回去看看,增加印象
反射+抽象工厂的核心思想是:
在运行时,传入不同子类的全限定名,得到不同的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中的数据库配置。 我想你应该突然意识到这一点。 .
这里的driver-class-name是不是感觉和我们一开始加载的类很像? 这是因为MySQL版本不同,驱动类不同。 这体现了使用反射的好处:无需修改源码,加载配置文件即可。 即可完成驱动类的替换。
的优点和缺点
不过,有得必有失。 一项技术不可能只有优点没有缺点。 反射也有两个相对隐蔽的缺点:
反思总结