上接深刻java虚拟机——深刻java虚拟机(二)——类加载器详解(上),在上一篇文章中,咱们讲解了类的生命周期的加载和链接,这一篇咱们接着上面往下看。html
类的初始化:在类的生命周期执行完加载和链接以后就开始了类的初始化。在类的初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋值,在程序中,类的初始化有两种途径:(1)在变量的声明处赋值。(2)在静态代码块处赋值,好比下面的代码,a就是第一种初始化,b就是第二种初始化java
静态变量的声明和静态代码块的初始化均可以看作静态变量的初始化,类的静态变量的初始化是有顺序的。顺序为类文件从上到下进行初始化,想到这,想起来一个很无耻的面试题,分享给你们看一下:面试
你们先看看这里的程序会输出什么?spa
不知道你们的答案是什么,若是不介意的话能够把你的答案写到评论上,看看有多少人的答案和你同样的。我先说说我刚开始的答案吧。我认为会输出:.net
counter1 = 1xml
Counter2 = 1htm
不知道你们的答案是否是这个,反正个人是。下面咱们来看一下正确答案:对象
不知道你作对没有,反正我刚开始作错了。好,如今我来解释一下为何会是这个答案。在给出解释以前,咱们先来看一个概念:blog
Java程序对类的使用方式可分为两种 接口
主动使用
被动使用
•全部的Java虚拟机实现必须在每一个类或接口被Java程序“首次主动使用”时才初始化他们
主动使用(六种)
–建立类的实例
–访问某个类或接口的静态变量,或者对该静态变量赋值
–调用类的静态方法
–反射(如Class.forName(“com.bzu.csh.Test”))
–初始化一个类的子类
–Java虚拟机启动时被标明为启动类的类(Java Test)
OK,咱们开始解释一下上面的答案,程序开始运行,首先执行main方法,执行main方法第一条语句,调用Singleton类的静态方法,这里调用Singleton类的静态方法就是主动使用Singleton类。因此开始加载Singleton类。在加载Singleton类的过程当中,首先对静态变量赋值为默认值,
Singleton=null
counter1 = 0
Counter2 = 0
给他们赋值完默认值值以后,要进行的就是对静态变量初始化,对声明时已经赋值的变量进行初始化。咱们上面提到过,初始化是从类文件从上到下赋值的。因此首先给Singleton赋值,给它赋值,就要执行它的构造方法,而后执行counter1++;counter2++;因此这里的counter1 = 1;counter2 = 1;执行完这个初始化以后,而后执行counter2的初始化,咱们声明的时候给他初始化为0 了,因此counter2 的值又变为了0.初始化完以后执行输出。因此这是的
counter1 = 1
counter2 = 0
类初始化步骤
(1)假如一个类尚未被加载或者链接,那就先加载和链接这个类
(2)假如类存在直接的父类,而且这个父类尚未被初始化,那就先初始化直接的父类
(3)假如类中存在初始化语句,那就直接按顺序执行这些初始化语句
在上边咱们咱们说了java虚拟机实现必须在每一个类或接口被Java程序“首次主动使用”时才初始化他们,上面也举出了六种主动使用的说明。除了上述六种情形,其余使用Java类的方式都被看做是被动使用,不会致使类的初始化。程序中对子类的“主动使用”会致使父类被初始化;但对父类的“主动”使用并不会致使子类初始化(不可能说生成一个Object类的对象就致使系统中全部的子类都会被初始化)
注:调用ClassLoader类的loadClass方法加载一个类,并非对类的主动使用,不会致使类的初始化。
当java虚拟机初始化一个类时,要求它的全部的父类都已经被初始化,但这条规则并不适用于接口。
在初始化一个类时,并不会先初始化它所实现的接口
在初始化一个接口时,并不会先初始化它的父接口
所以,一个父接口并不会由于它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会致使该接口的初始化。只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才能够认为是对类或接口的主动使用 。若是是调用的子类的父类属性,那么子类不会被初始化。