Java 类加载机制
类的加载:
类的初始化:
类什么时候才被初始化:
1)创建类的实例,也就是new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM启动时标明的启动类,即文件名和类名相同的那个类
只有这6中情况才会导致类的类的初始化。
假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。
类的加载:
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的Java.lang.Class对象,用来封装类在方法区类的对象。
类加载器:
import java.net.URL; public class Main { public static void main(String[] args) { URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].toExternalForm()); } } }
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/resources.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/rt.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/sunrsasign.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/jsse.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/jce.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/charsets.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/jfr.jar file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/classes
Java类加载机制
类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示的对象组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:
(1) 装载:查找和导入Class文件;
(2) 链接:把类的二进制数据合并到JRE中;
(a)校验:检查载入Class文件数据的正确性;
(b)准备:给类的静态变量分配存储空间;
(c)解析:将符号引用转成直接引用;
(3) 初始化:对类的静态变量,静态代码块执行初始化操作
类加载器和双亲委派模型
(1) Bootstrap ClassLoader : 将存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar 名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用
(2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库加载。开发者可以直接使用扩展类加载器。
(3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。
双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的类加载器都是如此。因此所有的加载请求最终都应该传达到顶层的启动类加载器中,只有当父加载器反馈无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。这些类加载器的父子关系不是以继承的关系实现,而都是使用组合关系来复用父加载器的代码。
public class ClassLoaderTest { public static void main(String[] args) { //输出ClassLoaderText的类加载器名称 System.out.println("ClassLoaderText类的加载器的名称:" + ClassLoaderTest.class.getClassLoader().getClass().getName()); System.out.println("System类的加载器的名称:" + System.class.getClassLoader()); System.out.println("ArrayList类的加载器的名称:" + ArrayList.class.getClassLoader()); ClassLoader cl = ClassLoaderTest.class.getClassLoader(); while (cl != null) { System.out.print(cl.getClass().getName() + "->"); cl = cl.getParent(); } System.out.println(cl); } }
Output:
ClassLoaderText类的加载器的名称:sun.misc.Launcher$AppClassLoader System类的加载器的名称:null ArrayList类的加载器的名称:null sun.misc.Launcher$AppClassLoader->sun.misc.Launcher$ExtClassLoader->null
好处:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。
https://mp.weixin.qq.com/s/-Q255hbgbMIVymxceH_odw
http://blog.csdn.net/java2000_wl/article/details/8040633
http://blog.csdn.net/gjanyanlig/article/details/6818655
http://www.cnblogs.com/fsjohnhuang/p/4284515.html
http://blog.csdn.net/gjanyanlig/article/details/6818889
http://www.cnblogs.com/ITtangtang/p/3978102.html