类加载
java程序运行时类加载的方式和流程是:
编译 》加载 》验证 》准备 》解析 》初始化
编译:将java代码编译为字节码文件
加载:查找并通过io读入字节码文件,在内存中生出一个代表类的class对象,作为访问方法区的
输入入口,使用到类的时候才会加载
验证:字节码的校验,是否正确
准备:给类的静态变量分配内存,并赋予默认值
解析:将符号引用替换为直接引用,静态链接过程
初始化:对类的静态遍历,初始化指定值,执行静态代码块
类加载器
以上的流程中类加载过程主要通过类加载器实现,java中主要有四个类加载器
引导类加载器
负责加载jre lib目录下的核心类库,如charset.jar,rt.jar等
扩展类加载器
负责加载lib目录下 ext扩展目录的jar类包
应用程序类加载器
负责加载classPath 路径下的类包,就是我们写的代码
自定义加载器
负责加载用户自定义路径的类
双亲委派机制
上述加载器的加载顺序是:首先加载自定义加载器,然后是应用程序类加载器,然后再是扩展类加载器,最后才是引导类加载器
加载的方式:
自定义加载器加载 加载到类 则返回
没有找到,向上委托(父加载器,没有继承关系)
appClassLoad(应用程序加载器) 加载到类 返回
没有找到,委托上级
extClassLoad(扩展类加载器) 加载到类 返回
没有找到,委托上级
classLoad(引导类) 加载到类 向下委托
没有找到,再向下委托 。。。。
流程:
自定义 》》 app 》》 ext 》》 system 》》 ext 》》 app 》》自定义
这个就是所谓的双亲委派机制。那么这儿可能就有疑问了:
为啥不从system(引导类) 开始往下找,还要来回循环一遍?
基本上大部分的类加载的都是我们自己写的内容(自带的内容需要加载的很少),双亲委派机制只有第一次会从app开始加载,第二次的时候不用再向上委托了,如果从system开始往下找,那么循环会更多次。
还有两个特别重要的原因(主要原因)
1. 沙箱安全机制,避免核心api库被篡改
比如我也定义个String,如果从上往下,那么String不是被覆盖了?
2. 避免类的重复加载
自定义类加载器实现
只需要继承ClassLoader, 该类有两个核心方法,一个是loadClass(String, boolean),
实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义
类加载器主要是重写findClass方法。
打破双亲委派机制
如何打破双亲委派机制?
其实很简单,就不往上委托,自己去加载
重写我们的 loadClass方法就可以了,但是要注意核心的比如Object这种类不能自定义
加载(会有沙箱安全问题)
java默认是双亲委派机制,为什么要打破?
最常见的打破双亲委派机制就是我们的Tomcat
原因:
1.tomcat 是一个容器,同时会部署多个应用,如果我们多个应用程序用的东西都
是不同版本的,一个jdk6 一个jdk8 那么不是就出问题了?所以要保证相互隔离
2.相同的类库相同的版本最好能共享,不然有10个就需要10个相同类库加载
3.基于安全考虑,应用程序的类库和web容器类库要隔离开来