Java类加载三:类加载机制分析(代码分析)

时间:2022-12-29 09:33:35

我们通过代码来分析类的加载逻辑,如有不正之处,欢迎批评指正。

代码1:

package com.ips.classloader;

public class Coordinate {
private static Coordinate obj = new Coordinate();
public static int x;
public static int y = 1;

private Coordinate() {
x++;
y++;
}

public static Coordinate getInstance() {
return obj;
}

public static void main(String[] args) {
Coordinate.getInstance();
System.out.println("x = " + x);
System.out.println("y = " + y);
}
}

执行结果:

x = 1
y = 1

代码2:

package com.ips.classloader;

public class Coordinate {
public static int x;
public static int y = 1;

private static Coordinate obj = new Coordinate();

private Coordinate() {
x++;
y++;
}

public static Coordinate getInstance() {
return obj;
}

public static void main(String[] args) {
Coordinate.getInstance();
System.out.println("x = " + x);
System.out.println("y = " + y);
}
}

执行结果:

x = 1
y = 2

分析:

代码1中成员变量声明顺序如下:

    private static Coordinate obj = new Coordinate();
public static int x;
public static int y = 1;

代码2中成员变量声明顺序如下:

    public static int x;
public static int y = 1;
private static Coordinate obj = new Coordinate();

我们只是对Coordinate类的变量声明顺序做了调整,执行结果确是不一样的,为什么会这样呢,我们分析一下代码的执行逻辑。

这里涉及到类加载过程的三个阶段:装载、链接、初始化。

  1. 装载:就是将class文件读入内存,并对字节码的检查,看是否符合class文件规范,并在内存中生成一个代表类或接口的 java.lang.Class 实例。
  2. 链接:对类的基本类型变量、引用类型变量进行空间分配,同时对基本类型变量进行赋值(系统默认值)。
  3. 初始化:按照代码的书写顺序进行执行(这里涉及到指令重排,严格意义上代码不是按照书写顺序执行的),也就是说在初始化之前只是为类分配了存储空间,具体的变量值是系统默认值。

代码1分析:

  1. 装载:将Coordinate类加载至内存中
  2. 链接:为类的变量分配内存空间,分配顺序为先基本类型后引用类型,因此该类的分配顺序为x、y、obj,系统赋予的默认值为0,0,null
  3. 初始化:按它们声明的顺序进行初始化,先初始化obj ,执行new操作后使x、y的值分别为1,3;然后再初始化x、y,x没有指定值所以不执行保留值1,y被赋值为2,故最终结果输出为1,2。

代码2分析:
1. 装载:将Coordinate类加载至内存中
2. 链接:为类的变量分配内存空间,分配顺序为先基本类型后引用类型,因此该类的分配顺序为x、y、obj,系统赋予的默认值为0,0,null
3. 初始化:按它们声明的顺序进行初始化,先初始化x、y,x没有指定值所以不进行赋值,保留系统默认值,y被赋值为2,之后初始化obj,执行new操作后,x、y的值分别为1、3,故最终结果输出为1,3。

若我们将x、y修改为实例变量(非静态变量),执行结果肯定是1、3,两个类中的非静态变量值是互不影响的。