標籤:blog http ar 使用 sp for java strong on
上接深入java虛擬機器——深入java虛擬機器(二)——類載入器詳解(上),在上一篇文章中,我們講解了類的生命週期的載入和串連,這一篇我們接著上面往下看。
類的初始化:在類的生命週期執行完載入和串連之後就開始了類的初始化。在類的初始化階段,java虛擬機器執行類的初始化語句,為類的靜態變數賦值,在程式中,類的初始化有兩種途徑:(1)在變數的聲明處賦值。(2)在靜態代碼塊處賦值,比如下面的代碼,a就是第一種初始化,b就是第二種初始化
[html] view plaincopyprint?
- public class Test
- {
- public static int a = 0;
- public static int b ;
- static{
- b=2;
- }
- }
靜態變數的聲明和靜態代碼塊的初始化都可以看做靜態變數的初始化,類的靜態變數的初始化是有順序的。順序為類檔案從上到下進行初始化,想到這,想起來一個很無恥的面試題,分享給大家看一下:
[java] view plaincopyprint?
- package com.bzu.csh;
- class Singleton
- {
- private static Singleton singleton = new Singleton();
- public static int counter1;
- public static int counter2 = 0;
- private Singleton()
- {
- counter1++;
- counter2++;
- }
- public static Singleton getInstance()
- {
- return singleton;
- }
- }
- public class Test
- {
- public static void main(String[] args)
- {
- Singleton singleton = Singleton.getInstance();
- System.out.println("counter1 = " + singleton.counter1);
- System.out.println("counter2 = " + singleton.counter2);
- }
- }
大家先看看這裡的程式會輸出什嗎?
不知道大家的答案是什麼,如果不介意的話可以把你的答案寫到評論上,看看有多少人的答案和你一樣的。我先說說我剛開始的答案吧。我認為會輸出:
counter1 = 1
Counter2 = 1
不知道大家的答案是不是這個,反正我的是。下面我們來看一下正確答案:
不知道你做對沒有,反正我剛開始做錯了。好,現在我來解釋一下為什麼會是這個答案。在給出解釋之前,我們先來看一個概念:
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虛擬機器初始化一個類時,要求它的所有的父類都已經被初始化,但這條規則並不適用於介面。
在初始化一個類時,並不會先初始化它所實現的介面
在初始化一個介面時,並不會先初始化它的父介面
因此,一個父介面並不會因為它的子介面或者實作類別的初始化而初始化。只有當程式首次使用特定介面的靜態變數時,才會導致該介面的初始化。只有當程式訪問的靜態變數或靜態方法確實在當前類或當前介面中定義時,才可以認為是對類或介面的主動使用 。如果是調用的子類的父類屬性,那麼子類不會被初始化。
類的生命週期(下)類的初始化【轉】