楔子
類字面常量
Java還提供了另一種方法來生成對象的引用:類字面常量,對上述程序來說,就像這樣FancyToy.class
。這樣做不僅更簡單,而且更安全,因爲它在編譯時就會受到檢查。並且它根除了對forName
方法的調用,所以效率更高。
類字面量不可可以用於普通類,也可以應用於接口、數組以及基本數據類型。另外,對應基本數據類型包裝類,還有一個標準字段TYPE
.
當使用.class
來創建對Class
對象的引用時,不會自動地初始化該Class對象
。爲了使用類而做的準備工作實際包含了三個步驟
- 加載:這是類加載器執行的。該步驟檢查字節碼(通常在classpath所指定的路徑中查找,但這並非是必須的),並從這些字節碼 中創建一個Class對象。
- 鏈接:在鏈接階段將驗證類中的字節碼,爲
static
字段分配存儲空間,並且如果需要的話,將解析這個類創建的對其他類的所有引用。 - 初始化:如果該類具有超類,則先初始化超類,執行static初始化器和static初始化塊。
直到第一次引用一個static方法(構造器隱式地是static)或者非常量的static字段,纔會進行類初始化。
package com.study.java8.char18;
/**
* @author study
* @version 1.0
* @description
* @date 2020/6/7 20:31
*/
// typeinfo/ClassInitialization.java
import java.util.*;
class Initable {
static final int STATIC_FINAL = 47;
static final int STATIC_FINAL2 =
ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3 {
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
/********************************/
public static void main(String[] args) throws Exception {
Class initable = Initable.class;
System.out.println("After creating Initable ref");
// Does not trigger initialization:
System.out.println(Initable.STATIC_FINAL);
// Does trigger initialization:
System.out.println(Initable.STATIC_FINAL2);
// Does trigger initialization:
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("com.study.java8.char18.Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
初始化有效地儘可能的“惰性”,從對initable 引用的創建中可以看到,僅使用.class
語法來獲得對類對象的引用不會引發初始化。但與此相反,使用Class.forName()
來產生 Class
引用會立即就進行初始化,如 initable3
。
如果一個 static final
值是編譯期常量 (如 Initable.staticFinal) 那麼這個值不需要對 Initable 類進行初始化就可以被讀取。但是 ,如果只是將一個字段設置爲 static
和 final
,還不足以確保這種行爲,例如,對Initable.staticFinal2
的訪問將強制進行類的初始化,因爲它不是一個編譯期常量。
如果一個static 字段不是 final的,那麼在對它訪問時,總是要求它在被讀取之前,要麼進行鏈接(爲這個字段分配存儲空間) 和初始化(初始化存儲該空間),就像對 Initable2.staticNonFinal
的訪問中所看到的那樣。