1. 程式人生 > >a中static關鍵字詳解。

a中static關鍵字詳解。

 

由於學習需要找了半天找到了這篇文章,發現作者寫的很不錯,就轉載給大家:

 

 

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

 

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

    public修飾的static成員變數和成員方法本質是全域性變數和全域性方法,當宣告它類的物件時,不生成static變數的副本,而是類的所有例項共享同一個static變數。

    private修飾的staitc成員變數,表示這個變數可以在類的靜態程式碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用),但是不能在其他類中通過類名來直接引用,這一點很重要。而且private修飾的方法,也不能在其他類中通過類名來直接引用。

    實際上你需要搞明白,private是訪問許可權限定,static表示不要例項化就可以使用 

 

    static修飾的成員變數和成員方法習慣上稱為靜態變數和靜態方法,可以直接通過類名來訪問,訪問語法為:

    類名.靜態方法名(引數列表...)

    類名.靜態變數名

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

 

1.static變數

      宣告為static的變數在定義的地方被初始化,並且只能初始化一次。

      如果初始化,就生成類初始化函式<clinit>,否則沒有:a.在它被宣告的時候賦值, b.在靜態或非靜態快裡初始化 

      按照是否靜態的對類成員變數進行分類可分兩種:一種是被static修飾的變數,叫靜態變數或類變數;另一種是沒有被static修飾的變數,叫例項變數。兩者的區別是: 

     在類被載入時static修飾的成員變數被初始化,與類關聯,只要類存在,static變數就存在。對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次記憶體,在載入類的過程中完成靜態變數的記憶體分配,可用類名直接訪問(方便),當然也可以通過物件來訪問(但是這是不推薦的)。一個static變數單獨劃分一塊儲存空間,不與具體的物件繫結在一起,該儲存空間被類的各個物件所共享。也就是說當宣告一個物件時,並不產生static變數的拷貝,而是該類所有的例項物件共用同一個static變數。static修飾的成員變數能在建立任何例項物件之前被訪問,而不必引用任何物件,也就是說不管建立多少物件,static修飾的變數只佔有一塊記憶體。當執行時,在程式空間中,類的所有物件訪問到的靜態變數都是同一個值,當其中一個物件改變了靜態變數的值,其他物件都將受到影響。

      對於例項變數,沒建立一個例項,就會為例項變數分配一次記憶體,例項變數可以在記憶體中有多個拷貝,互不影響(靈活)。當某個物件被建立時,它們才真正地存在於記憶體空間之中,而且物件本身對它們的改變,不會影響到其它物件。就好像 Person的一個物件zhangsan,zhangsan的改變不會影響到其它Person物件一樣。

 

      被static修飾而沒有被final修飾的類的屬性變數只能在兩種情況下初始化:(可以不初始化) 

      如果初始化,就生成類初始化函式<clinit>,否則沒有

      a.在它被宣告的時候賦值,例:

       public class Test{

          public static l int a=8;

          private Test(){

          }

       }

 

      b.在靜態或非靜態快裡初始化

      public class Test{

          public static l int a;

          static{a=50;}

          private Test(){

          }

      }

解釋:

當類的屬性被同時被修飾為static時候,他屬於類的資源(類變數),在類載入後,進行連線時候,分三步: 先驗證;然後準備,準備時,先分配記憶體,接著預設初始化;可以進行解析。最後,進行類初始化,類初始化前,必須保證它的父類已經初始化了,所以最先初始化的是超類,對於介面,不必初始其父介面。類初始化時,它把類變數初始化語句及靜態初始化語句放到類初始化方法中,所以,如果無此兩種語句,也就沒<clinit>類初始化方法,而建構函式是在當類 被例項化的時候才會執行,所以用建構函式,這時候這個屬性沒有被初始化.程式就會報錯.而static塊是類被載入的時候執行,且只執行這一次,所以在 static塊中可以被初始化.

 

 

看下面這段程式碼:

class Value{

  static int c=0;

  static void inc(){

    c++;

  }

}

class Count{

  public static void prt(String s){

    System.out.println(s);

  }

  public static void main(String[] args){

    Value v1,v2;

    v1=new Value();

    v2=new Value();

    prt("v1.c="+v1.c+"  v2.c="+v2.c);

    v1.inc();

    prt("v1.c="+v1.c+"  v2.c="+v2.c);  

  }

}

結果如下:

v1.c=0  v2.c=0

v1.c=1  v2.c=1

 

由此可以證明它們共享一塊儲存區。static變數有點類似於C中的全域性變數的概念。值得探討的是靜態變數的初始化問題。我們修改上面的程式:

 

class Value{

  static int c=0;

  Value(){

    c=15;

  }

  Value(int i){

    c=i;

  }

  static void inc(){

    c++;

  }

}

class Count{

  public static void prt(String s){

    System.out.println(s);

  }

    Value v=new Value(10);

    static Value v1,v2;

    static{

      prt("v1.c="+v1.c+"  v2.c="+v2.c);

      v1=new Value(27);

      prt("v1.c="+v1.c+"  v2.c="+v2.c);

      v2=new Value(15);

      prt("v1.c="+v1.c+"  v2.c="+v2.c);

    }

 

  public static void main(String[] args){

    Count ct=new Count();

    prt("ct.c="+ct.v.c);

    prt("v1.c="+v1.c+"  v2.c="+v2.c);

    v1.inc();

    prt("v1.c="+v1.c+"  v2.c="+v2.c);

    prt("ct.c="+ct.v.c);

  }

}

 

執行結果如下:

 

v1.c=0  v2.c=0

v1.c=27  v2.c=27

v1.c=15  v2.c=15

ct.c=10

v1.c=10  v2.c=10

v1.c=11  v2.c=11

ct.c=11

 

  這個程式展示了靜態初始化的各種特性。如果你初次接觸Java,結果可能令你吃驚。可能會對static後加大括號感到困惑。首先要告訴你的是,static定義的變數會優先於任何其它非static變數,不論其出現的順序如何。正如在程式中所表現的,雖然v出現在v1和v2的前面,但是結果卻是v1和v2的初始化在v的前面。在static{後面跟著一段程式碼,這是用來進行顯式的靜態變數初始化,這段程式碼只會初始化一次,且在類被第一次裝載時。如果你能讀懂並理解這段程式碼,會幫助你對static關鍵字的認識。在涉及到繼承的時候,會先初始化父類的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

 

2.static方法

 

    靜態方法可以直接通過類名呼叫,任何的例項也都可以呼叫,因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的例項變數和例項方法(就是不帶static的成員變數和成員成員方法),只能訪問所屬類的靜態成員變數和成員方法。因為static方法獨立於任何例項,因此static方法必須被實現,而不能是抽象的abstract。

示例程式碼:

class Simple{

   static void go(){

     System.out.println("Go...");

   }

}

public class Cal{

  public static void main(String[] args){

    Simple.go();

  }

}

呼叫一個靜態方法就是“類名.方法名”,靜態方法的使用很簡單如上所示。一般來說,靜態方法常常為應用程式中的其它類提供一些實用工具所用,在Java的類庫中大量的靜態方法正是出於此目的而定義的。     

     宣告為static的方法有以下幾條限制:

     ◆它們僅能呼叫其他的static 方法。 

     ◆它們只能訪問static資料。 

     ◆它們不能以任何方式引用this 或super

     ◆靜態方法中不能定義靜態變數

 

3.static程式碼塊

 

    static程式碼塊也叫靜態程式碼塊,是在類中獨立於類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態的程式碼塊,如果static程式碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個程式碼塊只會被執行一次。靜態程式碼塊沒有名字,因此不能顯式呼叫,而只有在類載入的時候由虛擬機器來呼叫。它主要用來完成一些初始化操作。

 

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

static final用來修飾成員變數和成員方法,可簡單理解為“全域性常量”! 

對於變數,表示一旦給值就不可修改,並且通過類名可以訪問。 

對於方法,表示不可覆蓋,並且可以通過類名直接訪問。

同時被final和static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)

 a.在它被定義的時候,例: 

   public class Test{

       public final static int a=5;

       private Test(){

       }

   }

 b.在類的靜態塊裡初始化,例: 

   public class Test{

       public final static int a;

       static{

         a=0;

       }

   }

 c、特別對於初始化時候呼叫丟擲異常的建構函式,初始時候注意,特別是在實現單例模式時(只能這麼初始化)

如:

   class A { 

       private final static A a;

       static {

         try {

            a=new A();

         }catch(Exception e){

            throws new RuntimeException(e);  //必須有,不然不能完成常量的正確初始化

         }

       }

       private A() throws Exception{}

    }

解釋:

當類的屬性被同時被修飾為static和final的時候,他屬於類的資源(類常量),那麼就是類在被載入進記憶體的時候(也就是應用程 序啟動的時候)就要已經為此屬性分配了記憶體,所以此時屬性已經存在,它又被final修飾,所以必須在屬性定義了以後就給其初始化值.而建構函式是在當類 被例項化的時候才會執行,所以用建構函式,這時候這個屬性沒有被初始化.程式就會報錯.而static塊是類被載入的時候執行,且只執行這一次,所以在 static塊中可以被初始化.

 

 

