什么是Java类装载机制?

Java类装载机制指的是JVM如何加载和查找类的过程。在Java程序运行过程中,JVM需要定位并加载需要使用的类文件,Java类装载机制便是完成这个过程的。

Java 类装载有五个过程:加载、验证、准备、解析和初始化。以下是Java类装载的详细使用攻略。

1. 加载

加载是指将类的字节码数据加载到内存中,并为之创建一个 java.lang.Class 对象。加载阶段需要执行如下三个过程:

  • 通过类的全名获取定义此类的二进制字节流。
  • 将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 在内存中生成一个 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

2. 校验

校验是指对加载的类文件进行验证,确保符合JVM规范和应用规范。校验阶段的主要任务就是进行以下四个检查:

  • 文件格式的验证:检查class字节码的格式是否合法。
  • 元数据的验证:对类自身信息进行语义分析,保证不存在不符合Java语言规范的元数据信息。
  • 字节码验证:通过数据流检查和控制流检查等方式,确定代码语义是合法的、符合逻辑的。
  • 符号引用验证:保证解析动作能正确执行。

3. 准备

准备是指为类的静态变量分配内存并赋予初始值的过程。这些变量所使用的内存都将在方法区中进行分配。注意,这时候进行默认初始化,所以不会对类的静态变量进行显示赋值。

4. 解析

解析是Java将类的二进制数据中的符号引用转换为直接引用的过程。换而言之,就是虚拟机将常量池中的符号引用替换为直接引用的过程。解析一般发生在以下情况:

  • 调用类的静态方法或属性时,解析类获得程序要加载的对象。
  • 常量池中的符号引用转变为直接引用。

5. 初始化

初始化是类装载的最后一个阶段,也是真正意义上类准备开始执行的阶段。初始化阶段是执行类构造器()方法的过程,()方法由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。当构造器运行时,初始化会按照程序员指定的顺序(即静态变量定义的顺序)对静态变量进行初始化。详细的执行顺序可以参见JLS,此处不作赘述。

至此,Java类装载机制的所有过程都分析完毕。下面通过两个示例来详细说明Java类装载机制的实现过程。

示例一:动态代理类装载和执行

我们通过动态代理示例看看一个类的初始化过程。在Java中,动态代理技术是很常用的,可以通过动态代理实现对象的远程调用等。下面是一个简单的动态代理示例代码。

public interface HelloService {
    void sayHello(String name);
}

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello, " + name);
    }
}

public class HelloProxy implements InvocationHandler {
    Object target;

    public HelloProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before Method Invoke");
        Object result = method.invoke(target, args);
        System.out.println("After Method Invoke");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建被代理对象
        HelloService helloService = new HelloServiceImpl();
        // 创建代理对象
        HelloService proxy = (HelloService) Proxy.newProxyInstance(
                HelloService.class.getClassLoader(),
                new Class[]{HelloService.class},
                new HelloProxy(helloService)
        );
        // 调用方法
        proxy.sayHello("world");
    }
}

在运行该示例过程中,Java类装载机制的过程如下:

  1. 首先加载 HelloService 接口。由于该接口并没有任何实现,所以Java只将其字节码加载到内存中,不会进行进一步的校验、准备和初始化操作。
  2. 加载 HelloServiceImpl 类,由于该类实现了 HelloService 接口,所以在进行加载后,Java会进行校验、准备、初始化等操作并生成 HelloServiceImpl 类的对象。
  3. 加载 HelloProxy 类,由于该类实现了 InvocationHandler 接口,所以在加载后,Java会进行校验、准备、初始化等操作,并生成 HelloProxy 类的对象。
  4. Main 类中,先创建了 HelloServiceImpl 的一个实例,通过JVM方法Proxy.newProxyInstance()获取 HelloService 的一个代理对象。代理对象的实例化过程完全依赖于动态实现,所以需动态生成字节码。Java会先加载HelloProxy 类,由于HelloProxy 实现了InvocationHandler 接口,这时 Java会将该对象转化为字节码,在运行时动态生成代理类。这个过程比较常见,前几个步骤详见前面的分析内容。代理类实现了 HelloService接口,并将 HelloProxy 实例作为目标对象传递进去,代理对象转化为了默认实现的 HelloService 接口,以此完成了代理关系的建立。

整个过程中,Java类装载机制主要用到了对 HelloServiceImplHelloProxy 和代理类的加载、校验、准备和初始化等过程,我们可以清晰地在代码执行过程中看到加载类(使用 ClassLoader等类),校验类(字节码格式是否正确),运行静态代码块(初始化类),以及动态生成代理类等过程。

示例二:静态变量初始化

下面通过一个静态变量初始化的示例来看看在Java类装载机制中的实现过程。

public class StaticTest {
    private static int num = 1;
    private static final String s = "Hello, world";

