一、什么是java反射?
在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后 new 类名()
方式来获取该类的对象。也就是说我们需要在写代码的时候(编译期或者编译期之前)就知道我们要实例化哪一个类,运行哪一个方法,这种通常被称为 静态的类加载 。
但是在有些场景下,我们事先是不知道我们的代码的具体行为的。比如,我们定义一个服务任务工作流,每一个服务任务都是对应的一个类的一个方法。
- 服务任务B执行哪一个类的哪一个方法,是由服务任务A的执行结果决定的
- 服务任务C执行哪一个类的哪一个方法,是由服务任务A和B的执行结果决定的
- 并且用户不希望服务任务的功能在代码中写死,希望通过配置的方式执行不同的程序
面对这个情况,我们就不能用代码 new 类名()
来实现了,因为你不知道用户具体要怎么做配置,这一秒他希望服务任务A执行Xxxx类的x方法,下一秒他可能希望执行Yyyy类的y方法。当然你也可以说提需求嘛,用户改一次需求,我改一次代码。这种方式也能需求,但对于用户和程序员个人而言都是痛苦,那么有没有一种方法 在运行期动态的改变程序的调用行为的方法 呢?这就是要为大家介绍的“ java反射机制 ”。
那么java的反射机制能够做那些事呢?大概是这样几种:
-
在程序运行期动态的根据
package名.类名
实例化类对象 -
在程序运行期动态获取类对象的信息,包括对象的成本变量和方法
-
在程序运行期动态使用对象的成员变量属性
-
在程序运行期动态调用对象的方法(私有方法也可以调用)
二、Hello World
我们定义一个类叫做Student
package com.zimug.java.reflection; public class Student { public String nickName; private Integer age; public void dinner(){ System.out.println("吃晚餐!"); } private void sleep(int minutes){ System.out.println("睡" + minutes + "分钟"); } }
如果不用反射的方式,我相信只要学过java的朋友肯定会调用dinner方法
Student student = new Student(); student.dinner();
如果是反射的方式我们该怎么调用呢?
//获取Student类信息 Class cls = Class.forName("com.zimug.java.reflection.Student"); //对象实例化 Object obj = cls.getDeclaredConstructor().newInstance(); //根据方法名获取并执行方法 Method dinnerMethod = cls.getDeclaredMethod("dinner"); dinnerMethod.invoke(obj); //打印:吃晚餐!
通过上面的代码我们看到,com.zimug.java.reflection.Student类名和dinner方法名是字符串。既然是字符串我们就可以通过配置文件,或数据库、或什么其他的灵活配置方法来执行这段程序了。这就是反射最基础的使用方式。
三、类加载与反射关系
java的类加载机制还是挺复杂的,我们这里为了不混淆重点,只为大家介绍和“反射”有关系的一部分内容。
java执行编译的时候将java文件编译成字节码class文件,类加载器在类加载阶段将class文件加载到内存,并实例化一个java.lang.Class的对象。比如:对于Student类在加载阶段