static特殊講解:

  請先看下面這段程式:

 

  public class Hello{

    public static void main(String[] args){ //(1)

      System.out.println("Hello,world!");   //(2)

    }

  }

 

  看過這段程式,對於大多數學過Java 的從來說,都不陌生。即使沒有學過Java,而學過其它的高階語言,例如C,那你也應該能看懂這段程式碼的意思。它只是簡單的輸出“Hello,world”,一點別的用處都沒有,然而,它卻展示了static關鍵字的主要用法。

 

    在1處,我們定義了一個靜態的方法名為main,這就意味著告訴Java編譯器,我這個方法不需要建立一個此類的物件即可使用。你還記得你是怎麼執行這個程式嗎?一般,我們都是在命令列下,打入如下的命令:

javac Hello.java

java Hello

Hello,world!

  這就是你執行的過程,第一行用來編譯Hello.java這個檔案,執行完後,如果你檢視當前,會發現多了一個Hello.class檔案,那就是第一行產生的Java二進位制位元組碼。第二行就是執行一個Java程式的最普遍做法。執行結果如你所料。在2中,你可能會想,為什麼要這樣才能輸出。好,我們來分解一下這條語句。(如果沒有安裝Java文件,請到Sun的官方網站瀏覽J2SE API)首先,System是位於java.lang包中的一個核心類,如果你檢視它的定義,你會發現有這樣一行:public static final PrintStream out;接著再進一步,點選PrintStream這個超連結,在METHOD頁面,你會看到大量定義的方法,查詢println,會有這樣一行:

 

public void println(String x)。

好了,現在你應該明白為什麼我們要那樣呼叫了,out是System的一個靜態變數,所以可以直接使用,而out所屬的類有一個println方法。

 

通常一個普通類不允許宣告為靜態的,只有一個內部類才可以。這時這個宣告為靜態的內部類可以直接作為一個普通類來使用,而不需例項一個外部類。如下程式碼所示:

 

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

 

 

 

static,final初始化總結:

1、被final修飾而沒有被static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)

   a.在它被宣告的時候賦值;b.在建構函式裡初始化;c .在非靜態塊裡

2、被static修飾而沒有被final修飾的類的屬性變數只能在兩種情況下初始化:(可以不初始化)

   a.在它被宣告的時候賦值;b.在靜態或非靜態快裡初始化;

3、同時被final和static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)

   a.在它被定義的時候;b.在類的靜態塊裡初始化

 

 

 

 

1.final變數:

 

      當你在類中定義變數時,在其前面加上final關鍵字,那便是說,這個變數一旦被初始化便不可改變,這裡不可改變的意思對基本型別來說是其值不可變,而對於物件變數來說其引用不可再變。其初始化可以在兩個地方,一是其定義處,也就是說在final變數定義時直接給其賦值,二是在建構函式中。這兩個地方只能選其一,要麼在定義時給值,要麼在建構函式中給值,不能同時既在定義時給了值,又在建構函式中給另外的值。另外也可以在非靜態塊裡初始化

   public class Test{

       private final int a;

          {

             a=3;  //在非靜態塊裡初始化 

          }

   }

解釋:當這個屬性被修飾為final,而非static的時候,它屬於類的例項物件的資源(例項常量),當類被載入進記憶體的時候這個屬性並沒有給其分配記憶體空間,而只是 定義了一個變數a,只有當類被例項化的時候這個屬性才被分配記憶體空間,而例項化的時候同時執行了建構函式,所以屬性被初始化了,也就符合了當它被分配記憶體 空間的時候就需要初始化,以後不再改變的條件.

 

     當函式引數為final型別時,你可以讀取使用該引數,但是無法改變該引數的值。

 

     另外方法中的內部類在用到方法中的參變數時,此參變也必須宣告為final才可使用

 

2.final方法

 

     如果一個類不允許其子類覆蓋某個方法,則可以把這個方法宣告為final方法。 

使用final方法的原因有二: 

    第一、把方法鎖定,防止任何繼承類修改它的意義和實現。 

    第二、高效。編譯器在遇到呼叫final方法時候會轉入內嵌機制,大大提高執行效率。

 

3.final類

 

     final類不能被繼承,因此final類的成員方法沒有機會被覆蓋,預設都是final的。在設計類時候,如果這個類不需要有子類,類的實現細節不允許改變,並且確信這個類不會載被擴充套件,那麼就設計為final類。

 

由於學習需要找了半天找到了這篇文章,發現作者寫的很不錯,就轉載給大家