反射的好处
我们在第一次接触反射的时候,总会有个很大的疑问,反射看起来好麻烦啊,各种get.get,他究竟有什么好处,能用来做什么呢?
我们先来看一下《编程思想》这本书中是怎么说的.
RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在在编译时打开和检查.class文件.(换句话说,我们可以用"普通"方式调用对象的所有方法).对于反射机制来说,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件。 --《编程思想》
这段话看起来很叼的样子,有点云里雾里的,首先RTTI的意思就是以普通的方式来创建对象,调用方法,就是我们常用的new
关键字。这段话的意思简化版就是:编译器将.java文件编译成.class文件之后,普通方式创建的对象就不能再变了,我只能选择是运行还是不运行这个.class文件。是不是感觉很僵硬,假如现在我有个写好的程序已经放在了服务器上,每天供人家来访问,这时候Mysql数据库宕掉了,改用Oracle,这时候该怎么怎么办呢?假如没有反射的话,我们是不是得修改代码,将Mysql驱动改为Oracle驱动,重新编译运行,再放到服务器上。是不是很麻烦,还影响用户的访问。
假如我们使用反射Class.forName()来加载驱动,只需要修改配置文件就可以动态加载这个类,Class.forName()生成的结果在编译时是不可知的,只有在运行的时候才能加载这个类,换句话说,此时我们是不需要将程序停下来,只需要修改配置文件里面的信息就可以了。这样当有用户在浏览器访问这个网站时,都不会感觉到服务器程序程序发生了变化。
所以在《Mybatis技术原理与实战》是这么说反射的好处的。
配置性大大提高,如同Spring IOC容器,给很多配置设置参数,使得java应用程序能够顺利跑起来,大大提高了Java的灵活性和可配置性,降低模块间的耦合。--《Mybatis技术原理与实战》
相应的实例
Eclipse的代码自动补全就是利用了反射原理动态的加载要展示的方法,因为反射会影响性能,所以开了自动补全刚开始的时候会卡一下,主要还是我电脑性能不行。
反射的进阶
在JDK动态代理中可以利用反射直接获取到接口的方法来执行,从而可以将代理类要处理的代码抽象出来形成统一的接口。
Java的反射中Class.forName和ClassLoader的区别
ClassLoader:
ClassLoader
就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。
例如ClassLoader.getSystemClassLoader().loadClass("com.test.mytest.ClassForName");
是不会初始化类的。
Class.forName():
Class.forName()
方法实际上也是调用的CLassLoader来实现的。
Class.forName(String name, boolean initialize,ClassLoader loader)
方法来手动选择在加载类的时候是否要对类进行初始化。
应用场景:
1. 在我们熟悉的Spring框架中的IOC的实现就是使用的ClassLoader。
2. 使用JDBC时通常是使用Class.forName()方法来加载数据库连接驱动。这是因为在JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// Register ourselves with the DriverManager
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
Class.forName()
会初始化类,所以自动加载static
代码段,从而注册自己。