什么是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日

相关文章

  • 使用jdk1.8实现将list根据指定的值去分组的操作

    这里是使用JDK1.8实现将List根据指定的值进行分组的完整攻略。 1. 需求分析 我们要实现将List根据指定的值进行分组,要求在分组结果中,具有相同指定值的元素会被分到同一组中。例如,假设我们有如下的Student类: public class Student { private int id; private String name; private…

    Java 2023年5月26日
    00
  • 如何利用java实现生成PDF文件

    下面给出关于“如何利用Java实现生成PDF文件”的完整攻略: 如何利用Java实现生成PDF文件 一、背景介绍 PDF 是 Portable Document Format 的缩写,即“可移植文档格式”。它是由 Adobe 公司开发的一种文件格式,具有在不同平台上跨越不同应用程序的相同外观的特性。本攻略介绍如何利用 Java 实现生成 PDF 文件。 二、…

    Java 2023年5月19日
    00
  • 深入Java Final

    深入Java Final的完整攻略 什么是Java Final Java Final关键字表示某个实体不可更改,这个实体可能是变量、方法或类。 当我们将一个变量声明为final时,它表示该变量只能被赋值一次,一旦被赋值就不能再改变。相应地,当我们将一个方法声明为final时,它表示该方法不能再被子类重写。最后,当我们将一个类声明为final时,它表示该类不能…

    Java 2023年5月26日
    00
  • 搭建MyBatis-Plus框架并进行数据库增删改查功能

    搭建MyBatis-Plus框架并进行数据库增删改查功能的完整攻略如下: 准备工作 下载和安装JDK和MySQL; 创建一个Spring Boot项目; 在项目中添加mybatis-plus-boot-starter依赖; 在项目的配置文件中配置数据库连接信息。 配置MyBatis-Plus框架 创建数据库表; 创建实体类,并在类上添加@TableField…

    Java 2023年6月1日
    00
  • JDK19新特性使用实例详解

    JDK19新特性使用实例详解 本文将详细讲解JDK19新特性的使用实例,并给出两条具体的示例说明。 1. Lambda表达式 Lambda表达式是JDK8引入的一个重要特性,它可以使代码更加简洁、易读。我们来看一个实例: // 普通的排序方法 Collections.sort(list, new Comparator<String>() { @O…

    Java 2023年5月26日
    00
  • Struts2通过自定义标签实现权限控制的方法

    Struts2框架通过自定义标签实现权限控制是一种比较常用的方法。下面分为两步详细讲解如何实现: 第一步:自定义标签 首先需要定义一个标签处理类,这个类必须继承TagSupport类,实现其中的doStartTag方法 public class AuthTag extends TagSupport { private String permission; p…

    Java 2023年5月20日
    00
  • springboot快速整合Mybatis组件的方法(推荐)

    下面是关于springboot快速整合Mybatis组件的方法的攻略,包括以下几个步骤: 1.基础环境搭建 首先,我们应该新建一个SpringBoot工程,选择maven进行构建。我们需要在pom.xml文件中添加Mybatis和Mybatis-spring-boot-starter依赖项。核心代码如下: <dependency> <gro…

    Java 2023年5月19日
    00
  • SpringBoot雪花算法主键ID传到前端后精度丢失问题的解决

    首先,我们需要了解雪花算法主键ID的生成方式,它会生成一个64bit的整数,其中高42位代表毫秒级时间戳,中间的位数为机器ID和进程ID等信息,低位12位为序列号。因此,我们需要进行精度处理,以避免前端显示时的精度丢失问题。 解决这个问题的方法是将生成的Long类型的主键ID转换为String类型,在传到前端时进行显示。SpringBoot提供了一个注解@J…

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