JVM三种预定义类型类加载器
扩展类加载器: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块(初始化)。
注意:反射在写类的路径的时候,要把类完整路径写上。