本博文主要分享一些我学习Java反射机制的一些心得体会。
Java反射机制
Java反射机制是指在程序运行时,对于任何一个类,都能知道这个类的所有属性和方法;对于任何一个对象,都能够调用其任何一个属性和方法。这种动态获取信息和动态调用对象方法的功能称为Java的反射机制。
简单来说,反射机制可以在程序运行中检查类、接口、方法和变量等信息,不需要知道类名,还可以在运行中实例化新对象(class.newInstance方法)、调用方法。
动态编译和反射机制
为什么要使用反射机制?直接创建对象不好吗?这里涉及到动态和静态编译的概念。
静态编译:在编译时确定类型,绑定对象
动态编译:在运行时确定类型,绑定对象
interface Function{
void doSomething();
}
class functionA implements Function{
@Override
public void doSomething() {
System.out.println("functionA");
}
}
class functionB implements Function{
@Override
public void doSomething() {
System.out.println("functionB");
}
}
public class Test {
public static void main(String[] args) {
Function f = new functionA();
// Function f = new functionB();
f.doSomething();
}
}
从上方代码中,我们需要哪个类,只要new一下即可。但这样需要停止程序,修改代码并且重新编译。这种做法对于一个需要不停运行的程序时不允许的(例如服务器),这就需要用到Java的反射机制来实现动态编译。
interface Function{
void doSomething();
}
class functionA implements Function{
@Override
public void doSomething() {
System.out.println("functionA");
}
}
class functionB implements Function{
@Override
public void doSomething() {
System.out.println("functionB");
}
}
public class Test{
public static void main(String[] args) {
try {
//从配置文件读取需要的类名
String className = getClassNameFromConfig();
Function f = (Function) Class.forName(className).newInstance();
f.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用反射机制,要加载所需要的类型时,只要修改配置文件中的类名即可,不用重新编译。
反射机制的优缺点
-
优点: 运行期间进行类型判断,动态加载类,提高了代码的灵活性
-
缺点: 反射相当于一系列解释操作,通知 JVM 要做的事,这样性能要比Java代码直接执行相应操作要慢。
获取Class
在动态编译的情况下,Java 一般有两种方法获取Class对象:
- 类名.class
- Class.forName(“类名”)
public class Reflect1 {
public static void main(String[] args) throws ClassNotFoundException {
// 类名.class
Class reflect1Class1 = Reflect1.class;
System.out.println(reflect1Class1.getName());
// Class.forName
Class reflect1Class2 = Class.forName("Reflect1");
System.out.println(reflect1Class2.getName());
/* 不属于动态加载范畴 */
// 实例对象.getClass()
Reflect1 reflect1 = new Reflect1();
Class reflect1Class3 = reflect1.getClass();
System.out.println(reflect1Class3.getName());
}
}
输出结果:
Reflect1
Reflect1
Reflect1
三种获取方式的区别:
类名.class方式适用于在编译时已经知道具体的类; Class.forName()方式适用于运行时动态获取Class对象,因为forName方法会出现找不到类的情况,因此必须捕获 ClassNotFoundException异常;getClass方法针对的是运行时的对象,返回其所属类的Class的对象。
Java反射相关操作
- 获取类名 class.getName()
- 获取类修饰符 class.getModifiers()
- 获取包信息 class.getPackage()
- 获取父类的Class对象 class.getSuperclass()
- 获取接口信息 class.getInterface()
- 获取构造函数 class.getConstructors()
- 获取构造函数参数 constructors.getParameterTypes()
- 获取Method方法 class.getMethod(), class.getMethods(), class.getDeclaredMethod()
- 获取成员变量 class.getField(), class.getFields(), class.getDeclaredField()
- … …
这篇文章总结的比较全面:Java反射使用总结
反射的应用场景
- 使用 JDBC 连接数据库时使用
Class.forName()
通过反射加载数据库的驱动程序; - Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系;
- 动态配置实例的属性;
- … …
好了,关于反射机制,本次就分享到这里,如果文中有任何错误或者欠妥的地方,还望指正。
Life is fantastic!
参考
- https://juejin.im/post/6844904005294882830
- https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/basic/reflection.md
- http://tengj.top/2016/04/28/javareflect/