1. 程式人生 > >Java static基本認知

Java static基本認知

family 的區別 什麽 浪費 參考 無法 擁有 his 打印

一、 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方法,就像是本類自己的方法一樣。但是相應的其他靜態參數和一下非靜態參數和非靜態方法就無法直接調用了。

二、 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

Java static基本認知