Java 类加载过程与类加载器详细介绍

yizhihongxing

让我为您讲解一下 "Java 类加载过程与类加载器详细介绍" 的完整攻略。

什么是类加载?

Java 语言是一种面向对象程序设计语言,其中最基本的组成单位是类。在 Java 语言中,类是由编译器编译 Java 代码后生成的字节码文件,这些字节码文件最终是由 Java 虚拟机来执行的。而在 Java 虚拟机的执行过程中,类加载器则负责将类文件加载到 JVM 中进行执行。

类加载过程是 Java 虚拟机执行程序时不可避免的一部分。类加载器负责管理 Java 类的加载、验证、解析和初始化等工作。只有在类被加载到 JVM 中后,才能开始执行相应的方法。

类加载的过程

类加载过程主要包括三个步骤:加载、链接和初始化。

加载

类加载器在类加载的过程中,首先需要从 classpath 中找到指定的字节码文件,并将其加载到 JVM 的内存中。在运行期间,类加载器将一个类的全限定名作为输入,然后返回表示此类的实例。类加载器并不关心这些字节码的来源,可以来自本地磁盘、网络等任何地方。

一个类文件只有在第一次加载时才会被解析并填充到内存中去

链接

在将 Class 文件加载到内存之后,Java 虚拟机还需要完成以下三个步骤的检查和处理:

  1. 验证:主要是对字节码文件进行验证,确保其格式符合 JVM 的要求,比如能否转换成正确的 Class 文件格式,是否有明显的安全问题等;
  2. 准备:这个阶段主要是为类变量(即 static 变量)分配内存并设置其初始值;
  3. 解析:将符号引用转换为直接引用,这是Java虚拟机规范预留的,目的在于支持使用 Java 语言实现的“动态绑定”。

初始化

在完成类的加载和链接之后,JVM 执行类的初始化操作。初始化过程是类加载过程中的最后一个阶段,该阶段目的是对类变量进行赋值,静态语句块和静态方法也会得以执行。类中的类变量是在准备阶段分配的内存空间,而在初始化阶段会完成变量的赋值,因此才能被使用。一个类在进行初始化时,可能需要执行与其他类有依赖关系的变量或方法。JVM 通常采用按照代码中顺序初始化的方式完成初始化工作。

类加载器分类

Java 类加载器一般被分为以下四种:

  1. BootStrapClassLoader(启动类加载器):用于加载 JDK 中的核心类库,是 Java 虚拟机自带的类加载器,负责加载 Java 的核心类库,如 JRE/lib 目录下的 rt.jar 包中所有的类。
  2. ExtensionClassLoader(扩展类加载器):用于加载在 Java 虚拟机设置的扩展目录(JAVA_HOME/jre/lib/ext 或者由系统变量 java.ext.dir 指定的目录)中的所有类。它的父类加载器为启动类加载器。
  3. AppClassLoader(系统类加载器):负责加载应用程序 ClassPath 目录下的所有类。当执行 java 命令时,系统类加载器会自动从环境变量 CLASSPATH 中获取类加载路径。
  4. 自定义类加载器:开发人员可以根据需要自己编写类加载器,继承自ClassLoader,实现findClass()方法,通过定义相应的规则,例如从网络上下载类文件,从特定文件夹中读取类文件等。

类加载器示例

下面,我用两个示例来说明类加载器的应用。

示例一

假设我们有以下两个 Java 类,它们位于 /path/to/ClassA 和 /path/to/ClassB 中,ClassB 文件需要依赖 ClassA。

package org.example;

public class ClassA {
    public void hello() {
        System.out.println("Hello from ClassA!");
    }
}
package org.example;

public class ClassB {
    public void hello() {
        ClassA a = new ClassA();
        a.hello();
        System.out.println("Hello from ClassB!");
    }
}

在我们使用 javac 命令编译这两个类之后,会在我们当前的工作目录下生成两个 .class 文件,在这里使用相对路径表示。

接下来,我们在一个 Java 类中,使用自定义类加载器加载这两个类,并执行 ClassB 类的 hello 方法。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CustomClassLoader extends ClassLoader {
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = Files.readAllBytes(Paths.get(name + ".class"));
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            return super.findClass(name);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        CustomClassLoader customClassLoader = new CustomClassLoader();
        Class<?> classA = customClassLoader.loadClass("org.example.ClassA");
        Object instanceA = classA.newInstance();
        Method methodA = classA.getMethod("hello");
        methodA.invoke(instanceA);

        Class<?> classB = customClassLoader.loadClass("org.example.ClassB");
        Object instanceB = classB.newInstance();
        Method methodB = classB.getMethod("hello");
        methodB.invoke(instanceB);
    }
}

通过运行上述代码,我们可以看到,类 ClassB 被成功加载并调用。

示例二

再来看一个示例,它用这自定义类加载器,从指定的远程 HTTP 服务器中加载类文件。

我们使用 Maven 的一个插件(远程 JAR 插件)来实现将 Demo 类加载到服务器上的过程。在这个示例中,我们假设这个插件将 Demo 类和它所依赖的类上传到了服务器的指定目录下。

