類的加載分為三個階段,加載--->鏈接--->初始化
類加載的過程
將class表示的二進制文件加載到內存,放在方法區中,並在堆中創建一個java.lang.Class對象(封裝的是class的數據結構)
類的主動使用,會加載類
1 new Test()
2 對類中的靜態變量進行讀寫,對接口中的靜態變量進行讀取
3 反射某個類 , Class.forName()
4 調用靜態方法
5 初始化子類時,父類將被初始化
6 啟動類 ,采用java命令執行某個類
類的被動使用,不會加載類
1 子類引用父類的靜態變量 ,父類會被初始化,子類不會被初始化
public class Parent { public static int a = 123; static { System.out.println("Parent init"); } } public class Child extends Parent { static { System.out.println("Child init"); } } public class Test { public static void main(String[] args) { // 這里是訪問的父類的靜態變量 System.out.println(Child.a); } }
只初始化了父類
2 通過引用類型定義數組,不會觸發此類的初始化
3 final修飾的常量,不會觸發此類的初始化,因為在編譯期間就放入了常量池
4 final修飾的復雜類型,會觸發此類的初始化,因為在編譯期間不能計算得出其值
4 JVM最先初始化的總是java.lang.Object類
類初始化要注意的地方
/** * * 在static塊里,不能對在它后面的靜態變量進行讀取的操作,但可以寫入 * 在對它前面定義的靜態變量,可以讀取和寫入 */ public class MyObject { private static String x = "abc"; static { x= "edf"; b =12; //這里會提示錯誤,can not reference a field before define it //System.out.println(b); } private static int b = 10; }
/** * * 在初始化類Foo時候,會執行類的static塊。這時候開啟兩個線程進行new對象,JVM保證了只能有一個線程執行類的初始化 * */ public class ClinitTest { public static void main(String[] args) { new Thread(()-> {new Foo();}).start(); new Thread(()-> {new Foo();}).start(); } } class Foo{ private static AtomicBoolean init = new AtomicBoolean(true); static { System.out.println(Thread.currentThread().getName() + " will be inint"); while (init.get()) { } System.out.println(Thread.currentThread().getName() + " have done inint"); } }
對象實例化
類的初始化和對象實例化有時是同時進行的
class Price { static Price P = new Price(2.7); static double apple = 20; double price; public Price(double orange) { price = apple - orange; } } public class PriceTest { public static void main(String[] args) { //Price.P訪問了類的靜態變量,會觸發類的初始化,即(加載,連接,初始化),當執行構造函數時 //apple還沒有初始化完成,處於連接階段的准備階段,其值為默認值0,這時構造函數計算的price為-2.7 System.out.println(Price.P.price);// 結果為-2.7 } }
class Price { static Price P = new Price(2.7); final static double apple = 20; double price; public Price(double orange) { price = apple - orange; } } public class PriceTest { public static void main(String[] args) { //apple在編譯階段就完成賦值了,其值為20,這時構造函數計算的price為17.3 System.out.println(Price.P.price);// 結果為17.3 } }
class Price { static double apple = 20; static Price P = new Price(2.7); double price; public Price(double orange) { price = apple - orange; } } public class PriceTest { public static void main(String[] args) { //Price.P訪問了類的靜態變量,會觸發類的初始化,即(加載,連接,初始化),當執行構造函數時 //apple已經完成了初始化,其值為20了,這時構造函數計算的price為17.3 System.out.println(Price.P.price);// 結果為17.3 } }