Java基礎——static 靜靜地隨著類的載入而載入

weixin_34353714發表於2017-06-18

static表示“全域性”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static程式碼塊,但是Java語言中沒有全域性變數的概念。

簡單分的可以有這麼幾個類別

  • static 變數
  • static 方法
  • static 程式碼塊

一、特點

  • 1、隨著類的載入而載入,優先於物件存在,不依賴於類的例項,被所有物件所共享,也就是說——static的成員和方法可以被直接被類名呼叫

    被static修飾的成員變數和成員方法獨立於該類的任何物件。也就是說,它不依賴類特定的例項,被類的所有例項共享。只要這個類被載入,Java虛擬機器就能根據類名在執行時資料區的方法區內定找到他們。因此,static物件可以在它的任何物件建立之前訪問,無需引用任何物件。

    只要一個類的方法或者成員被static修飾,那麼就可以直接通過 類名.方法名 或者 類名.變數名 訪問。當然也可以被該類的例項呼叫

  • 2、static的方法僅能呼叫其他的static 方法,只能訪問static資料,不能以任何方式引用this 或super。

    static有足夠的自由,但是隻能陪自由的人玩,隨著類的載入而載入,沒有this,meiyou super。

    阿敏說:別的成員都是伴隨著物件存在的,static如果調不是static的東西不可能,不是一個等級,static在別人不在,怎麼一起工作?

  • 3、通常一個普通類不允許宣告為靜態的(非法),只有內部類才可以。這時這個宣告為靜態的內部類可以直接作為一個普通類來使用,而不需例項一個外部類。

    靜態類只允許是內部類,使用內部靜態類不需要例項化。
    靜態內部類和非靜態內部類之間到底有什麼不同呢?下面是兩者間主要的不同。

  • (1)內部靜態類不需要有指向外部類的引用。但非靜態內部類需要持有對外部類的引用。()

  • (2)非靜態內部類能夠訪問外部類的靜態和非靜態成員。靜態類不能訪問外部類的非靜態成員。他只能訪問外部類的靜態成員。

  • (3)一個非靜態內部類不能脫離外部類實體被建立,一個非靜態內部類可以訪問外部類的資料和方法,因為他就在外部類裡面。

二、static與訪問修飾符

1、static與訪問修飾符

與public
用public修飾的static成員變數和成員方法本質是全域性變數和全域性方法

與private
static 變數前可以有private修飾,表示這個變數可以在類的靜態程式碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用),但是不能在其他類中通過類名來直接引用,這一點很重要。實際上你需要搞明白,private是訪問許可權限定,static表示不要例項化就可以使用,這樣就容易理解多了。static前面加上其它訪問許可權關鍵字的效果也以此類推。

三、static變數

按照是否靜態的對類成員變數進行分類可分兩種:

  • 一種是被static修飾的變數,叫靜態變數或類變數;
  • 另一種是沒有被static修飾的變數,叫例項變數。

兩者的區別是:

靜態變數/類變數

對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次記憶體,在載入類的過程中完成靜態變數的記憶體分配,可用類名直接訪問(方便),當然也可以通過物件來訪問(但是這是不推薦的)。

例項變數

對於例項變數,每建立一個例項,就會為例項變數分配一次記憶體,例項變數可以在記憶體中有多個拷貝,互不影響。

四、static方法

  • 靜態方法可以直接通過類名呼叫,
  • 任何的例項也都可以呼叫,
  • 靜態方法不能被繼承
  • 靜態方法中不能用this和super關鍵字
    (我不能例項化都可以存在,我this我幹嘛,我不能被繼承,你super我幹嘛)
  • 不能直接訪問所屬類的例項變數和例項方法,只能訪問所屬類的靜態成員變數和成員方法。
    (因為例項成員與特定的物件關聯!)
  • 因為static方法獨立於任何例項,因此static方法不能是abstract,因為static方法要有方法體。

static方法可以是private,這個不衝突,訪問修飾符永遠只做訪問修飾符做的事情,但是標記為private的static方法在其他類中(包括子類)就不能通過 類名.方法名 呼叫了。

4、靜態程式碼塊

用static修飾的程式碼塊表示靜態程式碼塊,當Java虛擬機器(JVM)載入類時,就會執行該程式碼塊

五、證明

1、證明 類的所有例項共享同一個static變數


public class SpeClassTest {
     static int num = 0;
}



.

.

一個物件改變static屬性的值,全部物件改變


import java.util.Arrays;

public class TestClass {
    public static void main(String[] args) {    
        SpeClassTest spe1 = new  SpeClassTest();
        SpeClassTest spe2 = new  SpeClassTest();
        System.out.println("spe1 的成員值:"+spe1.num+"    spe2 的成員值:"+spe2.num+"\n");
        // 因為SpeClassTest裡面的num屬性是static的,所有物件共享,一個變則全部變
        spe1.num = 3;
        System.out.println("spe1 的成員值:"+spe1.num+"    spe2 的成員值:"+spe2.num+"\n");
    }
}

