JVM三种预定义类型类加载器
当一个 JVM启动的时候,Java缺省开始使用如下三种类型类装入器: 启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。 启动类加载器是无法被Java程序直接引用的。因为:Bootstrap ClassLoader是C++编写的(Hotpot虚拟机,jdk1.5后默认的jvm),它是虚拟机的一部分,其他类加载器是Java实现的,独立于虚拟机之外,并且全部继承自抽象类java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。扩展类加载器:Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载DK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
应用程序类加载器:Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
自定义类加载器
JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,如果编写了自己的ClassLoader,便可以做到如下几点:
(1)在执行非置信代码之前,自动验证数字签名。
(2)动态地创建符合用户特定需要的定制化构建类。
(3)从特定的场所取得java class,例如数据库中和网络中。
双亲委派模型
工作过程:1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给
父类加载器ExtClassLoader去完成。
2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求
委派给BootStrapClassLoader去完成。
3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),
会使用ExtClassLoader来尝试加载;
4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,应用程序也会变得一片混乱。类不会重复加载。
缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。
双亲委派模型,系统类防止内存中出现多份同样的字节码,保证Java程序安全稳定运行。
Class.forName()和ClassLoader.loadClass()的区别
Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块;(它也有可以控制static块是否执行的forName()函数);
ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
验证:
/**
* 被加载的类
* Created by KB404 on 2017/2/24.
*/
public class HelloClass
{
static {
System.out.println("I am a static block!!!");
}
}
/** * Created by KB404 on 2017/2/24. *///小知识点:当一个地方进行catch跳转了,try中跳转出下面的代码就不会执行了。public class HelloClassTest { public static void main(String[] args) { try { ClassLoader appLoader = Thread.currentThread().getContextClassLoader(); ClassLoader extLoader = Thread.currentThread().getContextClassLoader().getParent(); ClassLoader bootLoader = Thread.currentThread().getContextClassLoader().getParent().getParent(); System.out.println(appLoader); //获得AppClassLoader加载器 System.out.println(extLoader); //获得ExtClassLoader加载器 System.out.println(bootLoader); //获得BootStrapClassLoader加载器 //类ClassLoard加载类,不执行static块,只把类加载到内存中(也就是不进行连接和初始化) appLoader.loadClass("cn.edu.uestc.classloardtest.HelloClass"); //反射加载类,执行static块 Class.forName("cn.edu.uestc.classloardtest.HelloClass"); //ClassLoard只有newInstance()后才执行static块 /* try { appLoader.loadClass("cn.edu.uestc.classloardtest.HelloClass").newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }*/ } catch (ClassNotFoundException e) { System.out.print("not found"); } }}其结果为:
结果证明:
(1)我们获得不到引导类加载器(BootStrapClassLoard),获取时出现null。
(2)当用ClassLoard加载类时候,它只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
(3)当用反射的方法加载类时,会执行其static块(初始化)。
注意:反射在写类的路径的时候,要把类完整路径写上。