Java Static關鍵字詳解


  提起static關鍵字,相信大家絕對不會陌生,但是,想要完全說明白,猛的一想,發現自己好像又說不太明白... ...比方說,昨天被一個同學問起的時候... ... 當然,不是所有人都像我一樣學藝不精的,但是像這樣的基礎不牢的人應該不少,因為常用,所以用大家都會,但是談到精細之處都夠嗆。這篇博客是我翻出我原來的學習筆記再加上自己看的一些博客整理出來的,供基礎知識不怎么牢靠的同學參考參考。

1. static 關鍵字要解決的問題

  這里摘錄一下《Java編程思想(第四版)》里關於static關鍵字的一段原話:(P29)通常來說,當創建類時,就是在描述那個類的對象的外觀與行為。除非用new創建那個對象,否則,實際上並未獲得任何對象。執行new來創建對象的時候,數據存儲空間才被分配,其方法才供外界調用。有兩種情形用上述方法是無法解決的。一種情形是,只想為某特定域分配單一存儲空間,而不去考慮究竟要創建多少個對象,甚至根本不需要創建任何對象。另一種情形是,希望某個方法不與包含他的類的任何對象關聯在一起。也就是說,即使沒有創建對象,也能夠調用方法。簡單來說,static的主要目的就是創建獨立於具體對象的域變量與方法。

2. static修飾的變量或方法或類的加載時機

  在加載類的同時加在static修飾的部分。(注意:這個時候,還不存在具體對象,並且這個過程只進行一次

3. 通過代碼示例來分別看看靜態變量、靜態方法、靜態類的效果

3.1 靜態變量

public class StaticTest{

    public static int count =0;
    
    @SuppressWarnings("static-access")
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        StaticTest test1 = new StaticTest();
        System.out.println(test1.count);
        StaticTest test2 = new StaticTest();
        test2.count++;
        System.out.println(test1.count+" "+test2.count+" "+StaticTest.count);
    }

}

輸出結果:

0
1    1    1

可見,static變量並不是所在類的某個具體對象所有,而是該類的所有對象所共有的,靜態變量既能被對象調用,也能直接拿類來調用。

除此之外,靜態變量不能引用非靜態方法,原因正如前面描述靜態加載時機中說的那樣,加載靜態的時候,非靜態的變量、方法等還不存在,當然就無法引用了。但是,非靜態方法或類卻能正常引用靜態變量或方法。因為非靜態總是在靜態之后出現的。

3.2 靜態方法

  靜態方法和靜態變量一樣,屬於類所有,在類加載的同時執行,不屬於某個具體的對象,所有對象均能調用。對於靜態方法需要注意以下幾點:

  • 它們僅能調用其他的static 方法。
  • 它們只能訪問static數據。
  • 它們不能以任何方式引用this 或super。
class Simple {
    static void go() {
       System.out.println("Welcome");
    }
}
 
public class Cal {
    public static void main(String[] args) {
       Simple.go();
    }
}

 

  靜態方法一般用於工具類中,可以直接拿類名調用工具方法進行使用。

3.3 靜態類

  一般來說,一個普通類是不允許被聲明為static的,但是,在內部類中可以將其聲明為static的,這個時候,外部類可以直接調用內部類,因為static的內部類是在加載外部類的同時加載的,所以也就是說,並不要實例化外部類就能直接調用靜態內部類。看例子:

public class BaseStatic {
  static {
        System.out.println("Load base static");
    }

    public BaseStatic(){
        System.out.println("BaseStatic constructor");
    }
    
    static class BaseInnerClass{
        static{
            System.out.println("Base inner class static");
        }
        
        public BaseInnerClass(){
            System.out.println("BaseInnerClass constructor");
        }
    }

}


public class StaticLoadOrderTest{

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new BaseStatic.BaseInnerClass();
    }

}

在看答案之前,自己想想這個輸出結果是什么?

