1. 程式人生 > >java中類載入與靜態變數、靜態方法與靜態程式碼塊詳解與初始化順序

java中類載入與靜態變數、靜態方法與靜態程式碼塊詳解與初始化順序

我們知道java中類的生命週期為裝載連線初始化使用解除安裝五個過程,如下圖所示:


1.載入 

    我們編寫一個java類的程式碼,經過編譯之後生成一個字尾名為.class的檔案,java虛擬機器就能識別這種檔案。java的生命週期就是class檔案從載入到消亡的過程。 關於載入,其實,就是將原始檔的class檔案找到類的資訊將其載入到方法區中,然後在堆區中例項化一個java.lang.Class物件,作為方法區中這個類的資訊的入口。但是這一功能是在JVM之外實現的,主要的原因是方便讓應用程式自己決定如何獲取這個類,在不同的虛擬機器實現的方式不一定相同,hotspot虛擬機器是採用需要時在載入的方式,

也有其他是先預先載入的。 (可以參考深入理解JVM這本書)

2.連線 
    連線一般是載入階段和初始化階段交叉進行,過程由以下三部分組成:
    (1)驗證:確定該類是否符合java語言的規範,有沒有屬性和行為的重複,繼承是否合理,總之,就是保證jvm能夠執行 
    (2)準備:主要做的就是為由static修飾的成員變數分配記憶體,並設定預設的初始值 
        預設初始值如下:

                    1.八種基本資料型別預設的初始值是0 
                    2.引用型別預設的初始值是null 

                    3.有static final修飾的會直接賦值,例如:static final int x=10;則預設就是10.

(3)解析:這一階段的任務就是把常量池中的符號引用轉換為直接引用,說白了就是jvm會將所有的類或介面名、欄位名、方法名轉換為具體的記憶體地址。

3.初始化 
    初始化這個階段就是將靜態變數(類變數)賦值的過程,即只有static修飾的才能被初始化,執行的順序就是:父類靜態域或著靜態程式碼塊,然後是子類靜態域或者子類靜態程式碼塊(靜態程式碼塊先被載入,然後再是靜態屬性)

4.使用 

    在類的使用過程中依然存在以下三步:

    (1)物件例項化:就是執行類中建構函式的內容,如果該類存在父類JVM會通過顯示或者隱示的方式先執行父類的建構函式,在堆記憶體中為父類的例項變數開闢空間,並賦予預設的初始值,然後在根據建構函式的程式碼內容將真正的值賦予例項變數本身,然後,引用變數獲取物件的首地址,通過操作物件來呼叫例項變數和方法 

    (2)垃圾收集:當物件不再被引用的時候,就會被虛擬機器標上特別的垃圾記號,在堆中等待GC回收 
    (3)物件的終結:物件被GC回收後,物件就不再存在,物件的生命也就走到了盡頭
5.類解除安裝 
    類解除安裝即類的生命週期走到了最後一步,程式中不再有該類的引用,該類也就會被JVM執行垃圾回收,從此生命結束…

程式碼示例:

package com.etc.test;class A{static int a;//類變數String name;int id;//靜態程式碼塊static{a=10;System.out.println("這是父類的靜態程式碼塊"+a);}//構造程式碼塊{id=11;System.out.println("這是父類的構造程式碼塊id:"+id);}A(){System.out.println("這是父類的無參建構函式");}A(String name){System.out.println("這是父類的name"+name);}}class B extends A{String name;static int b;static{b=12;System.out.println("這是子類的靜態程式碼塊"+b);}B(String name) {super();this.name = name;System.out.println("這是子類的name:"+name);}}public class Test666 {public static void main(String[] args) {B bb=new B("GG");}}

靜態程式碼在類的初始化階段被初始化。

非靜態程式碼則在類的使用階段(也就是例項化一個類的時候)才會被初始化。

  • 靜態變數
  • 可以將靜態變數理解為類變數(與物件無關),而例項變數則屬於一個特定的物件。

靜態變數有兩種情況:

  • 靜態變數基本資料型別,這種情況下在類的外部不必建立該類的例項就可以直接使用
  • 靜態變數是一個引用。這種情況比較特殊,主要問題是由於靜態變數一個物件的引用,那麼必須初始化這個物件之後才能將引用指向它。
  • 因此如果要把一個引用定義成static的必須在定義的時候就對其物件進行初始化
  1. public class TestForStaticObject{  
  2. static testObject o = new testObject (); //定義一個靜態變數並例項化  
  3. public static void main(String args[]){  
  4. //在main中直接以“類名.靜態變數名.方法名”的形式使用testObject的方法  
  5. }  
  6. }  
  • 靜態方法

 類變數不同方法(靜態方法與例項方法)在記憶體中只有一份無論該類有多少個例項,都共用一個方法

    靜態方法與例項方法的不同主要有:

  • 靜態方法可以直接使用,而例項方法必須類例項化之後通過物件來呼叫
  • 外部呼叫靜態方法時,可以使用“類名.方法名”或者“物件名.方法名”的形式。
  • 例項方法只能使用這種方式物件名.方法名
  • 靜態方法只允許訪問靜態成員。而例項方法中可以訪問靜態成員例項成員
  • 靜態方法中不能使用this(因為this是與例項相關的)。
  • 靜態程式碼塊

 在java類中,可以將某一塊程式碼宣告為靜態的。

  1. static {  
  2. //靜態程式碼塊中的語句  
  3. }  

靜態程式碼塊主要用於類的初始化它只執行一次並且在同屬於一個類的main函式之前執行

靜態程式碼塊的特點主要有:

  • 靜態程式碼塊會在類被載入時自動執行
  • 靜態程式碼塊只能定義在類裡面不能定義在方法裡面
  • 靜態程式碼塊裡變數都是區域性變數只在塊內有效。
  • 一個類中可以定義多個靜態程式碼塊,按順序執行
  • 靜態程式碼塊只能訪問類的靜態成員,而不允許訪問例項成員

 靜態程式碼塊和靜態函式的區別

java 靜態程式碼塊:

一般情況下,如果有些程式碼必須在專案啟動前就執行的時候,需要使用靜態程式碼塊,這種程式碼是主動執行它只執行一次並且在同屬於一個類的main函式之前執行

靜態函式:

需要在專案啟動的時候就初始化,在不建立物件的情況下,其他程式來呼叫的時候,需要使用靜態方法,這種程式碼是被動執行的.

注意:

(1)靜態變數是屬於整個類的變數而不是屬於某個物件的。注意不能把任何方法體內的變數宣告為靜態,例如:
fun()
{<