Java static基本認知


 

 

一、 static的用途

  在Java編程思想中有這么一句話:“static方法就是沒有this的方法。在static方法內部不能調用非靜態方法,反過來是可以的。而且可以在沒有創建任何對象的前提下,僅僅通過類本身來調用static方法。這實際上正是static方法的主要用途”

  這句話在我理解來說就相當於是static方法是屬於類的,而非靜態方法是屬於對象的。在實際應用中就相當於非靜態方法只有在new出相應的對象來才能調用,而靜態方法只需要’類名.方法’即可調用。

  在什么情況下我們需要使用static關鍵字呢?

a、 對於一個類相對固定的一些參數及方法

b、 一些需要預先定義的參數及方法

c、 一些調用廣泛的公共參數及方法

在我看來static關鍵字的核心是避免一些內存的重復占用,我們想象一個java工程在開始運行時,會通過jvm分配一片空間,在這一片空間中會預先加載所有需要的類,然后就是我們在new對象時不停的占據內存。但是例如枚舉類(之后稱為Contact)中的參數是會在其他很多對象和函數中調用,如果我們每次調用的時候都需要重新new一個Contact對象,豈不是會在內存中重復占用非常多的資源。這時我們就需要static關鍵字修飾Contact類中的參數,這樣在我們預先加載所有的類時,Contact類中通過static修飾的參數就會隨着Contact類一起被加載在內存中,在我們需要的時候就可以直接通過Contact.參數名來調用這個參數而不需要重復占用另外的內存。(如果在面試中剛好被問到了,答出來應該會很加分的)

  1、 修飾類變量

    static變量一般也被稱為靜態變量,靜態變量和非靜態變量的區別是:靜態變量被所有的對象所共享,在內存中只有一個副本,它當且僅當在類初次加載時會被初始化。而非靜態變量是對象所擁有的,在創建對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。

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

接下來舉例說明:

 

這是一個基本的Man類,並生成了兩個對象,但是從類的定義上來看,我們可以認為sex參數的值固定為男,但生成了兩個對象就相當於在內存在將sex參數存儲了兩遍,這無疑是一種內存上的浪費。而這時我們給sex參數加上static關鍵字會變成什么樣呢?

 

我們只需要給Man類中的sex參數賦值就會使所有的Man對象sex值進行改變。

雖然sex參數成為了Man類的共享屬性,但是實際中我們很少這么使用,因為這樣會使得該參數很難控制,因為它在任何地方都可能被改變。所以我們一般采用其他的使用方法:

 

 

通過private+static修飾count屬性,並在Man的構造函數中對Man對象的數量進行統計並賦值給id屬性來區分不同的Man對象。並且因為private修飾外界無法隨意更改countid參數。

  2、 修飾類方法

與靜態參數使用場景基本相同,但是需要注意的是,同一個類,靜態方法中無法直接調用非靜態參數及非靜態方法,但非靜態方法中可以直接調用靜態方法及靜態參數。

 

 

  3、 靜態代碼塊

靜態代碼塊主要作用是在類加載之前進行一些數據的處理,對需要參數或對象的初始化。保證接下來對該參數或對象的使用。

 

這個例子就大致展現出了靜態代碼塊的一些基本的作用,另外我們可以看看這個例子,展現了靜態代碼塊和對象初始化時的加載順序:

 

 

  4、 靜態導包

靜態導包是一種比較少見是static使用方式,接下來使用一個例子來說明一下吧:

 

 

如圖,當我們使用import static staticpkg.Man.*;時,在不與本類方法名沖突的前提下,我們可以在本類中直接使用Man類中的static方法,就像是本類自己的方法一樣。但是相應的其他靜態參數和一下非靜態參數和非靜態方法就無法直接調用了。

 

5、 靜態內部類

 

首先簡單描述一下什么是內部類,Java中是允許在類的構建中定義另一個類的,內部類有很多優點,首先就是不能被同一包的其他類所見,具有很好的封裝性,其次還有就是內部類對象可以訪問創建它的對象的實現,包括私有數據等;

 

還是舉個例子說明一下:

 

 

 

輸出結果為:

 

 

從例子中的注釋我們可以看到內部類有許多的特性:

 

1、 靜態內部類中可以存在靜態成員,而非靜態內部類中不能有靜態成員。

2、 靜態內部類可以訪問外部類的靜態成員,而非靜態內部類則可以訪問外部類的所有成員。

3、 如果需要在其他類中實例化本類中的非靜態內部類,需要先修改非靜態內部類的訪問限制符,然后實例化外部類,再通過外部類對象實例化非靜態內部類。

4、 如果需要在其他類中實例化本類中的靜態內部類,需要先修改靜態內部類的訪問限制符,然后直接通過new 該靜態內部類就可以了。

5、 調用靜態內部類的靜態成員時,可以通過外部類名.內部靜態類名.靜態參數名稱或靜態方法名。

 

二、 static作用分析

 

1、 之前說了static是跟隨着類進行加載的,那么我們是否能通過this來進行靜態參數的調用呢?

 

 

我們可以看到雖然靜態參數是隨着類進行加載的,但是在參數名有沖突的情況下依然可以通過this來進行參數調用,以此區分全局變量和局部變量的不同。

 

2、 static是跟跟隨着類進行加載的,那么伴隨着類、基礎、對象的初始化過程中相應的加載順序是怎樣的?

舉例1

 

 

 

 

這兩段代碼的輸出結果會是怎樣的?

我們說了static是跟着類一起進行加載的,那么我們在new Test3()時會加載Test3類這時發現Test3繼承Test2那么我們需要先加載Test2類的靜態代碼塊,再加載Test3類的靜態代碼塊。類加載完成后我們開始通過Test3提供的構造器來今天Test3對象的初始化,而因為Test3繼承了Test2所以在編譯時默認Test3的構造器中有一個super()的調用,意思就是先加載Test2的構造器再加載Test3的構造器。所以這兩段的代碼的輸出結果如下:

 

 

舉例2

 

 

 

 

 

 

這三段代碼的加載順序又應該是怎樣的呢?

首先和舉例1一樣main函數中加載Test3類,但是因為繼承Test2所以先加載Test2類的靜態代碼塊,然后是Test3的靜態代碼塊,然后開始加載Test3的構造函數了,同樣因為繼承需要先加載Test2的構造函數,但是需要初始化Test2對象時需要將Test2類中的全局變量進行逐行加載,這時我們就需要加載new Test4(“test22222222”),這時就需要加載Test4類了,執行Test4類的靜態代碼塊,然后執行Test4的構造器,Test4對象加載完成,進行Test2類的構造器加載,然后到了Test3類構造器加載,同上需要先加載Test4類,因為Test4類的靜態代碼塊已經加載完成無需重復加載,所以加載Test4的構造器,然后加載Test3的構造器,所以這三段代碼的輸出結果應為:

 

 

舉例3

 

 

Main函數中明明什么都沒寫為什么還是會打印呢?

因為在加載main函數時,會先對Test5類進行逐行加載,這時會從上到下依次加載兩個靜態代碼塊,加載完成后,本類加載完成開始執行main函數中的代碼,結果main函數中沒有代碼,程序結束。

 

以上是我對static的一些基本的認知,希望能對你有所幫助,代碼依然是圖片,代碼還是手敲一遍試一下更有感覺的。

 

參考博客:

https://www.cnblogs.com/dotgua/p/6354151.html?utm_source=itdadao&utm_medium=referral

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

https://www.cnblogs.com/aademeng/articles/6192954.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM