一、引入
包括以下 7 个阶段:
-
加载(Loading)
-
验证(Verification)
-
准备(Preparation)
-
解析(Resolution)
-
初始化(Initialization)
-
使用(Using)
-
卸载(Unloading)
类加载过程
包含了加载、验证、准备、解析和初始化这 5 个阶段。(详细参看其他博客)
准备阶段:
类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存。
全局变量(成员变量,可能也会被人被称为实例变量):比如在类的方法外定义的变量public int cnt=1;是全局变量或者叫做字段。
1.全局变量既不在堆中,也不在栈中,而是在全局区(静态区),全局变量的存储和静态变量是在一起的。(有点蒙,为什么这么说,静态区难道不是属于方法区?可能是分得更细)
2.初始化的全局变量和静态变量在同一块区域“数据段”;未初始化的全局变量和静态变量在相邻的另一块区域“数据段中的BSS段:特点是可读可写;在程序执行前BSS段会清0”
实例变量不会在这阶段分配内存,它会在对象实例化时随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。
准备阶段初始值一般为 0 值,例如下面的类变量 value 被初始化为 0 而不是 123。
public static int value = 123;
如果类变量是常量,那么它将初始化为表达式所定义的值而不是 0。例如下面的常量 value 被初始化为 123 而不是 0。
public static final int value = 123;
初始化
二、这是由于类加载过程的准备阶段+初始化阶段导致的“坑(结果)”:
问题产生:static Main2 m = new Main2();与public static int cnt=1;声明的先后位置导致的
/** 1. 因为static Main2 m = new Main2();用了static修饰,所以类初始化的时候,会执行一次构造方法 2. 因为static Main2 m = new Main2();是写在public static int cnt=1;之后的,所以syso将会输出cnt=2 **/ public class Main2 { public static int cnt=1; static Main2 m = new Main2(); public Main2(){ cnt++; System.out.println(" cnt="+cnt); } public static void main(String[] args) {} } //输出cnt=2
/** 静态字段的初始化规则为:1.首先默认初始化为0,null等等;然后再显示初始化。 所以显示初始化m=new Main2()的之前,cnt=0;然后再执行构造函数,其中cnt++使cnt的值变为1;然后再显示初始化cnt=1,所以syso输出cnt=1 **/ public class Main2 { static Main2 m = new Main2(); public static int cnt=1; public Main2(){ cnt++; System.out.println(" cnt="+cnt); } public static void main(String[] args) {} } //输出cnt=1