美文网首页JVM虚拟机
JVM1.ClassLoader加载和初始化类流程

JVM1.ClassLoader加载和初始化类流程

作者: ygxing | 来源:发表于2019-06-14 17:30 被阅读0次

一.Java类执行步骤


我们要执行Hello.java,其代码如下:

public class Hello{
    public static void main(String[] args){
        System.out.println("hello,world!");
    }
}
编译:

将java文件编译为JVM可读的字节码(class文件),我们经常使用的代码编辑器,在运行项目的时候,就会将我们写的java文件编译成字节码

javac Hello.java
//执行完成之后,会生成一个Hello.class
运行:

ClassLoader将字节码放到Java虚拟机中执行

java Hello
//运行结果: hello,world!

二. 类加载器ClassLoader

1. 功能作用

ClassLoader是类加载器, 程序在运行过程中,会根据需要,通过Java类加载器(ClassLoader)来加载某个Class文件到内存中,只有Class文件被加载到JVM中之后,才能被其他Class使用

2. 类加载步骤

2.1. 加载(Loading)
  • 通过类全名,例如:java.lang.Integer
  • 主要通过URLClassLoader, 查找并加载类的Class文件(字节码)到JVM方法区内(JDK1.8使用本地内存), 可以加载的Class文件如下:
    • 加载磁盘class文件
    • 加载网络Class文件
    • 加载ZIP,Jar等归档文件
    • 编译Java源文件
  • 然后在堆内存创建一个Class对象,用来封装类在方法区的数据结构
/**
 * URLClassLoader测试类
 */
public class URLClassLoaderTest{
    public static void main(String[] args){
        System.out.println("hello,world!");
    }
}
2.2. 链接(Linking)
  • 将各个class文件链接起来,配合工作
  • 链接分为验证,准备,解析三步
2.2.1 验证

重新进行编译检查,验证类的正确性,确保被加载的类可以正确执行

2.2.2 准备
  • 为类的静态变量分配内存空间
  • 并初始化为一个默认值
2.2.3 解析
  • 将类中的符号引用解析为直接引用
  • 符号引用: 在java中没有指针类型,某个对象就是一个符号引用
  • 直接引用: 指针指向某个地址空间,就是直接引用
2.3 初始化(Initialzation)
  • 为类的静态变量赋值
  • 执行静态代码块的代码
  • 类代码执行步骤:
    • 多个静态代码块和静态变量按顺序执行
    • 在对象创建过程中会先初始化成员变量
    • 然后执行构造代码块
    • 最后再执行构造方法
2.3.1 初始化测试
public class JVM {
    //静态成员变量
    public static int num1;
    static {
        //静态代码块,打印日志和赋值
        System.out.println("static1 num1 = " + num1);
        num1 = 2;
        System.out.println("static2 num1 = " + num1);
    }

    public static void main(String[] args) {
        //main方法打印num1
        System.out.println("main num1 = " + num1);
    }
}
//运行结果:
static1 num1 = 0
static2 num1 = 2
main num1 = 2
  • 运行结果分析
    • 首先ClassLoader准备内存空间,把静态变量num1设置为默认值0
    • 然后按顺序执行static代码块,打印num1的值
    • 然后再执行num1的赋值语句
    • 最后再打印num1的值
2.3.2 在哪些情况下初始化类

如果JVM没有使用类,那么类的加载过程到链接这一步就结束了,不会执行初始化的代码,也就是类需要我们主动使用,才会初始化类,类的主动使用有下面六种情况:

  • 初始化一个类的实例
  • 访问某个类的静态变量,或者为类的静态变量赋值
  • 调用了类的静态方法
  • 使用反射,实例化一个对象
  • 初始化一个子类,那么父类也会被初始化
  • JVM启动时标明了要启动的类,例如文章开头的java Hello
public class JVM {
    // 静态成员变量
    public static int num1;
    static {
        // 静态代码块,打印日志和赋值
        System.out.println("JVM static1 num1 = " + num1);
        num1 = 2;
        System.out.println("JVM static2 num1 = " + num1);
    }

    public JVM() {
        // 构造函数,赋值为3
        num1 = 3;
    }

