类加载的过程
加载-》连接-》初始化
三种类加载器
AppClassLoader-> ExtClassLoader-> BootStrapClassLoader
- 启动类加载器(Bootstrap ClassLoader)
jre/lib/*jar、rt.jar、jce.jar
- 扩展类加载器(Extension ClassLoader)
jre/lib/ext/*.jar
- 应用程序类加载器(Application ClassLoader)
classpath
demo
package com.jysemel.java.basic.claasloader;
public class ClassLoaderDemo {
public static void main(String[] args) {
//父子关系 AppClassLoader-> ExtClassLoader-> BootStrapClassLoader
ClassLoader demo = ClassLoaderDemo.class.getClassLoader();
System.out.println("demo " + demo);
System.out.println("demo parent " + demo.getParent());
//BootStrapClassLoader 引导类加载器,由C++ 实现,无法获取
System.out.println("BootStrapClassLoader " + demo.getParent().getParent());
//String,Integer 等基础均由 BootStrapClassLoader 加载
ClassLoader c1 = String.class.getClassLoader();
System.out.println("c1 " + c1);
ClassLoader c2 = Integer.class.getClassLoader();
System.out.println("c2 " + c2);
}
}
package com.jysemel.java.basic.claasloader;
public class ClassLoaderDemo1 {
public static void main(String[] args) {
//BootStrapClassLoader
System.out.println("BootStrapClassLoader " + System.getProperty("sun.boot.class.path"));
//ExtClassLoader
System.out.println("ExtClassLoader " + System.getProperty("java.ext.dirs"));
//AppClassLoader
System.out.println("AppClassLoader " + System.getProperty("java.class.path"));
}
}
向上委托查找、向下委托加载
保护底层类不会被应用程序覆盖
如何打破双亲委派
常见原因
容器部署多个应用可能依赖不同版本三方类库,保证每个应用类库独立和相互隔离 支持
打破方式
通过spi机制,使用ServiceLoader.load去加载
通过自定义类加载器,继承classloader,重写loadclass方法
demo
spi
package com.jysemel.java.basic.claasloader;
import com.jysemel.java.basic.claasloader.spi.SpiService;
import java.util.ServiceLoader;
public class SpiDemo {
public static void main(String[] args) {
ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(SpiService.class);
System.out.println("加载到的 spiService 实现:");
for (SpiService spiService : serviceLoader) {
spiService.execute();
}
}
}
package com.jysemel.java.basic.claasloader.spi;
public interface SpiService {
void execute();
}
package com.jysemel.java.basic.claasloader.spi;
public class SpiServiceImpl1 implements SpiService {
@Override
public void execute() {
System.out.println("SpiServiceImpl1 执行,当前线程: " + Thread.currentThread().getName());
}
}
package com.jysemel.java.basic.claasloader.spi;
public class SpiServiceImpl2 implements SpiService {
@Override
public void execute() {
System.out.println("SpiServiceImpl2 执行,当前线程: " + Thread.currentThread().getName());
}
}
重写loadClass()
package com.jysemel.java.basic.claasloader.loadclass;
import java.io.*;
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("MyClassLoader 尝试加载类: " + name);
// 1. 检查类是否已被加载
Class<?> loadedClass = findLoadedClass(name);
if (loadedClass != null) {
System.out.println("类已加载,直接返回: " + name);
return loadedClass;
}
// 2. 尝试使用父加载器加载(双亲委派)
try {
Class<?> parentLoaded = super.loadClass(name);
System.out.println("父加载器成功加载: " + name);
return parentLoaded;
} catch (ClassNotFoundException e) {
System.out.println("父加载器无法加载: " + name);
}
// 3. 如果父加载器失败,自己尝试加载(本例未实现,直接抛出异常)
// 实际中可以在这里添加 findClass 逻辑
throw new ClassNotFoundException("类 " + name + " 找不到");
}
}
package com.jysemel.java.basic.claasloader.loadclass;
public class Test {
public static void main(String[] args) throws Exception {
// 创建自定义类加载器实例
MyClassLoader loader = new MyClassLoader();
// 使用自定义加载器加载一个存在的类(如 java.lang.String)
Class<?> stringClass = loader.loadClass("java.lang.String");
System.out.println("加载 String 的类加载器: " + stringClass.getClassLoader());
System.out.println("----------------------------");
// 加载一个不存在的类(触发异常)
try {
Class<?> unknownClass = loader.loadClass("com.example.Unknown");
} catch (ClassNotFoundException e) {
System.out.println("捕获异常: " + e.getMessage());
}
}
}
jdk9之后变化
ExtClassLoader 被PlatformClassLoader取代