在谈论到反射这个问题时,你是否有如下疑问?
无论是在.NET还是Java中反射的原理和机制是一样的,理解了一种另一种就可以迎刃而解,想要理解反射首先需要了解底层的一些概念和运行,理解了反射有助于你理解程序的运行原理,目前很多框架(java、.NET)中都引入了反射这一个技术,反射其实也不是什么新的技术只是几个不同的操作过程集成到一起关联起来了。
从表面上我们看到的效果是这样的:通过传入一个字符串可以得到某个类的对象,在这一个过程中做了很多事情。你是否有下面的一些疑问存在?
JDK、JRE是一回事吗?
JRE和JVM有联系吗?
JVM和类加载器什么关系?
类加载器加载类的过程?
反射和类加载之间有什么神秘关系?
看完这篇文章,也许你会对反射有一个清晰的认识。
JDK是为我们开发提供的一个开发类库,里面存在着大量的开发类,而JRE是开发好的程序运行的环境,也就是说你的电脑上如果想运行java程序可以没有JDK,但要有JRE这个运行环境,往往下载了JDK开发包已经包含了JRE这个环境,安装时是可以选择不安装JRE的,你开发好的程序都需要测试、运行等,因此有必要安装JRE。
而JVM是存在JRE这个环境里的,只是在JRE这个环境里面不止仅有JVM,JVM是必须的,如果没有其它的类辅助JVM运行,JVM是没有办法运行的,举个简单的例子来看看好想红花也许绿叶来衬托、如果没有绿叶的衬托怎么会显示出红花的价值呢。
类加载器这些类是JVM提供的,负责把类(.class文件)读入到内存中,并且为每个加载到内存中的类创建一个Class对象,你可以理解为一般我们看到的类都有一个超类Class,当一个类加载时就会为这个类实例化一个Class对象,这个对象负责唯一标示该类,事实表明这个Class对象非常有用,如论是反射还是注解等的实现都依赖于这一对象,我们通过这个Class对象里面的方法可以获取到任何一个类的所有方法(包括父类集成来的)、所有字段(包括私有属性)、构造器等等,在上一篇注解博客中核心就是利用了Class对象的getMethods()方法,得到一个类的所有方法,然后循环判断注解才实现对注解方法起作用。
获得Class对象的三种方式
1.Class类的forName(String clazzName)
2.调用某个类的class属性,如Person.class
3.某个对象的getClass()方法
类加载的步骤(想了解的更细节可以看一下JVM规范)
1.加载
指的是将.class文件读入内容,并为之创建一个Class对象;可以理解为所有的类也是实例,它们都是java.lang.Class这个类的实例。
加载类的途径
A:从本地文件系统中加载.class文件
B:从JAR包中加载.class文件,例如你连接mysql或oracle数据库时,是不是有一个驱动jar包,驱动类都放在这个jar中,再多说一点:关于驱动jar文件,一个驱动可以连接哪个数据库或者支持啥功能,本地事务还是全局事务,主要看驱动里面支持不支持。
C:网络加载
D;java文件先编译,再加载
2.连接
将内容中的.class二进制文件读入到JRE代表的进程内容中,又分为验证、准备、解析三个过程。
3.初始化
主要是对一些静态字段赋值操作,初始化时可能并没有类的实例呢,所以是只初始化类范围的变量,如static修饰的变量。
初始化不仅仅是对目标类初始化,如果它有继承的父类,它的父类会都初始化,我们知道所有类都是object的子类,object每次都会被初始化,这也解析了为什么你可以调用Object这个类的方法,因此它也初始化了。
类加载器
从上图中我们可以得知哪些类加载器以及它们主要负责加载哪些类,其中MyClassLoader1/2是自定义类加载器,可以从指定目录加载类,即.java文件不再classpath路径下也可以加载,有时我们会遇到找不到类的问题,其实就是类路径写的不错。
类加载器加载类算法?
内存管理、CUP调度等都有自己的算法,比如先进先出、最早使用原则,写算法的目的是实现资源合理调配,从各种方案中找到一种可以解决实际问题的思路,类再加载类时也存在这样的问题,如遇到一个.class类后,让哪一个加载器加载?去哪里找加载的类?等等,电脑是很傻的,不要把电脑想的很聪明,不要让电脑去做选择,它的选择是我们给它指定的。
算法
依赖原则:当一个类加载时,它所依赖的类同时被加载。
尊老爱幼::针对加载器,每次加载类都让着长辈,父加载器优先。
缓存:所有加载好的类放入一个缓存中,加载某个类时先去缓存中查找,不存在的话才去加载(如果你修改了一个加载好的类,也是存在的不去重新加载),这也是为什么每次我们修改了一个类后,需要重新启动tomcat即重启JVM。
上面这些是实现反射的基础,总结就两点一是Class对象;而是类加载器;反射主要是依赖于java的这两个特性实现的反射过程,下篇中将用一个实例来实现反射,通过从属性文件或者配置文件中读取类的字符串信息来实例化类,Spring框架也是利用的这一个过程实现依赖注入的。
对于底层的一些东西觉的还是有必要理解、并可以使用,各种框架都依赖于底层,这对学习框架也是很有帮助的。