    static {
        System.out.println("静态代码块。。。");
        num = 2;
    }

    public static void main(String[] args) {
        System.out.println(Parent.num);
        System.out.println(Parent.s);
    }
}

在运行该示例过程中,Java类装载机制的过程如下:

  1. 首先加载 StaticTest 类文件。
  2. 在加载静态变量 num 时,执行赋值操作,并将赋值结果2存储到内存中。当第二次访问时,直接从内存中取值输出。
  3. 在加载静态变量 s 时,会因为 sfinal,这时候赋值操作会直接跳过。在静态代码块中将 num 的值设置为2。

整个过程中,Java类装载机制主要用到了对 StaticTest 类的加载、静态变量nums 的赋值操作,静态代码块最后对 num 的赋值操作。我们根据代码实际执行过程,可以看到Java类装载机制对于静态变量赋值等操作的处理过程。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:什么是Java类装载机制? - Python技术站

(0)
上一篇 2023年5月11日
下一篇 2023年5月11日

相关文章

  • SpringBoot2整合Drools规则引擎及案例详解

    Spring Boot 2整合Drools规则引擎及案例详解可以分为以下几个步骤: 第一步:引入Drools依赖 在pom.xml文件中引入Drools的依赖: <dependency> <groupId>org.drools</groupId> <artifactId>drools-core</arti…

    Java 2023年5月19日
    00
  • spring boot整合mybatis+mybatis-plus的示例代码

    下面我给您讲解一下“spring boot整合mybatis+mybatis-plus的示例代码”的完整攻略。 步骤1 – 添加依赖 首先,我们需要在 pom.xml 中添加以下依赖: <!– Spring Boot Mybatis Starter –> <dependency> <groupId>org.mybati…

    Java 2023年5月20日
    00
  • IDEA 中使用 ECJ 编译出现 java.lang.IllegalArgumentException的错误问题

    首先,我们需要了解什么是ECJ。ECJ(Eclipse Compiler for Java)是一款基于Eclipse平台的Java编译器,它可以用于将Java代码编译成字节码。而IDEA是一款广受欢迎的Java开发工具,它可以集成ECJ编译器,来编译Java代码。如果在IDEA中使用ECJ编译出现了java.lang.IllegalArgumentExcep…

    Java 2023年5月26日
    00
  • Spring使用Setter完成依赖注入方式

    Spring使用Setter完成依赖注入方式的完整攻略 什么是依赖注入 依赖注入(Dependency Injection,简称DI)是一种编程思想,它将一个对象所依赖的其他对象交给容器在运行期间动态地注入。这样可以消除类与类之间的耦合,降低代码的复杂度,提高代码的可维护性和可复用性。 Spring框架中使用依赖注入来管理对象之间的依赖关系,开发者只需要负责…

    Java 2023年5月27日
    00
  • SpringSecurity oAuth2.0的四种模式(小结)

    Spring Security OAuth2.0提供了四种模式:授权码模式、密码模式、客户端凭证模式和简化模式。每种模式都有不同的应用场景,下面将详细介绍这四种模式的特点和使用场景。 1. 授权码模式 授权码模式是OAuth2.0中最常用的授权模式,适合有服务端的应用场景。授权码模式的具体流程如下: 用户向客户端提供用户名和密码。 客户端使用这些信息,向授权…

    Java 2023年6月3日
    00
  • Java基础泛型详情

    Java基础泛型详情 什么是泛型 泛型是Java的一种特性,可以让用户在编写代码时将数据类型作为参数进行传递。通过泛型,Java可以实现更加安全、灵活和可读性强的代码。泛型的本质是参数化类型,也就是说,使用时可以在代码中传递各种类型的数据,这样可以避免一些常见的类型错误。 泛型的语法 定义泛型类可以使用以下语法: class 类名<泛型参数1, 泛型参…

    Java 2023年5月26日
    00
  • 详解spring Boot 集成 Thymeleaf模板引擎实例

    我来为你详细讲解“详解SpringBoot集成Thymeleaf模板引擎实例”的攻略。 概述 在SpringBoot应用中,Thymeleaf是一种流行的模板引擎,具有出色的可维护性和可扩展性。本文将详细介绍如何使用SpringBoot集成Thymeleaf模板引擎,帮助开发者快速地搭建Web应用,并在其中使用Thymeleaf实现模板渲染。 集成Thyme…

    Java 2023年5月15日
    00
  • java 重试框架 sisyphus 入门介绍

    下面是关于 Java 重试框架 Sisyphus 的入门介绍及完整攻略。 Sisyphus 是什么? Sisyphus 是一个 Java 重试框架。它的主要作用是在某些特定场景下,自动地对失败的操作进行重试。 Sisyphus 可以非常方便地配置,适用于多种使用场景,包括但不限于网络请求、数据库操作等。Sisyphus 还有非常完善的监控和日志记录机制,可以…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部