看到一道面试题:static代码块什么时候被执行?
看到网上有一些错误的答案,说是在类被加载的时候,这个回答是错误的。正确的答案是在类被初始化的时候才被执行。
想要理解static被执行的时机,需要理解一个类的生命周期。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析合称为连接。
加载
通过全限定名来获取此类的二进制字节流,然后将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
注意在加载的时候,需要用到双亲委派机制,不明白的点击这里。
验证
验证阶段是非常重要的,这个阶段是否严谨,直接决定了Java虚拟机是否能承受恶意代码的攻击。
主要验证有:文件格式验证、元数据验证、字节码验证、符号引用验证。
准备
为类变量分配内存并设置类变量初始值的阶段。这里所说的初始值,指的是数据类型的零值。如int的零值为0,boolean为false。
public static final int value=123;//在准备阶段的值为0,不是123。
public static final int value=123;//在准备阶段为123。
解析
把类或接口、字段名、方法名转为一个具体的内存地址。这个内存地址就是直接引用,而“名”就是符号引用。
初始化
在前面的过程中,除了加载阶段,可以通过自定义的加载器参与,其余动作是由虚拟机主导和控制进行的。
在初始化过程,主要是去执行中的代码。而是由static变量的赋值和静态语句块合并产生的。
如果一个类被执行的时候,虚拟机会保证其父类的先被执行,所以object是第一个被执行的的类。
但是并不是每一个类都会有初始化的过程,仅有6种情况会主动使用:
1.当创建某个类的新实例时(如通过new或者反射,克隆,反序列化等)
2.当调用某个类的静态方法时
3.当使用某个类或接口的静态字段时
4.当调用Java API中的某些反射方法时,比如类Class中的方法,或者java.lang.reflect中的类的方法时
5.当初始化某个子类时
6.当虚拟机启动某个被标明为启动类的类(即包含main方法的那个类)
最后贴一个实例来帮助理解
/**
* Created by JinTX on 2017/11/1.
*/
public class StaticTest {
public static class MyObject{
static {
System.out.println("object init");
}
}
public static class Father extends MyObject{//父类
public static int value = 1;
static{
System.out.println("father do static");
}
public Father(){
System.out.println("father do init");
}
}
public static class Son extends Father{//子类
static {
System.out.println("son do static");
}
public Son(){
System.out.println("son do init");
}
}
public static void main(String[] args){
System.out.println(Son.value);
Son son = new Son();
}
}
下面是正确答案:
object init
father do static
1
son do static
father do init
son do init