# 类加载器

  1. JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
  2. 从概念上讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
  3. 无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个,如下所示: 类加载器的分类 ClassLoader

# 引导类加载器

  • 这个类加载使用C/C++语言实现的,嵌套在JVM内部。
  • 它用来加载Java的核心库,用于提供JVM自身需要的类。
  • 它并不继承自 java.lang.ClassLoader ,没有父加载器
  • 加载扩展类加载器和应用程序类加载器,并指定为它们的父类加载器。
  • 出于安全考虑,Bootstrap引导类加载器只加载包名为 java、javax、sun等开头的类。

# 扩展类加载器

  • Java语言编写, 由 sun.misc.Launcher$ExtClassLoader 实现。
  • 派生于 ClassLoader 类
  • 父类加载器为 引导类加载器
  • 从java.ext.dirs系统属性所指定的目录中加载类库,或从 JDK 的安装目录的 jre/lib/ext 子目录下加载类库。如果用户创建的 JAR 放在此目录下,也会自动由扩展类加载器加载。
public class ClassLoaderTest {

	public static void main(String[] args) {

		// 获取系统类加载器
		ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
		System.out.println(systemClassLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

		// 获取扩展类加载器
		ClassLoader extClassLoader = systemClassLoader.getParent();
		System.out.println(extClassLoader); // sun.misc.Launcher$ExtClassLoader@1540e19d

		// 获取引导类加载器: 获取不到
		ClassLoader bootstrapClassLoader = extClassLoader.getParent();
		System.out.println(bootstrapClassLoader); // null

		// 用户自定义类加载器:使用的是默认的系统类加载器
		ClassLoader customClassLoader = ClassLoaderTest.class.getClassLoader();
		System.out.println(customClassLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2
		System.out.println(customClassLoader.getParent()); // sun.misc.Launcher$ExtClassLoader@1540e19d

		// 系统核心类库的类加载器:使用的是引导类加载器。
		ClassLoader stringClassLoader = String.class.getClassLoader();
		System.out.println(stringClassLoader); // null
		System.out.println(InputStream.class.getClassLoader()); // null
		
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 自定义类加载器

# 为什么要自定义类加载器?

  1. 隔离加载类
  2. 修改类的加载方式
  3. 扩展加载源
  4. 防止源码泄露

# 自定义类加载器实现步骤

  1. 通过继承抽象类 java.lang.ClassLoader 类的方式,并重写其中的 loadClass() 方法(JDK1.2之前)
  2. JDK1.2之后,不建议用户覆盖 loadClass()方法,而是重写 findClass() 方法。
  3. 如果没有特殊复杂需求,可以直接继承 java.net.URLClassLoader 类,这样可以避免编写 findClass() 方法及其获取字节流的方式,使自定义类加载器编写更加简洁。
public class MyClassLoader extends ClassLoader {

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		try {
			byte[] result = IOUtils.toByteArray(new FileInputStream(new File("C:\\Users\\leichu\\Desktop\\clazz\\User.class")));
			if (null == result) {
				throw new FileNotFoundException();
			} else {
				return defineClass(name, result, 0, result.length);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
		ClassLoader myClassLoader = new MyClassLoader();
		Class<?> clazz = Class.forName("com.leichu.jvm.User", true, myClassLoader);
		Object obj = clazz.newInstance();
		System.out.println(obj.getClass().getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2
		
		com.leichu.jvm.User user = (com.leichu.jvm.User) clazz.newInstance();
		user.setId(1L);
		user.setName("leichu");
		System.out.println(user);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 获取 ClassLoader 的途径

# 1. 获取当前类的 ClassLoader

class.getClassLoader()

# 2. 获取当前线程上下文的 ClassLoader

Thread.currentThread().getContextClassLoader()

# 3. 获取系统的 ClassLoader

ClassLoader.getSystemClassLoader()

# 4. 获取调用者的 ClassLoader

DriverManager.getCallerClassLoader()

public class GetClassLoaderTest {
	public static void main(String[] args) {
		// 1. 获取当前类的 ClassLoader
		System.out.println(GetClassLoaderTest.class.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2
		// 2. 获取当前线程上下文的 ClassLoader
		System.out.println(Thread.currentThread().getContextClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2
		// 3. 获取系统的 ClassLoader
		System.out.println(ClassLoader.getSystemClassLoader());  // sun.misc.Launcher$AppClassLoader@18b4aac2
		// 4. 获取调用者的 ClassLoader
	}
}
1
2
3
4
5
6
7
8
9
10
11
更新时间: 2021-04-28 08:44:23