先不急着看答案,我們先來看看這個執行過程:首先,在進入StaticLoadOrderTest的main方法之前,加載StaticLoadOrderTest類,然后執行new BaseStatic.BaseInnerClass();這里需要注意:因為BaseInnerClass是靜態的,所以這里並不需要加載外部類和實例化外部類,可以直接加載BaseInnerClass並實例化。所以輸出:

 Base inner class static
BaseInnerClass constructor

這里留個坑:當直接使用外部類類名.靜態內部類進行實例化的時候,如果外部類沒有加載的話(實際上也是沒有加載),那么這個statement: BaseStatic.BaseInnerClass中的BaseStatic是個什么存在????難道只是與靜態內部類發生了簡單的名稱關聯嗎?若是這樣還設計靜態內部類干嘛呢?我覺得java設計者們不至於犯這種錯誤吧?也可能因為自己對於JVM並不熟悉,對於底層不太了解,若是路過的大神能幫忙解決一下,感激不盡!!!!

3.4 關於靜態加載順序的示例

下面這段代碼的輸出是什么?

public class BaseStatic {
    static {
        System.out.println("Load base static");
    }
    
    public BaseStatic(){
        System.out.println("BaseStatic constructor");
    }
    
    static class BaseInnerClass{
        static{
            System.out.println("Base inner class static");
        }
        
        public BaseInnerClass(){
            System.out.println("BaseInnerClass constructor");
        }
    }

}

public class StaticLoadOrderTest {
    
    static {
        System.out.println("Load test");
    }
    
    public StaticLoadOrderTest(){
        System.out.println("Test constructor");
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new BaseStatic();
        new StaticLoadOrderTest();
        new BaseStatic.BaseInnerClass();
    }

}

  和上面一樣,分析一下過程:在進入main方法之前,需要加載StaticLoadOrderTest類,這時候發現有static代碼塊,先加載靜態代碼塊,然后進入main方法內部,new BaseStatic(),這時候需要加載BaseStatic類,同時也要先加載靜態代碼塊,然后調用構造器。注意:這里並沒有加載BaseInnerClass,因為它是內部類只有在真正用到的時候才會進行加載,相信聰明的讀者看到這個是不是想到了又一種單例設計模式的實現方式?自己研究吧。回到main方法中,接下來該執行new StaticLoadOrderTest()了,因為StaticLoadOrderTest類之前已經被加載過一次了,並且類只加載一次,所以這里就直接構造了;然后是最后一句new BaseStatic.BaseInnerClass()了,和上面例子一樣,這里就不再細講。所以輸出結果為:

Load test
Load base static
BaseStatic constructor
Test constructor
Base inner class static
BaseInnerClass constructor

  再考慮一下,如果我把上面的例子改成下面這樣,輸出結果又會是什么呢?

public class BaseStatic {
    static {
        System.out.println("Load base static");
    }
    
    public BaseStatic(){
        System.out.println("BaseStatic constructor");
    }
    
    static class BaseInnerClass{
        static{
            System.out.println("Base inner class static");
        }
        
        public BaseInnerClass(){
            System.out.println("BaseInnerClass constructor");
        }
    }

}



public class StaticLoadOrderTest extends BaseStatic{
    
    static {
        System.out.println("Load test");
    }
    
    public StaticLoadOrderTest(){
        System.out.println("Test constructor");
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new BaseStatic.BaseInnerClass();new StaticLoadOrderTest(); 
new BaseStatic();
} }

 

以上就是關於java中static關鍵字的一些知識了。

最后,預祝國足世預賽四強賽首站告捷!🇨🇳🇨🇳

  

碼字不易,請尊重原創,轉載請注明出處。

參考資料:

http://yongliang567.iteye.com/blog/904467

http://blog.csdn.net/anmei2010/article/details/4096131

http://blog.csdn.net/brouth/article/details/51656603

《Java編程思想(第四版)》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM