Java中的static關(guān)鍵字解析
本文轉(zhuǎn)子博客園埠通,單擊此處閱讀原文
static關(guān)鍵字是很多朋友在編寫代碼和閱讀代碼時(shí)碰到的比較難以理解的一個(gè)關(guān)鍵字豆同,也是各大公司的面試官喜歡在面試時(shí)問到的知識(shí)點(diǎn)之一。下面就先講述一下static關(guān)鍵字的用法和平常容易誤解的地方,最后列舉了一些面試筆試中常見的關(guān)于static的考題。以下是本文的目錄大綱:
一.static關(guān)鍵字的用途
二.static關(guān)鍵字的誤區(qū)
三.常見的筆試面試題
若有不正之處鹅髓,希望諒解并歡迎批評(píng)指正。請(qǐng)尊重作者勞動(dòng)成果京景,轉(zhuǎn)載請(qǐng)標(biāo)明原文鏈接:
http://www.cnblogs.com/dolphin0520/p/3799052.html
在《Java編程思想》P86頁(yè)有這樣一段話:
“static方法就是沒有this的方法窿冯。在static方法內(nèi)部不能調(diào)用非靜態(tài)方法,反過來(lái)是可以的确徙。而且可以在沒有創(chuàng)建任何對(duì)象的前提下醒串,僅僅通過類本身來(lái)調(diào)用static方法。這實(shí)際上正是static方法的主要用途鄙皇∥叨模”
這段話雖然只是說明了static方法的特殊之處,但是可以看出static關(guān)鍵字的基本作用育苟,簡(jiǎn)而言之较鼓,一句話來(lái)描述就是:
方便在沒有創(chuàng)建對(duì)象的情況下來(lái)進(jìn)行調(diào)用(方法/變量)椎木。
很顯然违柏,被static關(guān)鍵字修飾的方法或者變量不需要依賴于對(duì)象來(lái)進(jìn)行訪問,只要類被加載了香椎,就可以通過類名去進(jìn)行訪問漱竖。
static可以用來(lái)修飾類的成員方法、類的成員變量畜伐,另外可以編寫static代碼塊來(lái)優(yōu)化程序性能馍惹。
1)static方法
static方法一般稱作靜態(tài)方法,由于靜態(tài)方法不依賴于任何對(duì)象就可以進(jìn)行訪問,因此對(duì)于靜態(tài)方法來(lái)說万矾,是沒有this的悼吱,因?yàn)樗灰栏接谌魏螌?duì)象,既然都沒有對(duì)象良狈,就談不上this了后添。并且由于這個(gè)特性,在靜態(tài)方法中不能訪問類的非靜態(tài)成員變量和非靜態(tài)成員方法薪丁,因?yàn)榉庆o態(tài)成員方法/變量都是必須依賴具體的對(duì)象才能夠被調(diào)用遇西。
但是要注意的是,雖然在靜態(tài)方法中不能訪問非靜態(tài)成員方法和非靜態(tài)成員變量严嗜,但是在非靜態(tài)成員方法中是可以訪問靜態(tài)成員方法/變量的粱檀。舉個(gè)簡(jiǎn)單的例子:
在上面的代碼中,由于print2方法是獨(dú)立于對(duì)象存在的漫玄,可以直接用過類名調(diào)用茄蚯。假如說可以在靜態(tài)方法中訪問非靜態(tài)方法/變量的話,那么如果在main方法中有下面一條語(yǔ)句:
MyObject.print2();
此時(shí)對(duì)象都沒有睦优,str2根本就不存在第队,所以就會(huì)產(chǎn)生矛盾了。同樣對(duì)于方法也是一樣刨秆,由于你無(wú)法預(yù)知在print1方法中是否訪問了非靜態(tài)成員變量凳谦,所以也禁止在靜態(tài)成員方法中訪問非靜態(tài)成員方法。
而對(duì)于非靜態(tài)成員方法衡未,它訪問靜態(tài)成員方法/變量顯然是毫無(wú)限制的尸执。
因此,如果說想在不創(chuàng)建對(duì)象的情況下調(diào)用某個(gè)方法缓醋,就可以將這個(gè)方法設(shè)置為static如失。我們最常見的static方法就是main方法,至于為什么main方法必須是static的送粱,現(xiàn)在就很清楚了褪贵。因?yàn)槌绦蛟趫?zhí)行main方法的時(shí)候沒有創(chuàng)建任何對(duì)象,因此只有通過類名來(lái)訪問抗俄。
另外記住脆丁,即使沒有顯示地聲明為static,類的構(gòu)造器實(shí)際上也是靜態(tài)方法动雹。
2)static變量
static變量也稱作靜態(tài)變量槽卫,靜態(tài)變量和非靜態(tài)變量的區(qū)別是:靜態(tài)變量被所有的對(duì)象所共享,在內(nèi)存中只有一個(gè)副本胰蝠,它當(dāng)且僅當(dāng)在類初次加載時(shí)會(huì)被初始化歼培。而非靜態(tài)變量是對(duì)象所擁有的震蒋,在創(chuàng)建對(duì)象的時(shí)候被初始化,存在多個(gè)副本躲庄,各個(gè)對(duì)象擁有的副本互不影響查剖。
static成員變量的初始化順序按照定義的順序進(jìn)行初始化。
3)static代碼塊
static關(guān)鍵字還有一個(gè)比較關(guān)鍵的作用就是 用來(lái)形成靜態(tài)代碼塊以優(yōu)化程序性能噪窘。static塊可以置于類中的任何地方梗搅,類中可以有多個(gè)static塊。在類初次被加載的時(shí)候效览,會(huì)按照static塊的順序來(lái)執(zhí)行每個(gè)static塊无切,并且只會(huì)執(zhí)行一次。
為什么說static塊可以用來(lái)優(yōu)化程序性能丐枉,是因?yàn)樗奶匦?只會(huì)在類加載的時(shí)候執(zhí)行一次哆键。下面看個(gè)例子:
1
2
3
4
5
6
7
8
9
10
11
12
13classPerson{
privateDate birthDate;
publicPerson(Date birthDate) {
this.birthDate = birthDate;
}
booleanisBornBoomer() {
Date startDate = Date.valueOf("1946");
Date endDate = Date.valueOf("1964");
returnbirthDate.compareTo(startDate)>=0&& birthDate.compareTo(endDate) <0;
}
}
isBornBoomer是用來(lái)這個(gè)人是否是1946-1964年出生的,而每次isBornBoomer被調(diào)用的時(shí)候瘦锹,都會(huì)生成startDate和birthDate兩個(gè)對(duì)象籍嘹,造成了空間浪費(fèi),如果改成這樣效率會(huì)更好:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16classPerson{
privateDate birthDate;
privatestaticDate startDate,endDate;
static{
startDate = Date.valueOf("1946");
endDate = Date.valueOf("1964");
}
publicPerson(Date birthDate) {
this.birthDate = birthDate;
}
booleanisBornBoomer() {
returnbirthDate.compareTo(startDate)>=0&& birthDate.compareTo(endDate) <0;
}
}
因此弯院,很多時(shí)候會(huì)將一些只需要進(jìn)行一次的初始化操作都放在static代碼塊中進(jìn)行辱士。
1.static關(guān)鍵字會(huì)改變類中成員的訪問權(quán)限嗎?
有些初學(xué)的朋友會(huì)將java中的static與C/C++中的static關(guān)鍵字的功能混淆了听绳。在這里只需要記住一點(diǎn):與C/C++中的static不同颂碘,Java中的static關(guān)鍵字不會(huì)影響到變量或者方法的作用域。在Java中能夠影響到訪問權(quán)限的只有private椅挣、public头岔、protected(包括包訪問權(quán)限)這幾個(gè)關(guān)鍵字∈笾ぃ看下面的例子就明白了:
提示錯(cuò)誤"Person.age 不可視"峡竣,這說明static關(guān)鍵字并不會(huì)改變變量和方法的訪問權(quán)限。
2.能通過this訪問靜態(tài)成員變量嗎量九?
雖然對(duì)于靜態(tài)方法來(lái)說沒有this适掰,那么在非靜態(tài)方法中能夠通過this訪問靜態(tài)成員變量嗎?先看下面的一個(gè)例子荠列,這段代碼輸出的結(jié)果是什么类浪?
1
2
3
4
5
6
7
8
9
10
11
12publicclassMain {
staticintvalue =33;
publicstaticvoidmain(String[] args)throwsException{
newMain().printValue();
}
privatevoidprintValue(){
intvalue =3;
System.out.println(this.value);
}
}
View Code
這里面主要考察隊(duì)this和static的理解。this代表什么弯予?this代表當(dāng)前對(duì)象戚宦,那么通過new Main()來(lái)調(diào)用printValue的話个曙,當(dāng)前對(duì)象就是通過new Main()生成的對(duì)象锈嫩。而static變量是被對(duì)象所享有的受楼,因此在printValue中的this.value的值毫無(wú)疑問是33。在printValue方法內(nèi)部的value是局部變量呼寸,根本不可能與this關(guān)聯(lián)艳汽,所以輸出結(jié)果是33。在這里永遠(yuǎn)要記住一點(diǎn):靜態(tài)成員變量雖然獨(dú)立于對(duì)象对雪,但是不代表不可以通過對(duì)象去訪問河狐,所有的靜態(tài)方法和靜態(tài)變量都可以通過對(duì)象訪問(只要訪問權(quán)限足夠)。
3.static能作用于局部變量么瑟捣?
在C/C++中static是可以作用域局部變量的馋艺,但是在Java中切記:static是不允許用來(lái)修飾局部變量。不要問為什么迈套,這是Java語(yǔ)法的規(guī)定捐祠。
下面列舉一些面試筆試中經(jīng)常遇到的關(guān)于static關(guān)鍵字的題目,僅供參考桑李,如有補(bǔ)充歡迎下方留言踱蛀。
1.下面這段代碼的輸出結(jié)果是什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25publicclassTestextendsBase{
static{
System.out.println("test static");
}
publicTest(){
System.out.println("test constructor");
}
publicstaticvoidmain(String[] args) {
newTest();
}
}
classBase{
static{
System.out.println("base static");
}
publicBase(){
System.out.println("base constructor");
}
}
View Code
至于為什么是這個(gè)結(jié)果贵白,我們先不討論率拒,先來(lái)想一下這段代碼具體的執(zhí)行過程,在執(zhí)行開始禁荒,先要尋找到main方法猬膨,因?yàn)閙ain方法是程序的入口,但是在執(zhí)行main方法之前呛伴,必須先加載Test類寥掐,而在加載Test類的時(shí)候發(fā)現(xiàn)Test類繼承自Base類,因此會(huì)轉(zhuǎn)去先加載Base類磷蜀,在加載Base類的時(shí)候召耘,發(fā)現(xiàn)有static塊,便執(zhí)行了static塊褐隆。在Base類加載完成之后污它,便繼續(xù)加載Test類,然后發(fā)現(xiàn)Test類中也有static塊庶弃,便執(zhí)行static塊衫贬。在加載完所需的類之后,便開始執(zhí)行main方法歇攻。在main方法中執(zhí)行new Test()的時(shí)候會(huì)先調(diào)用父類的構(gòu)造器固惯,然后再調(diào)用自身的構(gòu)造器。因此缴守,便出現(xiàn)了上面的輸出結(jié)果葬毫。
2.這段代碼的輸出結(jié)果是什么镇辉?
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
35publicclassTest {
Person person =newPerson("Test");
static{
System.out.println("test static");
}
publicTest() {
System.out.println("test constructor");
}
publicstaticvoidmain(String[] args) {
newMyClass();
}
}
classPerson{
static{
System.out.println("person static");
}
publicPerson(String str) {
System.out.println("person "+str);
}
}
classMyClassextendsTest {
Person person =newPerson("MyClass");
static{
System.out.println("myclass static");
}
publicMyClass() {
System.out.println("myclass constructor");
}
}
View Code
類似地,我們還是來(lái)想一下這段代碼的具體執(zhí)行過程贴捡。首先加載Test類忽肛,因此會(huì)執(zhí)行Test類中的static塊。接著執(zhí)行new MyClass()烂斋,而MyClass類還沒有被加載屹逛,因此需要加載MyClass類。在加載MyClass類的時(shí)候汛骂,發(fā)現(xiàn)MyClass類繼承自Test類罕模,但是由于Test類已經(jīng)被加載了,所以只需要加載MyClass類帘瞭,那么就會(huì)執(zhí)行MyClass類的中的static塊手销。在加載完之后,就通過構(gòu)造器來(lái)生成對(duì)象图张。而在生成對(duì)象的時(shí)候锋拖,必須先初始化父類的成員變量,因此會(huì)執(zhí)行Test中的Person person = new Person()祸轮,而Person類還沒有被加載過兽埃,因此會(huì)先加載Person類并執(zhí)行Person類中的static塊,接著執(zhí)行父類的構(gòu)造器适袜,完成了父類的初始化柄错,然后就來(lái)初始化自身了,因此會(huì)接著執(zhí)行MyClass中的Person person = new Person()苦酱,最后執(zhí)行MyClass的構(gòu)造器售貌。
3.這段代碼的輸出結(jié)果是什么?
1
2
3
4
5
6
7
8
9
10
11
12
13publicclassTest {
static{
System.out.println("test static 1");
}
publicstaticvoidmain(String[] args) {
}
static{
System.out.println("test static 2");
}
}
View Code
雖然在main方法中沒有任何語(yǔ)句疫萤,但是還是會(huì)輸出颂跨,原因上面已經(jīng)講述過了。另外扯饶,static塊可以出現(xiàn)類中的任何地方(只要不是方法內(nèi)部恒削,記住,任何方法內(nèi)部都不行)尾序,并且執(zhí)行是按照static塊的順序執(zhí)行的钓丰。
參考資料:
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編程思想》