(轉)Java中的static關鍵字解析

來源:互聯網
上載者:User

標籤:code   public   參考資料   定義   bsp   cte   div   題目   非靜態方法   

轉載:

http://www.cnblogs.com/dolphin0520/p/3799052.html

一.static關鍵字的用途

  在《Java編程思想》P86頁有這樣一段話:

  “static方法就是沒有this的方法。在static方法內部不能調用非靜態方法,反過來是可以的。而且可以在沒有建立任何對象的前提下,僅僅通過類本身來調用static方法。這實際上正是static方法的主要用途。”

  這段話雖然只是說明了static方法的特殊之處,但是可以看出static關鍵字的基本作用,簡而言之,一句話來描述就是:

  方便在沒有建立對象的情況下來進行調用(方法/變數)。

  很顯然,被static關鍵字修飾的方法或者變數不需要依賴於對象來進行訪問,只要類被載入了,就可以通過類名去進行訪問。

  static可以用來修飾類的成員方法、類的成員變數,另外可以編寫static代碼塊來最佳化程式效能。

1)static方法

  static方法一般稱作靜態方法,由於靜態方法不依賴於任何對象就可以進行訪問,因此對於靜態方法來說,是沒有this的,因為它不依附於任何對象,既然都沒有對象,就談不上this了。並且由於這個特性,在靜態方法中不能訪問類的非靜態成員變數和非靜態成員方法,因為非靜態成員方法/變數都是必須依賴具體的對象才能夠被調用。

  但是要注意的是,雖然在靜態方法中不能訪問非靜態成員方法和非靜態成員變數,但是在非靜態成員方法中是可以訪問靜態成員方法/變數的。舉個簡單的例子:

在上面的代碼中,由於print2方法是獨立於對象存在的,可以直接用過類名調用。假如說可以在靜態方法中訪問非靜態方法/變數的話,那麼如果在main方法中有下面一條語句:

  MyObject.print2();

  此時對象都沒有,str2根本就不存在,所以就會產生矛盾了。同樣對於方法也是一樣,由於你無法預知在print1方法中是否訪問了非靜態成員變數,所以也禁止在靜態成員方法中訪問非靜態成員方法。

  而對於非靜態成員方法,它訪問靜態成員方法/變數顯然是毫無限制的。

  因此,如果說想在不建立對象的情況下調用某個方法,就可以將這個方法設定為static。我們最常見的static方法就是main方法,至於為什麼main方法必須是static的,現在就很清楚了。因為程式在執行main方法的時候沒有建立任何對象,因此只有通過類名來訪問。

  另外記住,即使沒有顯示地聲明為static,類的構造器實際上也是靜態方法。

2)static變數

  static變數也稱作靜態變數,靜態變數和非靜態變數的區別是:靜態變數被所有的對象所共用,在記憶體中只有一個副本,它若且唯若在類初次載入時會被初始化。而非靜態變數是對象所擁有的,在建立對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。

  static成員變數的初始化順序按照定義的順序進行初始化。

3)static代碼塊

  static關鍵字還有一個比較關鍵的作用就是 用來形成靜態代碼塊以最佳化程式效能。static塊可以置於類中的任何地方,類中可以有多個static塊。在類初次被載入的時候,會按照static塊的順序來執行每個static塊,並且只會執行一次。

  為什麼說static塊可以用來最佳化程式效能,是因為它的特性:只會在類載入的時候執行一次。下面看個例子:

 

class Person{    private Date birthDate;         public Person(Date birthDate) {        this.birthDate = birthDate;    }         boolean isBornBoomer() {        Date startDate = Date.valueOf("1946");        Date endDate = Date.valueOf("1964");        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;    }}

 

isBornBoomer是用來這個人是否是1946-1964年出生的,而每次isBornBoomer被調用的時候,都會產生startDate和birthDate兩個對象,造成了空間浪費,如果改成這樣效率會更好:

 

class Person{    private Date birthDate;    private static Date startDate,endDate;    static{        startDate = Date.valueOf("1946");        endDate = Date.valueOf("1964");    }         public Person(Date birthDate) {        this.birthDate = birthDate;    }         boolean isBornBoomer() {        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;    }}

 

因此,很多時候會將一些只需要進行一次的初始化操作都放在static代碼塊中進行。

二.static關鍵字的誤區

1.static關鍵字會改變類中成員的存取權限嗎?

  有些初學的朋友會將java中的static與C/C++中的static關鍵字的功能混淆了。在這裡只需要記住一點:與C/C++中的static不同,Java中的static關鍵字不會影響到變數或者方法的範圍。在Java中能夠影響到存取權限的只有private、public、protected(包括包存取權限)這幾個關鍵字。看下面的例子就明白了:

提示錯誤"Person.age 不可視",這說明static關鍵字並不會改變變數和方法的存取權限。

2.能通過this訪問靜態成員變數嗎?

  雖然對於靜態方法來說沒有this,那麼在非靜態方法中能夠通過this訪問靜態成員變數嗎?先看下面的一個例子,這段代碼輸出的結果是什嗎?

