全面解析Java类加载器

本文详细介绍了Java类加载器的工作原理,包括系统提供的引导类加载器、扩展类加载器和应用程序类加载器,以及自定义类加载器的创建。类加载遵循双亲委托机制,确保了Java核心类库的安全性。同时,文章通过代码示例展示了如何自定义类加载器并加载类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先知道我们写的.Java源码文件,经过javac.exe编译后成为了.class文件,.class文件中描述了类的各种信息,最终都需要加载到虚拟机之后才能运行和使用。而虚拟机如何加载这些.class文件?.class文件的信息进入到虚拟机后会发生什么变化?下面我们就来了解一下java类加载的过程:
我们写的类,首先会编译成字节码文件,然后通过Java 中的类加载器负责将我们需要使用的类,加载进内存。而类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。
当然jvm启动的时候,并不是一次性加载所有的类,而是根据需要动态去加载类,主要分为隐式加载和显示加载。
隐式加载:程序代码中不通过调用ClassLoader来加载需要的类,而是通过JVM类自动加载需要的类到内存中。例如,当我们在类中继承或者引用某个类的时候,JVM在解析当前这个类的时,发现引用的类不在内存中,那么就会自动将这些类加载到内存中。
  显示加载:代码中通过Class.forName(),this.getClass.getClassLoader.LoadClass(),自定义类加载器中的findClass()方法等。

  1. 引导类加载器(bootstrap class loader):

    它用来加载 Java 的核心库(jre/lib/rt.jar),是用原生C/C++代码来实现的,并不继承自java.lang.ClassLoader。加载扩展类和应用程序类加载器,并指定他们的父类加载器,因为不是java编写,所以在java中并无法展现,也获取不到。 但是我们可以通过简单代码来查看它所负责加载类的路径:
    package test;
    public class Test01{
    public static voidmain(String[]args){
    System.out.println(System.getProperty(“sun.boot.class.path”));
    }
    }

打印结果如下:

D:\ develop\Java\jdk1.8.0_20\jre\lib\resources.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\lib\rt.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\lib\sunrsasign.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\lib\jsse.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\lib\jce.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\lib\charsets.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\lib\jfr.jar;D:\Program
Files\Java\jdk1.8.0_20\jre\classes

  1. 扩展类加载器(extensions class loader):

它用来加载 Java 的扩展库(jre/ext/*.jar)。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。 也可以通过System.out.println(System.getProperty(“java.ext.dirs”))查看加载类文件的路径。

打印结果如下:
D:\develop\Java\jdk1.8.0_20\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

  1. 系统类加载器(system class loader):
    它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它,sun.misc.Launcher$AppClassLoader@58644d46

  2. 自定义类加载器(custom class loader):
    除了系统提供的类加载器以外,我们开发人员自己可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

  3. 类加载器的继承关系
    

在这里插入图片描述
ExtClassLoader,AppClassLoder继承URLClassLoader,而URLClassLoader继承ClassLoader,BoopStrapClassLoder不在上图中,因为它是由C/C++编写的,它本身是虚拟机的一部分,并不是一个java类。

jvm加载的顺序:BoopStrap ClassLoder-〉ExtClassLoader->AppClassLoder

  1. 类加载机制-双亲委托机制
    咱先得弄清楚双亲委托机制到底是什么东西,它是是通过类加载器将class文件加载到虚拟机中以备调用,但是我们可以自己也写一个java.lang.String,来冒充jdk中的String, 假设这两个String都还没有加载,那么首先自身的加载器 AppClassLoader会去内存中找,找不到,直到bootstrap也在内存中找不到,那这时bootstrap会加载rt.jar中的String,至此加载结束,你自己的高仿是加载不到的。

    假设内存中原有的String已经加载,那么更简单,你兴致冲冲的写完了高仿准备装逼的时候,bootstrap 或者AppClassLoader 已经在内存中找到了(至于哪个加载器寻找哪一个区域的内存空间我不是很清楚 所以也不敢妄下定论)那你的高仿又失去了表现机会,这样的加载模式,保证了java自身类的安全。
    public abstract class ClassLoader {protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    synchronized (getClassLoadingLock(name)) {
    // First, check if the class has already been loaded
    //findLoadedClass() 是去内存中查找是否加载过这个名字为 name的类,上面的注释是源码中的注释,也是在说 检查这个类是否已经被加载了
    Class<?> c = findLoadedClass(name);
    if (c == null) {
    long t0 = System.nanoTime();
    try {
    //parent为classload类中的属性,表示它的父加载器,如果父加载器不为空则调用父类的loadClass方法
    //可以想到如果当前类加载器没有在内存中找到该类,那么它的父类加载器就会去内存中寻找是否加载了该类
    if (parent != null) {
    c = parent.loadClass(name, false);
    } else {
    //如果父加载器为空,那么调用BootstrapClassLoader去寻找该类,这也证明了BootstrapClassLoader在java中是
    //以null的形式存在的
    c = findBootstrapClassOrNull(name);
    }
    } catch (ClassNotFoundException e) {
    // ClassNotFoundException thrown if class not found
    // from the non-null parent class loader
    }

       if (c == null) {
           //如果还是没有找到,那么调用findClass去查找 也就是从上面提到的路径中找
           // If still not found, then invoke findClass in order
           // to find the class.
           long t1 = System.nanoTime();
           c = findClass(name);
    
           // this is the defining class loader; record the stats
           sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
           sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
           sun.misc.PerfCounter.getFindClasses().increment();
       }
    

    }
    if (resolve) {
    resolveClass©;
    }
    return c;
    }
    }}

  2. 自定义类加载器步骤
    

1.继承ClassLoader
2.重写findClass()
3.调用defineClass()

package com.itcast.test;

public class Test {
public void say(){
System.out.println(“Hello MyClassLoader”);
}
}
package com.itcast.test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoad extends ClassLoader {
private String classpath;

public MyClassLoad(String classpath) {
    this.classpath = classpath;
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    try {
        byte[] classDate = getDate(name);
        if (classDate == null) {
        } else {
            //defineClass方法将字节码转化为类
            return defineClass(name, classDate, 0, classDate.length);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return super.findClass(name);
}

//返回类的字节码
private byte[] getDate(String className) throws IOException {
    InputStream in = null;
    ByteArrayOutputStream out = null;
    String path = classpath + File.separatorChar +
            className.replace('.', File.separatorChar) + ".class";
    try {
        in = new FileInputStream(path);
        out = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len = 0;
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        return out.toByteArray();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        in.close();
        out.close();
    }
    return null;
}

}package com.itcast.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestMyClassLoad {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//自定义类加载器的加载路径
MyClassLoader myClassLoader = new MyClassLoader(“E:\lib”);
//包名+类名
Class c = myClassLoader.loadClass(“com.itcast.test.Test”);
if (c != null) {
Object obj = c.newInstance();
Method method = c.getMethod(“say”, null);
method.invoke(obj, null);
System.out.println(c.getClassLoader().toString());
}
}
}

首先:需要在E盘下创建一个lib文件夹,再把idea项目中src的com文件夹直接复制 E盘lib文件夹即可。
打印结果如下:
在这里插入图片描述

备注:以上代码均在JDK1.8版本下进行,JDK1.9类加载器略有不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值