在以下幾種狀況下,java虛擬機將結束生命週期java
類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區,而後在
堆區建立一個java.lang.Class對象,用來封裝類在方法區內的數據結構程序員
類的加載的最終產品是位於堆區中的Class對象
Class對象封裝了類在方法區內的數據結構,而且向java程序員提供了訪問方法區內的數據結構的接口數據庫
有兩種類型的類加載器安全
public class ClassLoaderDemo { public static void main(String[] args) { ClassLoader loader = ClassLoaderDemo.class.getClassLoader(); while(loader!=null){ System.out.println(loader.getClass().getName()); loader = loader.getParent(); } System.out.println(String.class.getClassLoader()); } }
類被加載後,就進入鏈接階段,鏈接就是將已經讀入到內存的類的二進制數據合併到虛擬機的運行時環境中去
類的驗證內容網絡
在準備階段,java虛擬機爲類的靜態變量分配內存,並設置默認的初始值,例如對於Sample類,在準備階段,將int類型的靜態變量a分配4個字節的內存
空間,而且賦予默認值0,爲long類型的靜態變量b分配8個字節的內存空間,而且賦予默認值0數據結構
在解析階段,java虛擬機會把類的二進制數據中的符號引用替換爲直接引用,例如在Worker類的gotoWork()方法中會引用Car類的run()方法
public void gotoWorker(){ car.run(); }
在Worker類的二進制數據中,包含了一個對Car類的run()方法的符號引用,它由run()方法的全名和相關描述符組成,在解析階段,java虛擬機會把
這個符號引用替換爲一個指針,該指針指向Car類的run()方法在方法區內的內存位置,這個指針就是直接引用dom
在初始化階段,java虛擬機執行類的初始化語句,爲類的靜態變量賦予初始值,在程序中,靜態變量的初始化有兩種途徑:jvm
public class Sample{ private static int a = 1; // 在靜態變量的聲明處進行初始化 public static long b; public static long c; static{ b = 2; } // 在靜態代碼塊中進行初始化 }
class Singleton { //private static Singleton singleton = new Singleton(); // counter1=1 counter2=0 public static int counter1; public static int counter2 = 0; private static Singleton singleton = new Singleton(); // counter1=1 counter2=1 private Singleton(){counter1++; counter2++;} public static Singleton getInstance(){return singleton;} } public class MyTest{ public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println("counter1= " + singleton.counter1); System.out.println("counter2= " + singleton.counter2); } }
靜態變量的聲明語句,以及靜態代碼塊都被看作類的初始化語句,java虛擬機會按照初始化語句在類文件中的前後順序來依次執行它們
例如當如下Sample類被初始化後它的靜態變量a的取值爲4操作系統
public class Sample{ static int a = 1; static{ a = 2; } static{ a = 4; } public static void main(String[] args){ System.out.println("a= " + a)} // 打印a=4 }
class FinalTest{ public static final int x = 6/3; // 編譯時肯定 不會致使類初始化 // public static final int x = new Random().nextInt(100); // 運行時變量 對類進行初始化 static{ System.out.println("FinalTest static block"); } } public class Test2 { public static void main(String[] args) { System.out.println(FinalTest.x); } }
當java虛擬機初始化一個類時,要求它的全部父類都已經被初始化,可是這條規則並不適用於接口
在初始化一個類時,並不會先初始化它所實現的接口
在初始化一個接口時,並不會先初始化它的父接口
所以,一個父接口並不會由於它的子接口或者實現類的初始化而初始化,只有當程序首次使用特定接口的靜態變量時纔會致使接口初始化指針
public class Test3 { static{ System.out.println("Test3 static block"); } public static void main(String[] args) { System.out.println(Child.b); } } class Parent{ static int a = 3; static{ System.out.println("Parent static block"); } } class Child extends Parent{ static int b = 4; static{ System.out.println("Child static block"); } }
輸出:
Test3 static block
Parent static block
Child static block
4
public class Test4 { static{ System.out.println("Test4 static block"); } public static void main(String[] args) { Parent parent; System.out.println(Parent2.a); System.out.println(Child2.b); } } class Parent2{ static int a = 3; static{ System.out.println("Parent2 static block"); } } class Child2 extends Parent2{ static int b = 4; static{ System.out.println("Child2 static block"); } }
輸出:
Test4 static block
Parent2 static block
3
Child2 static block
4
對子類的主動使用會致使父類被初始化,但對父類的主動使用並不會致使子類初始化(不可能說生成一個Object類的對象致使全部子類初始化)
只有當程序訪問的靜態變量或靜態方法確實在當前類或當前接口中定義時,才能夠認爲是對類或接口的主動使用
public class Test5 { public static void main(String[] args) { System.out.println(Child3.a); Child3.doSomething(); } } class Parent3{ static int a = 3; static{ System.out.println("Parent3 static block"); } static void doSomething(){ System.out.println("do something"); } } class Child3 extends Parent3{ static{ System.out.println("Child3 static block"); } }
輸出:
Parent3 static block
3
do something
調用ClassLoader類的loadClass方法加載一個類,並非對類的主動使用,不會致使類的初始化
public class Test6 { public static void main(String[] args) throws Exception{ ClassLoader loader = ClassLoader.getSystemClassLoader(); loader.loadClass("com.ak.cls.C"); System.out.println("line"); Class.forName("com.ak.cls.C"); } } class C{ static{ System.out.println("Class C"); } }
輸出: line Class C