淺析static關(guān)鍵字

前言

今天讀了一些有關(guān)連接池的源代碼,發(fā)現(xiàn)一些類、接口和方法都用static修飾了,感覺怪怪的,有必要嗎?我自己寫的代碼基本很少用到static,除了一些常量,公共資源類,工具類等用到。大家都知道,用static修飾過的東西,都會(huì)在類加載的時(shí)候加載到方法區(qū),以便使用,既省時(shí),又提高了效率。前言部分基本上說了我今天寫這篇文章的目的。所以今天就細(xì)細(xì)分析一下唄!

一.static是什么東西?

static英文翻譯是靜態(tài)的。靜態(tài)是什么意思?是非動(dòng)態(tài)的,是不經(jīng)常變的,拿來就可以用。

二.static用途

1.修飾成員變量?

在我們平時(shí)的使用當(dāng)中,static最常用的功能就是修飾類的屬性和方法,讓他們成為類的成員屬性和方法,我們通常將用static修飾的成員稱為類成員或者靜態(tài)成員,這句話挺起來都點(diǎn)奇怪,其實(shí)這是相對(duì)于對(duì)象的屬性和方法來說的。請(qǐng)看下面的例子:(未避免程序太過臃腫,暫時(shí)不管訪問控制)

上面的代碼我們很熟悉,根據(jù)Person構(gòu)造出的每一個(gè)對(duì)象都是獨(dú)立存在的,保存有自己獨(dú)立的成員變量,相互不會(huì)影響,他們?cè)趦?nèi)存中的示意如下:

從上圖中可以看出,p1和p2兩個(gè)變量引用的對(duì)象分別存儲(chǔ)在內(nèi)存中堆區(qū)域的不同地址中,所以他們之間相互不會(huì)干擾。但其實(shí),在這當(dāng)中,我們省略了一些重要信息,相信大家也都會(huì)想到,對(duì)象的成員屬性都在這了,由每個(gè)對(duì)象自己保存,那么他們的方法呢?實(shí)際上,不論一個(gè)類創(chuàng)建了幾個(gè)對(duì)象,他們的方法都是一樣的:

從上面的圖中我們可以看到,兩個(gè)Person對(duì)象的方法實(shí)際上只是指向了同一個(gè)方法定義。這個(gè)方法定義是位于內(nèi)存中的一塊不變區(qū)域(由jvm劃分),我們暫稱它為靜態(tài)存儲(chǔ)區(qū)。這一塊存儲(chǔ)區(qū)不僅存放了方法的定義,實(shí)際上從更大的角度而言,它存放的是各種類的定義,當(dāng)我們通過new來生成對(duì)象時(shí),會(huì)根據(jù)這里定義的類的定義去創(chuàng)建對(duì)象。多個(gè)對(duì)象僅會(huì)對(duì)應(yīng)同一個(gè)方法,這里有一個(gè)讓我們充分信服的理由,那就是不管多少的對(duì)象,他們的方法總是相同的,盡管最后的輸出會(huì)有所不同,但是方法總是會(huì)按照我們預(yù)想的結(jié)果去操作,即不同的對(duì)象去調(diào)用同一個(gè)方法,結(jié)果會(huì)不盡相同。

我們知道,static關(guān)鍵字可以修飾成員變量和方法,來讓它們變成類的所屬,而不是對(duì)象的所屬,比如我們將Person的age屬性用static進(jìn)行修飾,結(jié)果會(huì)是什么樣呢?請(qǐng)看下面的例子:

我們發(fā)現(xiàn),結(jié)果發(fā)生了一點(diǎn)變化,在給p2的age屬性賦值時(shí),干擾了p1的age屬性,這是為什么呢?我們還是來看他們?cè)趦?nèi)存中的示意:

我們發(fā)現(xiàn),給age屬性加了static關(guān)鍵字之后,Person對(duì)象就不再擁有age屬性了,age屬性會(huì)統(tǒng)一交給Person類去管理,即多個(gè)Person對(duì)象只會(huì)對(duì)應(yīng)一個(gè)age屬性,一個(gè)對(duì)象如果對(duì)age屬性做了改變,其他的對(duì)象都會(huì)受到影響。我們看到此時(shí)的age和toString()方法一樣,都是交由類去管理。

雖然我們看到static可以讓對(duì)象共享屬性,但是實(shí)際中我們很少這么用,也不推薦這么使用。因?yàn)檫@樣會(huì)讓該屬性變得難以控制,因?yàn)樗谌魏蔚胤蕉加锌赡鼙桓淖?。如果我們想共享屬性,一般我們?huì)采用其他的辦法:

上面的代碼起到了給Person的對(duì)象創(chuàng)建一個(gè)唯一id以及記錄總數(shù)的作用,其中count由static修飾,是Person類的成員屬性,每次創(chuàng)建一個(gè)Person對(duì)象,就會(huì)使該屬性自加1然后賦給對(duì)象的id屬性,這樣,count屬性記錄了創(chuàng)建Person對(duì)象的總數(shù),由于count使用了private修飾,所以從類外面無法隨意改變。

