static 關(guān)鍵字
《Java編程思想》 中:static方法就是沒有this的方法
static 修飾的方法或者變量,不需要依賴實(shí)例對(duì)象就可以訪問,只要類被JVM加載了,就可以通過類名來(lái)訪問。
static 可以修飾方法,變量,還以編寫static代碼塊優(yōu)化程序性能
1. static 變量
static 修飾的變量稱為 靜態(tài)變量。
- 靜態(tài)變量: 被所有對(duì)象共享,在內(nèi)存中只有一個(gè)副本,它當(dāng)且僅當(dāng)在類加載時(shí)候會(huì)被初始化。
- 非靜態(tài)變量:為該對(duì)象所擁有,在內(nèi)存中存在多個(gè)副本,在對(duì)象創(chuàng)建的時(shí)候,初始化。
注意,前面這段總結(jié)是有點(diǎn)抽象,所以分別舉個(gè)例子加以詳細(xì)說明:
非靜態(tài)變量
public class TestMain {
public static void main(String[] args) {
new Apple();
}
}
public class Apple {
Orange orange = new Orange();
public Apple() {
System.out.println("Apple Construct");
}
}
public class Orange {
public Orange() {
System.out.println("Orange Construct");
}
}
運(yùn)行main方法后打印的后的輸出如下:
Orange Construct
Apple Construct
在執(zhí)行new Apple()這句代碼的時(shí)候,會(huì)創(chuàng)建Apple實(shí)例對(duì)象,然后執(zhí)行Apple()的無(wú)參構(gòu)造方法,在執(zhí)行構(gòu)造方法的時(shí)候,會(huì)先初始化父類構(gòu)造方法,這里Apple()對(duì)象沒有父類,解析來(lái)初始化Apple對(duì)象非靜態(tài)成員變量,也就是 Orange orange = new Orange(); 這段代碼,接下來(lái)Orange實(shí)例話的過程和Appple實(shí)例話的過程是一樣的。
實(shí)際上,如果我們對(duì)實(shí)例變量直接賦值或者使用實(shí)例代碼塊賦值,那么編譯器會(huì)將其中的代碼放到類的構(gòu)造函數(shù)中去,并且這些代碼會(huì)被放在對(duì)超類構(gòu)造函數(shù)的調(diào)用語(yǔ)句之后(還記得嗎?Java要求構(gòu)造函數(shù)的第一條語(yǔ)句必須是超類構(gòu)造函數(shù)的調(diào)用語(yǔ)句),構(gòu)造函數(shù)本身的代碼之前
靜態(tài)變量
在我們寫懶漢單例模式的時(shí)候,會(huì)用到靜態(tài)變量??聪旅娴睦?/p>
public class SingletonLazyNotSafe {
private static SingletonLazyNotSafe instance = null;
private SingletonLazyNotSafe() {
}
public static SingletonLazyNotSafe getInstance() {
if (instance == null) {
instance = new SingletonLazyNotSafe();
}
return instance;
}
}
這種懶漢單例模式是非線程安全,但是它利于我們分析代碼。為什么說是懶漢呢,loazyLoad,就是因?yàn)?把靜態(tài)變量 instance 設(shè)為null,這是因?yàn)樵贘VM加載Class對(duì)象的時(shí)候,會(huì)在加載的最后一個(gè)階段初始化階段,初始化類的靜態(tài)域,如果像餓漢單例模式那也直接將 instance = SingletonLazyNotSafe(),那樣會(huì)直接在類加載階段就直接初始化了該對(duì)象,可能這個(gè)時(shí)候還沒有真正用到該對(duì)象,這樣造成內(nèi)存空間的浪費(fèi)。
JVM加載類的過程分為三個(gè)階段,第一個(gè)階段加載字節(jié)文件,第二個(gè)階段 鏈接字節(jié)文件, 驗(yàn)證字節(jié)文件安全,分配靜態(tài)域內(nèi)存,第三個(gè)階段,初始化,類加載最后階段,如果有父類,則先對(duì)其初始化,然后執(zhí)行靜態(tài)域的初始化。
?
2. static 方法
static 關(guān)鍵字修飾的方法,一般稱為靜態(tài)方法,靜態(tài)方法沒有關(guān)鍵字this。
類的構(gòu)造方法是不是靜態(tài)方法呢?答案是否定的,很明顯,構(gòu)造方法里面有this關(guān)鍵字,可以引用成員變量。
Java虛擬機(jī)規(guī)范第二版中定義了四種不同的字節(jié)碼指令來(lái)處理Java程序中不同種類的方法的調(diào)用:
- invokestatic : 用于調(diào)用類(靜態(tài))方法
- invokespecial :用于調(diào)用實(shí)例方法,特化于super方法調(diào)用、private方法調(diào)用與構(gòu)造器調(diào)用
- invokevirtual :用于調(diào)用一般實(shí)例方法(包括聲明為final但不為private的實(shí)例方法)
- invokeinterface :用于調(diào)用接口方法
invokestatic與invokespecial調(diào)用的目標(biāo)必然是可以靜態(tài)綁定的(比如不被繼承),因?yàn)樗鼈兌紵o(wú)法參與子類型多態(tài)。
invokevirtual與invokeinterface的則一般需要做運(yùn)行時(shí)綁定。
JVM實(shí)現(xiàn)可以有選擇的根據(jù)final或?qū)嶋H運(yùn)行時(shí)類層次或類型反饋等信息試圖進(jìn)行靜態(tài)綁定。
那么Java中的實(shí)例構(gòu)造器是不是“靜態(tài)方法”呢?從Java語(yǔ)言規(guī)范中給出的“靜態(tài)方法”的定義來(lái)看,答案是“否”——首先從Java語(yǔ)言規(guī)范對(duì)“方法”的定義來(lái)說,構(gòu)造器根本不是“方法”;其次,實(shí)例構(gòu)造器有一個(gè)隱式參數(shù),“this”,在實(shí)例構(gòu)造器中可以訪問“this”,可以通過“this”訪問到正在初始化的對(duì)象實(shí)例的所有實(shí)例成員。
?
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í)行一次。
?
4. static 靜態(tài)內(nèi)部類
static還可以用來(lái)修飾內(nèi)部類,但是外部類不能用static修飾,編譯器會(huì)直接報(bào)錯(cuò)。
非static修飾的內(nèi)部類,不可以在其中定義static變量,或者static方法,或者static代碼塊。
非靜態(tài)內(nèi)部類,可以直接訪問外部類的成員變量和成員方法,哪怕是private修飾的
靜態(tài)內(nèi)部類,可以定義static變量,或者static方法,或者static代碼塊
靜態(tài)內(nèi)部類,不能外部類的成員變量和成員方法,靜態(tài)內(nèi)部類在實(shí)例化的時(shí)候不需要和外部類綁定。