import java.net.URL;
import java.net.URLClassLoader;

public class CustomClassLoaderDemo {
    public static void main(String[] args) throws Exception {
        URL[] urls = {new URL("http://localhost:/path/to/Demo.jar")};
        URLClassLoader classLoader = new URLClassLoader(urls);
        Class<?> clazz = classLoader.loadClass("org.example.Demo");
        Object obj = clazz.newInstance();
        System.out.println(obj.toString());
    }
}

在这个示例中,我们使用了 URLClassLoader 类来加载 Demo 类。它的作用是创建一个新的 URLClassLoader 对象,然后从指定的远程服务器中加载 Demo 类。需要注意的是,在使用URLClassLoader类时一定要提供正确的 URL 连接,否则会抛出 MalformedURLException 异常。

以上就是 Java 类加载过程与类加载器详细介绍的完整攻略,希望能够帮助到您。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 类加载过程与类加载器详细介绍 - Python技术站

(0)
上一篇 2023年6月25日
下一篇 2023年6月25日

相关文章

  • 如何验证IP地址?

    如何验证IP地址? 验证IP地址是确认一个给定的IP地址是否有效和合法的过程。下面是一个详细的攻略,用于验证IP地址的有效性。 步骤1:检查IP地址格式 首先,我们需要检查IP地址的格式是否正确。IP地址由四个十进制数(每个数的范围是0到255)组成,用点分隔。例如,正确的IP地址格式是192.168.0.1。以下是检查IP地址格式的示例代码: import…

    other 2023年7月30日
    00
  • Javascript 对象的解释

    Javascript 对象的解释 Javascript 是一种面向对象的编程语言,对象是 Javascript 中最重要的概念之一。对象是一种复合数据类型,可以用来存储和组织相关的数据和功能。 对象的定义和创建 在 Javascript 中,对象可以通过两种方式进行定义和创建:字面量和构造函数。 1. 字面量方式 使用字面量方式可以直接创建对象,通过使用花括…

    other 2023年10月14日
    00
  • django admin后管定制-显示字段的实例

    当我们在使用Django开发Web应用时,会使用到Django admin作为管理后台。但是Django admin默认情况下只显示了一些基本字段,有时我们需要定制显示哪些字段以及字段的顺序,本文将为你详细讲解Django admin后管定制-显示字段的实例。 Django admin显示字段默认值 首先,我们需要了解在Django admin中,每个Mod…

    other 2023年6月25日
    00
  • redis实现分布式session的解决方案

    下面是关于“redis实现分布式session的解决方案”的完整攻略: 什么是分布式session? Session一般指的是“会话”,分布式session指的就是用户的会话信息存储在多个节点上,而不是只存储在一台服务器上。分布式session可以让多个服务器共同维护用户状态,同时也可以分担单个服务器的压力,降低服务的单点故障。 为什么要使用redis实现分…

    other 2023年6月26日
    00
  • CCS进阶——div的宽度和高度是由什么决定的?

    CSS进阶——div的宽度和高度是由什么决定的? 介绍 CSS是构建网页的重要技术之一,其中的div元素被广泛使用。div元素允许我们将内容划分为不同的块,以达到更好的排版和布局效果。然而,对于初学者来说,div元素的宽度和高度的处理可能会带来一定的困难。那么,div的宽度和高度是由什么决定的呢? 宽度的决定 默认宽度 在未设置样式的情况下,div元素的默认…

    其他 2023年3月28日
    00
  • 如何能在局域网中隐藏电脑及IP地址(防止被攻击)

    如何在局域网中隐藏电脑及IP地址(防止被攻击) 在局域网中隐藏电脑及IP地址可以增加网络安全性,防止被攻击。下面是一些方法和示例说明,帮助你实现这一目标。 方法一:使用网络地址转换(NAT) 网络地址转换(NAT)是一种常用的方法,可以隐藏局域网中的电脑及IP地址。NAT将局域网内部的私有IP地址转换为公共IP地址,使得外部网络无法直接访问到内部电脑的真实I…

    other 2023年7月31日
    00
  • c语言中static的用法详细示例分析

    C语言中static的用法详细示例分析 在C语言中,static是一个关键字,用于声明静态变量、静态函数和限制变量的作用域。下面将详细讲解static的用法,并提供两个示例说明。 1. 静态变量 静态变量是在函数内部声明的变量,但其生命周期与程序的整个运行时间相同。静态变量只会被初始化一次,并且在函数调用之间保持其值不变。 #include <stdi…

    other 2023年7月29日
    00
  • linux中的常用命令与快捷键介绍

    接下来我会详细介绍“linux中的常用命令与快捷键”,以下是完整攻略: Linux中的常用命令与快捷键介绍 常用命令 文件/目录操作命令 ls: 列出当前目录下的所有文件和文件夹 cd <directory>: 进入指定的目录 mkdir <directory>: 创建新的目录 rm <file>: 删除文件 rm -r …

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部