类加载器
Posted 走出自己的未来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类加载器相关的知识,希望对你有一定的参考价值。
之前(已经n年之久)介绍过类加载的基本流程,虚拟机类加载过程,现在来重点关注一下其中几个比较重要的流程节点。
本文主要介绍类加载器,了解类加载器主要有哪些、加载流程、双亲委派以及打破双亲委派
1、类加载器种类
从java虚拟机角度来分,类加载器可以分为两种:
一种是启动类加载器,使用C++语言实现,是虚拟机自身的一部分。
一种是所有其他的类加载器,由java语言实现,独立于虚拟机外部,并且都继承自抽象类java.lang.ClassLoader。
从java开发人员的角度分,类加载器分为三种:
启动类加载器,主要加载<JAVA_HOME>\\lib 目录下的核心类
扩展类加载器,由sun.misc.Launcher$ExtClassLoader实现,主要加载<JAVA_HOME>\\lib\\ext 目录下的jar包
应用程序类加载器,由sun.misc.Launcher$AppClassLoader实现,主要加载classpath路径下的jar文件。
可以通过如下代码查看具体加载的jar:
static void testLoaderScope()
String bootLoader = System.getProperty("sun.boot.class.path");
System.out.println(bootLoader.replace(";", System.lineSeparator()));
System.out.println("==================");
String extLoader = System.getProperty("java.ext.dirs");
System.out.println(extLoader.replace(";", System.lineSeparator()));
System.out.println("==================");
String appLoader = System.getProperty("java.class.path");
System.out.println(appLoader.replace(";", System.lineSeparator()));
2、双亲委派
前面已经介绍了类加载器的种类,那在类加载的过程中,这些又如何工作的呢?
上图就是双亲委派的模型,在进行类加载时,首先是从底部开始,检查类是否已经加载,如果加载则直接返回类结果;如果没有,则向上由父类进行检查。如果都没有加载过,则由Bootstrap类加器进行加载,加载不到则交给子类加载器进行加载。如果都无法加载,则会抛出ClassNotFund异常。具体流程如下:
双亲委派的设计作用在于保证类加载的安全性,这里需要注意的是,虽然说的是父子类加载器,但是类加载器之间的父子关系并不是以继承关系来实现,而是以组合关系来复用父类加载器的代码。
3、自定义类加载器
实现自定义类加载器,需要继承ClassLoader,然后重写findClass方法去加载自定义的类文件即可。
自定义类加载器代码如下:
package com.wpb.jvm;
import com.wpb.jvm.test.HelloJvm;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class MyClassLoader extends ClassLoader
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
// 此目录为编译后的class文件路径
File file = new File("d:/", name.replace(".", "/").concat(".class"));
try
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b = fis.read()) != 0)
baos.write(b);
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();
return defineClass(name, bytes, 0 , bytes.length);
catch (Exception e)
e.printStackTrace();
return super.findClass(name);
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException
ClassLoader myClassLoader = new MyClassLoader();
Class<?> class1 = myClassLoader.loadClass("com.wpb.jvm.test.HelloJvm");
HelloJvm helloJvm = (HelloJvm) class1.newInstance();
helloJvm.sayHello();
自定义类信息如下:
package com.wpb.jvm.test;
public class HelloJvm
public void sayHello()
System.out.println("hello my custom class loader");
4、打破双亲委派
了解什么是双亲委派并且会自定义类加载器之后,我们来看下如何打破双亲委派模式,使用自己定义的加载来进行加载类文件。
自定义类加载器是重写findClass,而要想打破双亲委派,需要重写loadClass方法,在加载的时候就直接使用自己定义的方法进行加载,避免使用默认的方法,这样就不会走双亲委派模式。
自定义类加载器并重写loadClass方法:
package com.wpb.jvm;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassLoader2 extends ClassLoader
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
// 注意路径
File file = new File("G:/IdeaPrictise/jvm/out/production/jvm/", name.replace(".", "/").concat(".class"));
if (!file.exists())
return super.loadClass(name);
try
InputStream is = new FileInputStream(file);
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
catch (Exception e)
e.printStackTrace();
return super.loadClass(name);
public static void main(String[] args) throws ClassNotFoundException
MyClassLoader2 classLoader1 = new MyClassLoader2();
Class<?> class1 = classLoader1.loadClass("com.wpb.jvm.test.HelloJvm");
classLoader1 = new MyClassLoader2();
Class<?> class2 = classLoader1.loadClass("com.wpb.jvm.test.HelloJvm");
System.out.println(class1 == class2); // 应该返回false
加载的自定义类还是上述的HelloJvm对象。
此时最终的结果应该输出false。
类加载器的介绍就到这里了,下篇会从源码的角度来介绍下加载流程,以及为啥就能做到打破双亲委派。
以上是关于类加载器的主要内容,如果未能解决你的问题,请参考以下文章