2.修飾成員方法

static的另一個(gè)作用,就是修飾成員方法。相比于修飾成員屬性,修飾成員方法對(duì)于數(shù)據(jù)的存儲(chǔ)上面并沒有多大的變化,因?yàn)槲覀儚纳厦婵梢钥闯?,方法本來就是存放在類的定義當(dāng)中的。static修飾成員方法最大的作用,就是可以使用"類名.方法名"的方式操作方法,避免了先要new出對(duì)象的繁瑣和資源消耗,我們可能會(huì)經(jīng)常在幫助類中看到它的使用:

上面便是一個(gè)例子(現(xiàn)在還不太實(shí)用),但是我們可以看到它的作用,使得static修飾的方法成為類的方法,使用時(shí)通過“類名.方法名”的方式就可以方便的使用了,相當(dāng)于定義了一個(gè)全局的函數(shù)(只要導(dǎo)入該類所在的包即可)。不過它也有使用的局限,一個(gè)static修飾的類中,不能使用非static修飾的成員變量和方法,這很好理解,因?yàn)閟tatic修飾的方法是屬于類的,如果去直接使用對(duì)象的成員變量,它會(huì)不知所措(不知該使用哪一個(gè)對(duì)象的屬性)。

3.靜態(tài)代碼塊

在說明static關(guān)鍵字的第三個(gè)用法時(shí),我們有必要重新梳理一下一個(gè)對(duì)象的初始化過程。以下面的代碼為例:

上面的例子中,Person類中組合了四個(gè)Book成員變量,兩個(gè)是普通成員,兩個(gè)是static修飾的類成員。我們可以看到,當(dāng)我們new一個(gè)Person對(duì)象時(shí),static修飾的成員變量首先被初始化,隨后是普通成員,最后調(diào)用Person類的構(gòu)造方法完成初始化。也就是說,在創(chuàng)建對(duì)象時(shí),static修飾的成員會(huì)首先被初始化,而且我們還可以看到,如果有多個(gè)static修飾的成員,那么會(huì)按照他們的先后位置進(jìn)行初始化。

實(shí)際上,static修飾的成員的初始化可以更早的進(jìn)行,請(qǐng)看下面的例子:


在上面的例子中我們可以發(fā)現(xiàn)兩個(gè)有意思的地方,第一個(gè)是當(dāng)我們沒有創(chuàng)建對(duì)象,而是通過類去調(diào)用類方法時(shí),盡管該方法沒有使用到任何的類成員,類成員還是在方法調(diào)用之前就初始化了,這說明,當(dāng)我們第一次去使用一個(gè)類時(shí),就會(huì)觸發(fā)該類的成員初始化。第二個(gè)是當(dāng)我們使用了類方法,完成類的成員的初始化后,再new該類的對(duì)象時(shí),static修飾的類成員沒有再次初始化,這說明,static修飾的類成員,在程序運(yùn)行過程中,只需要初始化一次即可,不會(huì)進(jìn)行多次的初始化。

回顧了對(duì)象的初始化以后,我們?cè)賮砜磗tatic的第三個(gè)作用就非常簡(jiǎn)單了,那就是當(dāng)我們初始化static修飾的成員時(shí),可以將他們統(tǒng)一放在一個(gè)以static開始,用花括號(hào)包裹起來的塊狀語句中:

我們將上一個(gè)例子稍微做了一下修改,可以看到,結(jié)果沒有二致。

4.靜態(tài)內(nèi)部類

主要功能是訪問方便,無需new一個(gè)外部類,就可以訪問。

注意:不允許修飾普通類

5.靜態(tài)內(nèi)部接口

Static inner interface and inner interface is the same,用static修飾內(nèi)部接口其實(shí)沒什么意義。

三.static的誤區(qū)

1.能通過this訪問靜態(tài)成員變量嗎?

雖然對(duì)于靜態(tài)方法來說沒有this,那么在非靜態(tài)方法中能夠通過this訪問靜態(tài)成員變量嗎?先看下面的一個(gè)例子,這段代碼輸出的結(jié)果是什么?

public?class?Main {  ????

????static?int?value =?33;?????

????public?static?void?main(String[] args)?throws?Exception{????????

????????new?Main().printValue();? ? //輸出的是33

????}?????

????private?void?printValue(){????????

????????int?value =?3;????????

????????System.out.println(this.value);????

????}

}

這里面主要考察隊(duì)this和static的理解。this代表什么?this代表當(dāng)前對(duì)象,那么通過new Main()來調(diào)用printValue的話,當(dāng)前對(duì)象就是通過new Main()生成的對(duì)象。而static變量是被對(duì)象所享有的,因此在printValue中的this.value的值毫無疑問是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)限足夠)。

2.static能作用于局部變量么?

在C/C++中static是可以作用域局部變量的,但是在Java中切記:static是不允許用來修飾局部變量。不要問為什么,這是Java語法的規(guī)定。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容