浅谈Java类的加载

早就打算写一篇关于类加载的博客,因为我也曾被这个过程困扰过很久,今天终于有时间写下一些心得

网上有许多关于类加载的帖子,许多都有不严谨、错误,如果你想正确地,更深入地学习,有必要去oracle的官网看Java虚拟机规范。

路径有点深,跟着下面的图片左上角去找
在这里插入图片描述

----------进入正题----------

这里我们准备一个jclasslib bytecode viewer工具(GitHub里面有),用来解析字节码文件

Java文件编译成字节码(.class)文件后,需要通过加载这一重要环节进入虚拟机中

基本环节:

加载–链接–初始化

Loading(加载)

规范中有这样一句话:There are two kinds of classloaders: the bootstrap class loader supplied by the Java Virtual Machine, and user-defined class loaders. 说明了有两类加载器(bootstrap class loader和user-defined class),顾名思义,区别在于是否继承了抽象类ClassLoader,这些加载器有上下级关系,加载遵循双亲委派机制,即加载器收到加载请求时总会先委托上一级加载器加载,如果上一级不能加载再由自己加载

核心API由bootstrap class loader加载,例如要加载java.lang.String类,bootstrap class loader收到请求,而自己已经是加载器最高级,所以立即加载String类

加载这一步会生成一个Class对象(Java的反射机制),规范中有详细说明

而且注意加载一个类前会先加载它的父类,这里用代码演示

public class LodingTest {
 
 static class Father{
  static {
   System.out.println("加载父类中....");
  }
 }
 
 static class Son extends Father{
  static {
   System.out.println("加载子类中...");
  }
 }
 public static void main(String[] args) {
  new Son();
 }
}

在这里插入图片描述

Linking(链接)

Linking a class or interface involves verifying and preparing that class or interface, its direct superclass, its direct super interfaces, and its element type (if it is an array type), if necessary. Linking also involves resolution of
symbolic references in the class or interface, though not necessarily
at the same time as the class or interface is verified and prepared.

总结出三个部分:verifying preparing resolution 规范中都有详细说明

verifying即验证字节码文件,出于虚拟机安全的考虑

preparing过程会为一个类或者接口创建静态属性即类变量(类变量不带final修饰,因为final static 修饰的为常量,编译阶段已经处理)和静态代码块,并且初始化默认值,但强调一下此过程不需要执行要等到任何指令,默认初始化并不是显示地初始化,显示地初始化要到initializing阶段执行

resolution即symbolic reference(符号引用)的解析,与很多虚拟机指令如anewarray, checkcast, getfield,getstatic…的执行有关,resolution是一个非常复杂的过程,规范中用了相当长的篇幅叙述…

Initialization

初始化阶段即执行一个类或接口的初始化方法 clinit,这个方法不需要我们自己写,而且clinit这个名字在java程序中也不是合法的标识符,没有办法写,这个方法由虚拟机自动收集静态赋值动作和静态代码块完成

这里我们用我们上面提到的工具演示
代码

public class LodingTest {
 
 static int a = 1;
 static {
  double b = 0.5;
 }
 public static void main(String[] args) {
  new LodingTest();
 }
}

生成字节码文件后,用工具解析
在这里插入图片描述
我们能看到字节码中自动生成了clinit 方法
类的初始化也是有条件的,例如,C将被初始化当
执行任意一条和C的引用有关的虚拟机指令
初始化一个C的子类
如果C是个接口且声明了一个个非抽象、非静态的方法,初始化直接或间接实现C的类

----------------------------------------------------------------

先写到这里,有需要再补充…

发布了2 篇原创文章 · 获赞 4 · 访问量 113

猜你喜欢

转载自blog.csdn.net/weixin_44883515/article/details/104953670