今天又看了李刚的《疯狂Java讲义》里面关于类加载机制与反射的知识。
以前一直不知道,反射的原理,其实现在感觉还是比较自然的。
1.类加载的三大步骤
(1)加载
将类的class文件读入内存,并为之创建一个java.lang.Class对象。
(2)连接
把类的二进制数据合并到jre中
①验证:检验加载的类是否有正确的内部结构
②准备:为类变量分配内存,并默认初始化
③解析:将类的二进制数据中的符号引用替换为直接引用
(3)初始化
主要是对类变量进行初始化:①声明变量指定初始值②静态初始化块
2.什么是符号引用,什么是直接引用
这个参考知乎上的回答:https://www.zhihu.com/question/30300585
简单来说就是,符号引用是设计的字符串---用文本形式来表示引用关系
直接引用可能是指针/地址,也可能是地址偏移量,重点是它能直接被JVM使用
3.类加载的作用
目前我所知道的作用是
(1)只有加载了类,才能使用类的变量、方法,以及创建类的实例
(2)同时,类加载的时候,是在运行时搜集某个类的所有信息,因为类加载是运行的时候通过.class或者jar包进行解析,而这里面含有类的一切信息
搜集了这个类的信息之后,存起来,Class类关联,我们可以使用Class类来访问这些信息,这样就实现了反射的功能。
2016/06/05补充:
4.三种类加载器
(1)Bootstrap ClassLoader
加载Java核心类
位置:jre/lib/xxxx
注意:根类加载器由JVM自身实现,它加载的类库中包含了java.lang.*
而java.lang.*中包含了java.lang.ClassLoader,加载了这个库之后,才能加载后面的类加载器,因为(2)(3)是java.lang.ClassLoader的子类。
(2)Extension ClassLoader
加载jre/lib/ext下面的类
(3)Syetem ClassLoader
加载CLASSPATH环境变量下面的类
5.获得Class对象的三种方法
(1)使用Class类的forName静态方法
(2)使用具体某个类型的.class静态域
(3)使用某个类型对象的.getClass实例方法
注意:一般用地最多的是(1)和(2),其中(2)的效率更高(没有方法调用的过程),同时更加安全(会在编译期进行类型检查)
但是当有时候只能有一个字符串的类名信息(比如通过配置文件动态创建类),则只能使用Class的forName方法了。
6.用反射实现动态代理
其实这个知识点,最重要的是理解,动态代理是用来干什么的。
现在就来分解这个词语,代理是用来干什么的?
代理,就是代替一个对象来进行各种行为的另一个对象。
它具备原来的对象的所有的行为,但是会加入一些我们想加入的行为,而这些想加入的行为是我们可以在另一个地方统一管理的。
这样就实现了原来的对象的行为与我们想加入的一些行为的解耦,但是又可以将这些想加入的行为加入到原有对象的行为之中。
动态,就是说利用反射来创建了这个代替原有对象的另一个对象。
所以合起来动态+代理就是动态代理。
再解释一下,这个代理到底解决了一个什么问题。
假设我们有代码1,代码2,代码3,一共3段代码。
三段代码之中有一段重复的功能,这段重复的功能有100行代码,我们称这100行代码为代码段X
为了有效的复用代码,并且实现在修改的时候方便修改,我们可以将这相同的100行代码,定义为一个函数X,这样就实现了代码的复用以及易于书写与维护。
随着我们不断第抽取出函数X,函数Y等等等。我们发现了很多,与我们想要实现代码1,代码2,代码3业务逻辑无关的函数,但是又必须将它们写出来。
比如一个论坛里面你可以点击发帖、点击回帖,这些业务逻辑本身是和你现在的登录状态或者用户权限无关的,但是又必须要有一个登录验证或者权限验证的过程。
有的时候,这种与业务逻辑无关的代码有很多,将其肆意地穿插在业务逻辑之中,会使代码很难读,很难维护。
所以,我们可以使用代理将这些与业务逻辑无关的代码维护起来,然后将其统一放在我们的业务逻辑前面或者后面,这样,对于使用代理的人来说,就可以只关注业务逻辑
而其他的必要的但非业务逻辑的操作,就可以统一地放在代理中来管理了。