深刻理解java的static關鍵字

static關鍵字是不少朋友在編寫代碼和閱讀代碼時碰到的比較難以理解的一個關鍵字,也是各大公司的面試官喜歡在面試時問到的知識點之一。下面就先講述一下static關鍵字的用法和日常容易誤解的地方,最後列舉了一些面試筆試中常見的關於static的考題。如下是本文的目錄大綱:html

  一.static關鍵字的用途java

  二.static關鍵字的誤區面試

  三.常見的筆試面試題編程

  如有不正之處,但願諒解並歡迎批評指正。ide

  請尊重做者勞動成果,轉載請標明原文連接:性能

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

一.static關鍵字的用途

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

  「static方法就是沒有this的方法。在static方法內部不能調用非靜態方法,反過來是能夠的。並且能夠在沒有建立任何對象的前提下,僅僅經過類自己來調用static方法。這實際上正是static方法的主要用途。」this

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

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

  很顯然,被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塊能夠用來優化程序性能,是由於它的特性:只會在類加載的時候執行一次。下面看個例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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兩個對象,形成了空間浪費,若是改爲這樣效率會更好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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訪問靜態成員變量嗎?先看下面的一個例子,這段代碼輸出的結果是什麼?

1
2
3
4
5
6
7
8
9
10
11
12
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.下面這段代碼的輸出結果是什麼?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 static
test static
base constructor
test constructor

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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" );
     }
}
  View Code

  相似地,咱們仍是來想一下這段代碼的具體執行過程。首先加載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.這段代碼的輸出結果是什麼?

1
2
3
4
5
6
7
8
9
10
11
12
13
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 1
test 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編程思想》

相關文章
相關標籤/搜索