有关Java虚拟机类加载机制相关的文章一搜一大把,笔者这儿也不必再赘述一遍了。笔者这儿捞出一道code题要各位大佬来把玩把玩,假定你一眼就看出了端倪,那么祝贺你,你可以下山了:
public class StaticTest
{
public static void main(String[] args)
{
staticFunction();
}
static StaticTest st = new StaticTest();
static
{
System.out.println("1");
}
{
System.out.println("2");
}
StaticTest()
{
System.out.println("3");
System.out.println("a="+a+",b="+b);
}
public static void staticFunction(){
System.out.println("4");
}
int a=110;
static int b =112;
}
问题:请问这段程序的输出是什么?一般关于这类问题,小伙伴们脑海中必定浮现出这样的常识点:
Java中赋值次第:
父类的静态变量赋值
自身的静态变量赋值
父类成员变量赋值和父类块赋值
父类结构函数赋值
自身成员变量赋值和自身块赋值
自身结构函数赋值
按照这个理论输出是什么呢?答案输出:1 4,这样正确嚒?必定不正确啦,(midea808)这儿不是说上面的规矩不正确,而是说不能简略的套用这个规矩。 正确的答案是:
2
3
a=110,b=0
1
4
有没有答对呢?这儿首要的点之一:实例初始化不一定要在类初始化结束之后才开端初始化。 类的生命周期是:加载->验证->准备->解析->初始化->运用->卸载,只要在准备阶段和初始化阶段才会触及类变量的初始化和赋值,因此只针对这两个阶段进行剖析;
类的准备阶段需求做是为类变量分配内存并设置默认值,因此类变量st为null、b为0;(需求留心的是假定类变量是final,编译时javac将会为value生成ConstantValue特征,在准备阶段虚拟机就会根据ConstantValue的设置将变量设置为指定的值,假定这儿这么定义:static final int b=112,那么在准备阶段b的值就是112,而不再是0了。)
类的初始化阶段需求做的是施行类结构器(类结构器是编译器搜集悉数静态语句块和类变量的赋值语句按语句在源码中的次第吞并生成类结构器,政策的结构方法是(),类的结构方法是(),可以在仓库信息中看到),因此先施行第一条静态变量的赋值语句即st = new StaticTest (),此刻会进行政策的初始化,政策的初始化是先初始化成员变量再施行结构方法,因此打印2->设置a为110->施行结构方法(打印3,此刻a现已赋值为110,但是b仅仅设置了默认值0,并未结束赋值动作),等政策的初始化结束后持续施行之前的类结构器的语句,接下来就不具体说了,按照语句在源码中的次第施行即可。
这儿面还牵涉到一个冷常识,就是在嵌套初始化时有一个特别的逻辑。特别是内嵌的这个变量恰好是个静态成员,(grandwaycanada)并且是本类的实例。 这会导致一个诙谐的现象:“实例初始化居然出现在静态初始化之前”。 其实并没有提早,你要知道java记载初始化与否的时机。看一个简化的代码,把关键问题说明清楚:
public class Test {
public static void main(String[] args) {
func();
}
static Test st = new Test();
static void func(){}
}
根据上面的代码,有以下进程:
首先在施行此段代码时,首先由main方法的调用触发静态初始化。
在初始化Test 类的静态部分时,遇到st这个成员。
但恰巧这个变量引证的是本类的实例。
那么问题来了,此刻静态初始化进程还没结束就要初始化实例部分了。是这样么?
从人的角度是的。但从java的角度,一旦开端初始化静态部分,不论是否结束,后续都不会再从头触发静态初始化流程了。
因此在实例化st变量时,实际上是把实例初始化嵌入到了静态初始化流程中,并且在楼主的问题中,嵌入到了静态初始化的开端方位。这就导致了实例初始化完全至于静态初始化之前。这也是导致a有值b没值的原因。
毕竟再考虑到文本次第,效果就清楚明了了。
具体看到这儿,心中大概有个结论了吧。
来源:https://www.cnblogs.com/monkey7788/p/11992281.html