public class Main {      static int value = 33;     public static void main(String[] args) throws Exception{        new Main().printValue();    }     private void printValue(){        int value = 3;        System.out.println(this.value);    }}

運行結果:

33

     這裡面主要考察隊this和static的理解。this代表什嗎?this代表當前對象,那麼通過new Main()來調用printValue的話,當前對象就是通過new Main()產生的對象。而static變數是被對象所享有的,因此在printValue中的this.value的值毫無疑問是33。在printValue方法內部的value是局部變數,根本不可能與this關聯,所以輸出結果是33。在這裡永遠要記住一點:靜態成員變數雖然獨立於對象,但是不代表不可以通過對象去訪問,所有的靜態方法和靜態變數都可以通過對象訪問(只要存取權限足夠)。

3.static能作用於局部變數嗎?

  在C/C++中static是可以範圍局部變數的,但是在Java中切記:static是不允許用來修飾局部變數。不要問為什麼,這是Java文法的規定。

 

三.常見的筆試面試題

 

  下面列舉一些面試筆試中經常遇到的關於static關鍵字的題目,僅供參考,如有補充歡迎下方留言。

 

1.下面這段代碼的輸出結果是什嗎?

public class Test extends Base{     static{        System.out.println("test static");    }         public Test(){        System.out.println("test constructor");    }         public static void main(String[] args) {        new Test();    }} class Base{         static{        System.out.println("base static");    }         public Base(){        System.out.println("base constructor");    }}
base statictest staticbase constructortest constructor


至於為什麼是這個結果,我們先不討論,先來想一下這段代碼具體的執行過程,
在執行開始,先要尋找到main方法,因為main方法是程式的入口,但是在執行main方法之前,
必須先載入Test類,而在載入Test類的時候發現Test類繼承自Base類,因此會轉去先載入Base類,
在載入Base類的時候,發現有static塊,便執行了static塊。在Base類載入完成之後,
便繼續載入Test類,然後發現Test類中也有static塊,便執行static塊。在載入完所需的類之後,
便開始執行main方法。在main方法中執行new Test()的時候會先調用父類的構造器,然後再調用自身的構造器。因此,便出現了上面的輸出結果。

2.這段代碼的輸出結果是什嗎?

 

 

 

 

public class Test {    Person person = new Person("Test");    static{        System.out.println("test static");    }         public Test() {        System.out.println("test constructor");    }         public static void main(String[] args) {        new MyClass();    }} class Person{    static{        System.out.println("person static");    }    public Person(String str) {        System.out.println("person "+str);    }}  class MyClass extends Test {    Person person = new Person("MyClass");    static{        System.out.println("myclass static");    }         public MyClass() {        System.out.println("myclass constructor");    }}

 

test staticmyclass staticperson staticperson Testtest constructorperson MyClassmyclass constructor


類似地,我們還是來想一下這段代碼的具體執行過程。首先載入Test類,
因此會執行Test類中的static塊。接著執行new MyClass(),而MyClass類還沒有被載入,
因此需要載入MyClass類。在載入MyClass類的時候,發現MyClass類繼承自Test類,
但是由於Test類已經被載入了,所以只需要載入MyClass類,那麼就會執行MyClass類的中的static塊。
在載入完之後,就通過構造器來產生對象。
而在產生對象的時候,必須先初始化父類的成員變數,因此會執行Test中的Person person = new Person(),
而Person類還沒有被載入過,因此會先載入Person類並執行Person類中的static塊,接著執行父類的構造器,
完成了父類的初始化,然後就來初始化自身了,因此會接著執行MyClass中的Person person = new Person(),最後執行MyClass的構造器。

3.這段代碼的輸出結果是什嗎?

public class Test {         static{        System.out.println("test static 1");    }    public static void main(String[] args) {             }         static{        System.out.println("test static 2");    }}
test static 1test static 2


雖然在main方法中沒有任何語句,但是還是會輸出,原因上面已經講述過了。
另外,static塊可以出現類中的任何地方(只要不是方法內部,記住,任何方法內部都不行),並且執行是按照static塊的順序執行的。

 

     參考資料:

  http://lavasoft.blog.51cto.com/62575/18771/

 

  http://www.51cto.com/specbook/24/35011.htm

 

  http://blog.csdn.net/zhu_apollo/article/details/1888219

 

  http://blog.sina.com.cn/s/blog_70b845780100n9zz.html

 

  http://hi.baidu.com/yuiezt/item/b71ff5fbfe9c385cc8f3370d

 

  http://bbs.csdn.net/topics/330251070

 

  http://yezixingchen.iteye.com/blog/1597186

 

  《Java編程思想》

 

JAVA類首次裝入時,會對靜態成員變數或方法進行一次初始化,但方法不被調用是不會執行的,靜態成員變數和靜態初始化塊層級相同,非靜態成員變數和非靜態初始化塊層級相同。

先初始化父類的靜態代碼--->初始化子類的靜態代碼-->
初始化父類的非靜態代碼--->初始化父類建構函式--->
初始化子類非靜態代碼--->初始化子類建構函式

 

 

 

(轉)Java中的static關鍵字解析

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.