.

.

輸出

spe1 的成員值:0    spe2 的成員值:0
spe1 的成員值:3    spe2 的成員值:3

由此可以證明它們共享一塊儲存區。static變數有點類似於C中的全域性變數的概念。

2、靜態程式碼塊在類載入時載入,而且只執行一次,static定義的變數會優先於任何其它非static變數,不論其出現的順序如何

普通程式碼塊是在類初始化的時候載入
.
.


public class MyValue {
    static int val = 0;
    MyValue(){
        val = 10;
    }
    MyValue(int i){
        val = i;
    }
   void add(){
        val = val+1;
    }
}

.

.


import java.util.Arrays;
public class TestClass {
    MyValue myValue = new MyValue(10);
    static MyValue v1,v2;
    static{
        prt("v1.val  "+v1.val+"   v2.val:"+v2.val);
        v1 = new  MyValue(30);
        prt("v1.val  "+v1.val+"   v2.val:"+v2.val);
        v1 = new  MyValue(40);
        prt("v1.val  "+v1.val+"   v2.val:"+v2.val);
    }

    public static void main(String[] args) {    
        TestClass test = new TestClass();
        prt("test.myValue.val: "+test.myValue.val);
        prt("v1.val  "+v1.val+"   v2.val:"+v2.val);
        v1.add();
        prt("test.myValue.val: "+test.myValue.val);
        prt("v1.val  "+v1.val+"   v2.val:"+v2.val);
    }
    public static void prt(String s) {
           System.out.println(s);
    }
}

.

輸出

v1.val  0   v2.val:0
v1.val  30   v2.val:30
v1.val  40   v2.val:40
test.myValue.val: 10
v1.val  10   v2.val:10
test.myValue.val: 11
v1.val  11   v2.val:11

分析:

開始的三句輸出,

v1.val 0 v2.val:0
v1.val 30 v2.val:30
v1.val 40 v2.val:40

是因為TestClass這個靜態程式碼塊隨著類的載入而載入導致的(如果我們把main方法的裡面的邏輯去掉,那麼只有前面的三句了)

重點是:static定義的變數會優先於任何其它非static變數,不論其出現的順序如何。正如在程式中所表現的,雖然myValue出現在v1和v2的前面,但是結果卻是v1和v2的初始化在myValue的前面

.

.

.

而第4句

test.myValue.val: 10

是因為main方法裡面TestClass test = new TestClass();這句執行,但是此時static程式碼塊已經記載過就不會再載入了,所以列印的是10.

(如果沒TestClass test = new TestClass();這一句,那麼肯定val的值還是40)

後面幾乎就沒什麼了,沒什麼特別的了。

3、static類只能是內部類,其他靜態類非法


public class StaticCls {
    public static void main(String[] args) {
       OuterCls.InnerCls oi = new OuterCls.InnerCls();
    }
}

class OuterCls {
    public static class InnerCls {
       InnerCls() {
           System.out.println("InnerCls");
       }
    }
} 
    結果為:InnerCls

.
.
.

下面程式碼演示 java中建立靜態內部類和非靜態內部類


/* 下面程式演示如何在java中建立靜態內部類和非靜態內部類 */

class OuterClass{
   private static String msg = "GeeksForGeeks";
   // 靜態內部類
   public static class NestedStaticClass{
       // 靜態內部類只能訪問外部類的靜態成員
       public void printMessage() {
         // 試著將msg改成非靜態的,這將導致編譯錯誤 
         System.out.println("Message from nested static class: " + msg); 
       }
    }
    // 非靜態內部類
    public class InnerClass{
       // 不管是靜態方法還是非靜態方法都可以在非靜態內部類中訪問
       public void display(){
          System.out.println("Message from non-static nested class: "+ msg);
       }
    }
} 

class Main
{
    // 怎麼建立靜態內部類和非靜態內部類的例項
    public static void main(String args[]){
       // 建立靜態內部類的例項
       OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass();
       // 建立靜態內部類的非靜態方法
       printer.printMessage();   
       // 為了建立非靜態內部類,我們需要外部類的例項
       OuterClass outer = new OuterClass();        
       OuterClass.InnerClass inner  = outer.new InnerClass();
       // 呼叫非靜態內部類的非靜態方法
       inner.display();
       // 我們也可以結合以上步驟,一步建立的內部類例項
       OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
       // 同樣我們現在可以呼叫內部類方法
       innerObject.display();
    }
}

六、static和final一塊用表示什麼

static final用來修飾成員變數和成員方法,可簡單理解為“全域性常量”!
對於變數,表示一旦給值就不可修改,並且通過類名可以訪問。
對於方法,表示不可覆蓋,並且可以通過類名直接訪問。

相關文章