初始化与清理(从jvm类加载解释static,final加载顺序,让你一次就懂)

1,569 阅读3分钟

初始化与清理(从jvm类加载解释static,final加载顺序,让你一次就懂)

先说基础结论

  • static关键字不能应用于局部变量,因此它只能作用于域
  • static域(静态变量),static代码块只初始化一次
  • 构造器实际上是静态方法(static修饰的方法)
  • 初始化顺序:静态域(static代码块) > 变量 > 构造方法
  • 父类>子类

类加载过程

加载 - 验证 - 准备 - 解析 - 初始化(5个阶段)

  • 加载 是去加载.class文件的字节码
  • 验证 对字节码是否是安全的做验证
  • 准备 是初始化static域为默认值(参考下面表格)
public static int value = 123 ; //这个阶段先是value = 0(默认值)
boolean false
char []
byte 0
int 0
long 0
short 0
float 0.0
double 0.0
reference(引用) null
  • 解析 将常量池里面符号引用替换为直接引用
  • 初始化 对static域 和变量赋值。
public static int value = 123 ; //这个阶段才是value = 123
public int vaule2 = 123 ;//经过两个阶段,显示vlaue2 = 0(默认值) 然后 value2 = 123

最后一个阶段初始化是在以下五种情况发生时会发生。(注定同时前四个阶段也会发生)

  • new对象 (构造器实际上时static方法,因此相当于调用static方法了) 、引用static域或者static方法
  • 反射调用
  • 当初始化一个类的时候,发现其父类没有进行初始化,那么先触发其父类的初始化
  • main方法
  • 这种方式暂时还不懂 Class.forName()
package MyInitialization;
//解析 首先对应上面的main方法 会发生初始化,发生初始化说明了经历了加载 - 验证 - 准备 - 解析 - 初始化(5个阶段)。而准备阶段  是初始化static域为默认值 。 初始化阶段是静态域 (static代码块) > 变量 >  构造方法。因此先执行static Table table = new Table() ; 碰到new实例,又要初始化Table类。
public class StaticInitialization {
    public static void main(String[] args) {
        System.out.println("Creating new Cupboard() in main"); //c : 10 Creating new Cupboard() in main
        new Cupboard() ; //d 注意Cupboard类已经初始化过一次,所以 static域(静态变量),static代码块只初始化一次
        System.out.println("Creating new Cupboard() in main"); //f 13 Creating new Cupboard() in main
        new Cupboard() ; //g 重复d一样的过程 13 14 15
        table.f2(1); //h 16 f2(1)
        cupboard.f3(1); // i 17 f3(1)
    }
    static Table table = new Table() ; //a: 最先执行,初始化Table类
    static Cupboard cupboard = new Cupboard() ;//b: 接着执行 ,初始化Cupboard类
}
class Bowl{
    Bowl(int marker){
        System.out.println("Bowl(" + marker + ")");
    }
    void f1(int marker){
        System.out.println("f1(" + marker + ")");
    }
}

class Table{  //静态域 (static代码块) > 变量 >  构造方法
    static Bowl bowl1 = new Bowl(1) ; // 1 打印Bowl(1)
    Table(){
        System.out.println("Table()"); //3 Table()
        bowl2.f1(1); //4 f1(1)
    }
    void f2(int marker){
        System.out.println("f2(" + marker + ")");
    }
    static Bowl bowl2 = new Bowl(2) ; // 2 打印Bowl(2)
}
class Cupboard{ //静态域 (static代码块) > 变量 >  构造方法
     Bowl bowl3 = new Bowl(3) ; //7 Bowl(3) | 10 Bowl(3) (static域只初始化一次,所以只有bolw3才会再次赋值,因为在堆中又有一个对象啦)
    static Bowl bowl4 = new Bowl(4) ; // 5 Bowl(4)
    Cupboard(){
        System.out.println("Cupboard()"); //8 Cupboard() | 11 Cupboard()
        bowl4.f1(2); // 9 f1(2) | 12 f1(2)
    }
    void f3(int marker){
        System.out.println("f3(" + marker + ")");
    }
    static Bowl bowl5 = new Bowl(5) ; // 6  Bowl(5)
}

Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)

还有值得注意的一点。当调用下面的static final修饰的域时,第一个不需要初始化,因为是编译器常量。第二个需要,因为不是编译器常量。

static final int staicFinal = 47 ; //1 在准备阶段就把staticFinal = 47
staic final int staticFinal2 = new Random().nextInt(47) ; //2