    public static void test() {
        //静态方法 打印值
        System.out.println("JVM test num1 = " + num1);
    }
}
(1). 初始化一个类的实例
//JVM测试类
public class JVMTest {
    public static void main(String[] args) {
        JVM jvm = new JVM();
        System.out.print("JVMTest JVM.num1 = " + JVM.num1);
    }
}
//运行结果:
JVM static1 num1 = 0
JVM static2 num1 = 2
JVMTest JVM.num1 = 3
  • 运行结果分析:
    • 当执行new JVM()的时候,会先加载JVM这个类
    • 于是先加载到JVM中,然后再链接
    • 链接的准备过程中,先为JVM类的静态变量分配内存空间,并设置为默认值
    • 然后在执行JVM的static静态代码块,打印日志,并赋值
    • 最后执行构造方法
(2). 访问某个类的静态变量,或者为类的静态变量赋值
//JVM测试类
public class JVMTest {
    public static void main(String[] args) {
        System.out.print("JVMTest JVM.num1 = " + JVM.num1);
    }
}
运行结果:
JVM static1 num1 = 0
JVM static2 num1 = 2
JVMTest JVM.num1 = 2
(3). 调用了类的静态方法
//JVM测试类
public class JVMTest {
    public static void main(String[] args) {
        JVM.test();
    }
}
运行结果:
JVM static1 num1 = 0
JVM static2 num1 = 2
JVM test num1 = 2
(4). 使用反射,实例化一个对象
//JVM测试类
public class JVMTest {
    public static void main(String[] args) {
        try {
            Class.forName("jvm.JVM");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//运行结果
JVM static1 num1 = 0
JVM static2 num1 = 2
(5). 初始化一个子类,那么父类也会被初始化
//继承JVM的子类
public class JVM2 extends JVM {
    //num2静态变量
    public static int num2 = 2;
    static {
        //打印日志和赋值
        System.out.println("JVM2 static1 num2 = " + num2);
        num2 = 4;
        System.out.println("JVM2 static2 num2 = " + num2);
    }
    public static void main(String[] args) {
        //main方法 实例化一个JVM2对象
        new JVM2();
    }
}
//运行结果
JVM static1 num1 = 0
JVM static2 num1 = 2
JVM2 static1 num2 = 2
JVM2 static2 num2 = 4
  • 运行结果分析:
    • 当我们实现子类的时候,会先加载父类

相关文章

  • JVM1.ClassLoader加载和初始化类流程

    一.Java类执行步骤 我们要执行Hello.java,其代码如下: 编译: 将java文件编译为JVM可读的字节...

  • 1 类的加载

    类的加载 类的加载是指什么? 类的加载流程是什么?各个阶段做了什么事情? 加载、验证、准备、解析、初始化 加载成二...

  • Java对象初始化

    对象初始化流程: 加载字节码(只加载一次)->初始化字段->父类构造方法->自身构造方法。 静态数据的初始化 静态...

  • JVM扩展(1):试图讲清楚 类的加载与对象的创建

    1:类与对象 2:Java源代码经过 编译解释成二进制文件 流程 3:流程图中类加载(包括类初始化) 流程 4:...

  • iOS底层原理19:类和分类的加载

    前面已经探究了类的加载流程,类分为懒加载类和非懒加载类,他们有不同加载流程,下面来探究下分类的加载,以及分类和类搭...

  • JVM类的初始化顺序

    当new一个类对象时,如果该类还未被JVM所加载,则JVM首先加载该类,加载类流程中会有类的初始化操作,也就是类中...

  • JVM(五)类的初始化

    类的初始化 类的初始化步骤 假如类还未被加载和连接,那就先进行加载和连接 假如类存在直接父类,并且这个父类还未被初...

  • JVM类加载入门

    一 类加载顺序 class类加载-->验证-->准备--->解析--->初始化 class类加载:通过类加载器加载...

  • Java类加载机制

    Java类加载机制 类的生命周期是:加载->验证->准备->解析->初始化->使用->卸载,只有在准备阶段和初始化...

  • 类的初始化和实例初始化

    类初始化过程 一个类要创建实例需要先加载并初始化该类main方法所在的类需要先加载和初始化 一个子类要初始化需要先...

网友评论

    本文标题:JVM1.ClassLoader加载和初始化类流程

    本文链接:https://www.haomeiwen.com/subject/iueefctx.html