上接深刻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虛擬機初始化一個類時,要求它的全部的父類都已經被初始化,但這條規則並不適用於接口。
在初始化一個類時,並不會先初始化它所實現的接口
在初始化一個接口時,並不會先初始化它的父接口
所以,一個父接口並不會由於它的子接口或者實現類的初始化而初始化。只有當程序首次使用特定接口的靜態變量時,纔會致使該接口的初始化。只有當程序訪問的靜態變量或靜態方法確實在當前類或當前接口中定義時,才能夠認爲是對類或接口的主動使用 。若是是調用的子類的父類屬性,那麼子類不會被初始化。