类加载概念:
每个Java文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Java代码经转换后的虚拟机指令,当需要使用某个类时,虚拟机将会加载它的”.class”文件,并创建对应的class对象,将class文件加载到虚拟机的内存,这个过程称为类加载。
1、如何识别.class文件?
2、识别到对应的class文件后,该如何加载它?依赖加载器?
类加载器分为三种:
启动类加载器(Bootstrap classLoader):又称为引导类加载器,由C++编写,无法通过程序得到。主要负责加载JAVA中的一些核心类库,主要是位于/lib/rt.jar中。
拓展类加载器(Extension classLoader):主要加载JAVA中的一些拓展类,位于/lib/ext中,是启动类加载器的子类。
应用类加载器(System classLoader): 又称为系统类加载器,主要用于加载CLASSPATH路径下我们自己写的类,是拓展类加载器的子类。
寻找类加载器实例:
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
运行输出结果:
sun.misc.Launcher$AppClassLoader
@64fef26a
sun.misc.Launcher$ExtClassLoader
@1ddd40f3
null
3、双亲委派模型
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class c = findLoadedClass(name);//判断它是否被加载
if (c == null) {//没有被加载
long t0 = System.nanoTime();
try {//判断它是否有父类
if (parent != null) {//有父类
c = parent.loadClass(name, false);
} else {//没有父类
c = findBootstrapClassOrNull(name);//启动类加载器加载
}
} catch (ClassNotFoundException e) {
}
p; //父类没加载成功
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
这段代码实现了双亲委派模式。
双亲委派模型意义:
-系统类防止内存中出现多份同样的字节码
-保证Java程序安全稳定运行
4、类加载的三种方式:
loadClass:负责以双亲委派方式去加载类。
findClass:根据类的包路径找到class文件。
defindClass:负责从class字节码中加载Class对象,然后Class对象通过反射机 制才生成对象的。
递归是去把请求委派给父类加载器时调用loadClass()方法,加载时调用findClass方法。
当我们去写自己的自定义类加载器时,不用每次都重写 loadClass 方法,这样会破坏它的双亲委派机制,只需要重写 findClass方法就好了。