深入理解static关键字
文章目錄
- 1、static存在的主要意義
- 2、static的獨(dú)特之處
- 3、static應(yīng)用場景
- 4、靜態(tài)變量和實(shí)例變量的概念
- 5、靜態(tài)變量和實(shí)例變量區(qū)別【重點(diǎn)常用】
- 6、訪問靜態(tài)變量和實(shí)例變量的兩種方式
- 7、static靜態(tài)方法
- 8、static靜態(tài)代碼塊
- 9、static變量與普通變量區(qū)別
- 10、靜態(tài)內(nèi)部類
- 11、靜態(tài)導(dǎo)包
- 12、static注意事項(xiàng)
- 13、final與static的藕斷絲連
提到static關(guān)鍵字,相信大家都不陌生,這是相對比較難以理解的一個(gè)關(guān)鍵字,相信各位也都能深深感受的到!本篇文章將好好總結(jié)一下static這個(gè)關(guān)鍵字。
在開始講static之前,我想讓各位看一段有意思的代碼:
public class Test {static{System.out.println("test static 1");}static{System.out.println("test static 2");}public static void main(String[] args) {} }看完程序,小白童鞋發(fā)話了:啥玩意?main方法中啥都沒有,能運(yùn)行啥?博主你個(gè)星星星…
運(yùn)行結(jié)果: test static 1 test static 2小白童鞋:那啥…那啥…博主我說啥了,我啥都沒說…
其實(shí),上面的代碼懂的自然懂,不懂的自然就不懂了,因?yàn)樯厦娴拇a涉及到JVM的類加載了!當(dāng)然不在本篇博客文章的范疇內(nèi),如果有興趣理解上面的程序,這篇文章可能會(huì)對你有所幫助
這篇文章絕對讓你深刻理解java類的加載以及ClassLoader源碼分析
1、static存在的主要意義
static的主要意義是在于創(chuàng)建獨(dú)立于具體對象的域變量或者方法。以致于即使沒有創(chuàng)建對象,也能使用屬性和調(diào)用方法!
static關(guān)鍵字還有一個(gè)比較關(guān)鍵的作用就是 用來形成靜態(tài)代碼塊以優(yōu)化程序性能。static塊可以置于類中的任何地方,類中可以有多個(gè)static塊。在類初次被加載的時(shí)候,會(huì)按照static塊的順序來執(zhí)行每個(gè)static塊,并且只會(huì)執(zhí)行一次。
為什么說static塊可以用來優(yōu)化程序性能,是因?yàn)樗奶匦?只會(huì)在類加載的時(shí)候執(zhí)行一次。因此,很多時(shí)候會(huì)將一些只需要進(jìn)行一次的初始化操作都放在static代碼塊中進(jìn)行。
2、static的獨(dú)特之處
1、被static修飾的變量或者方法是獨(dú)立于該類的任何對象,也就是說,這些變量和方法不屬于任何一個(gè)實(shí)例對象,而是被類的實(shí)例對象所共享。
怎么理解 “被類的實(shí)例對象所共享” 這句話呢?就是說,一個(gè)類的靜態(tài)成員,它是屬于大伙的【大伙指的是這個(gè)類的多個(gè)對象實(shí)例,我們都知道一個(gè)類可以創(chuàng)建多個(gè)實(shí)例!】,所有的類對象共享的,不像成員變量是自個(gè)的【自個(gè)指的是這個(gè)類的單個(gè)實(shí)例對象】…我覺得我已經(jīng)講的很通俗了,你明白了咩?
2、在該類被第一次加載的時(shí)候,就會(huì)去加載被static修飾的部分,而且只在類第一次使用時(shí)加載并進(jìn)行初始化,注意這是第一次用就要初始化,后面根據(jù)需要是可以再次賦值的。
3、static變量值在類加載的時(shí)候分配空間,以后創(chuàng)建類對象的時(shí)候不會(huì)重新分配。賦值的話,是可以任意賦值的!
4、被static修飾的變量或者方法是優(yōu)先于對象存在的,也就是說當(dāng)一個(gè)類加載完畢之后,即便沒有創(chuàng)建對象,也可以去訪問。
3、static應(yīng)用場景
因?yàn)閟tatic是被類的實(shí)例對象所共享,因此如果某個(gè)成員變量是被所有對象所共享的,那么這個(gè)成員變量就應(yīng)該定義為靜態(tài)變量。
因此比較常見的static應(yīng)用場景有:
1、修飾成員變量
2、修飾成員方法
3、靜態(tài)代碼塊
4、修飾類【只能修飾內(nèi)部類也就是靜態(tài)內(nèi)部類】
5、靜態(tài)導(dǎo)包
以上的應(yīng)用場景將會(huì)在下文陸續(xù)講到…
4、靜態(tài)變量和實(shí)例變量的概念
靜態(tài)變量:
static修飾的成員變量叫做靜態(tài)變量【也叫做類變量】,靜態(tài)變量是屬于這個(gè)類,而不是屬于是對象。
實(shí)例變量:
沒有被static修飾的成員變量叫做實(shí)例變量,實(shí)例變量是屬于這個(gè)類的實(shí)例對象。
還有一點(diǎn)需要注意的是:static是不允許用來修飾局部變量,不要問我問什么,因?yàn)閖ava規(guī)定的!
5、靜態(tài)變量和實(shí)例變量區(qū)別【重點(diǎn)常用】
靜態(tài)變量:
靜態(tài)變量由于不屬于任何實(shí)例對象,屬于類的,所以在內(nèi)存中只會(huì)有一份,在類的加載過程中,JVM只為靜態(tài)變量分配一次內(nèi)存空間。
實(shí)例變量:
每次創(chuàng)建對象,都會(huì)為每個(gè)對象分配成員變量內(nèi)存空間,實(shí)例變量是屬于實(shí)例對象的,在內(nèi)存中,創(chuàng)建幾次對象,就有幾份成員變量。
6、訪問靜態(tài)變量和實(shí)例變量的兩種方式
我們都知道靜態(tài)變量是屬于這個(gè)類,而不是屬于是對象,static獨(dú)立于對象。
但是各位有木有想過:靜態(tài)成員變量雖然獨(dú)立于對象,但是不代表不可以通過對象去訪問,所有的靜態(tài)方法和靜態(tài)變量都可以通過對象訪問【只要訪問權(quán)限足夠允許就行】,不理解沒關(guān)系,來個(gè)代碼就理解了
public class StaticDemo {static int value = 666;public static void main(String[] args) throws Exception{new StaticDemo().method();}private void method(){int value = 123;System.out.println(this.value);}}猜想一下結(jié)果,我猜你的結(jié)果是123,哈哈是咩?其實(shí)
運(yùn)行結(jié)果: 666當(dāng)然肯定有一些基礎(chǔ)非常扎實(shí)的大佬會(huì)問為什么會(huì)有同學(xué)認(rèn)為輸出是123 。里面定義的value=123只是一個(gè)很普通的局部變量而已 。和成員變量沒有半毛錢關(guān)系 。和靜態(tài)變量也沒有關(guān)系。是的!確實(shí)如這位大佬說的一樣!只是博主我說的同學(xué)是小白同學(xué),站在小白童鞋的角度上,所以還望大佬理解見諒!我舉的這個(gè)StaticDemo例子主要目的是說明一下this也是可以訪問static的變量的!!!value=123只是一個(gè)跑龍?zhí)椎呐浣?#xff0c;旨在讓小白同學(xué)認(rèn)清其中的關(guān)系。
回過頭再去品味一下上面的那段話,你就能非??陀^明了了,這個(gè)思想概念要有只是這種用法不推薦!
因此小結(jié)一下訪問靜態(tài)變量和實(shí)例變量的兩種方法:
靜態(tài)變量:
類名.靜態(tài)變量
對象.靜態(tài)變量(不推薦)
靜態(tài)方法:
類名.靜態(tài)方法
對象.靜態(tài)方法(不推薦)
7、static靜態(tài)方法
static修飾的方法也叫做靜態(tài)方法,不知道各位發(fā)現(xiàn)咩有,其實(shí)我們最熟悉的static靜態(tài)方法就是main方法了小白童鞋:喔好像真的是哦。由于對于靜態(tài)方法來說是不屬于任何實(shí)例對象的,this指的是當(dāng)前對象,因?yàn)閟tatic靜態(tài)方法不屬于任何對象,所以就談不上this了。
還有一點(diǎn)就是:構(gòu)造方法不是靜態(tài)方法!
8、static靜態(tài)代碼塊
先看個(gè)程序吧,看看自個(gè)是否掌握了static代碼塊,下面程序代碼繼承關(guān)系為 BaseThree——> BaseTwo——> BaseOne
BaseOne類
package com.gx.initializationblock;public class BaseOne {public BaseOne() {System.out.println("BaseOne構(gòu)造器");}{System.out.println("BaseOne初始化塊");System.out.println();}static {System.out.println("BaseOne靜態(tài)初始化塊");}}BaseTwo類
package com.gx.initializationblock;public class BaseTwo extends BaseOne {public BaseTwo() {System.out.println("BaseTwo構(gòu)造器");}{System.out.println("BaseTwo初始化塊");}static {System.out.println("BaseTwo靜態(tài)初始化塊");} }BaseThree 類
package com.gx.initializationblock;public class BaseThree extends BaseTwo {public BaseThree() {System.out.println("BaseThree構(gòu)造器");}{System.out.println("BaseThree初始化塊");}static {System.out.println("BaseThree靜態(tài)初始化塊");} }測試demo2類
package com.gx.initializationblock;/*注:這里的ABC對應(yīng)BaseOne、BaseTwo、BaseThree * 多個(gè)類的繼承中初始化塊、靜態(tài)初始化塊、構(gòu)造器的執(zhí)行順序在繼承中,先后執(zhí)行父類A的靜態(tài)塊,父類B的靜態(tài)塊,最后子類的靜態(tài)塊,然后再執(zhí)行父類A的非靜態(tài)塊和構(gòu)造器,然后是B類的非靜態(tài)塊和構(gòu)造器,最后執(zhí)行子類的非靜態(tài)塊和構(gòu)造器*/ public class Demo2 {public static void main(String[] args) {BaseThree baseThree = new BaseThree();System.out.println("-----");BaseThree baseThree2 = new BaseThree();} }運(yùn)行結(jié)果
BaseOne靜態(tài)初始化塊 BaseTwo靜態(tài)初始化塊 BaseThree靜態(tài)初始化塊 BaseOne初始化塊BaseOne構(gòu)造器 BaseTwo初始化塊 BaseTwo構(gòu)造器 BaseThree初始化塊 BaseThree構(gòu)造器 ----- BaseOne初始化塊BaseOne構(gòu)造器 BaseTwo初始化塊 BaseTwo構(gòu)造器 BaseThree初始化塊 BaseThree構(gòu)造器至于static代碼塊運(yùn)行結(jié)果不是很清晰的童鞋,詳細(xì)講解請看這篇Static靜態(tài)代碼塊以及各代碼塊之間的執(zhí)行順序
以上僅僅是讓各位明確代碼塊之間的運(yùn)行順序,顯然還是不夠的,靜態(tài)代碼塊通常用來對靜態(tài)變量進(jìn)行一些初始化操作,比如定義枚舉類,代碼如下:
public enum WeekDayEnum {MONDAY(1,"周一"),TUESDAY(2, "周二"),WEDNESDAY(3, "周三"),THURSDAY(4, "周四"),FRIDAY(5, "周五"),SATURDAY(6, "周六"),SUNDAY(7, "周日");private int code;private String desc;WeekDayEnum(int code, String desc) {this.code = code;this.desc = desc;}private static final Map<Integer, WeekDayEnum> WEEK_ENUM_MAP = new HashMap<Integer, WeekDayEnum>();// 對map進(jìn)行初始化static {for (WeekDayEnum weekDay : WeekDayEnum.values()) {WEEK_ENUM_MAP.put(weekDay.getCode(), weekDay);}}public static WeekDayEnum findByCode(int code) {return WEEK_ENUM_MAP.get(code);}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;} }當(dāng)然不僅僅是枚舉這一方面,還有我們熟悉的單例模式同樣也用到了靜態(tài)代碼塊,如下:
public class Singleton {private static Singleton instance;static {instance = new Singleton();}private Singleton() {}public static Singleton getInstance() {return instance;} }9、static變量與普通變量區(qū)別
static變量也稱作靜態(tài)變量,靜態(tài)變量和非靜態(tài)變量的區(qū)別是:靜態(tài)變量被所有的對象所共享,在內(nèi)存中只有一個(gè)副本,它當(dāng)且僅當(dāng)在類初次加載時(shí)會(huì)被初始化。而非靜態(tài)變量是對象所擁有的,在創(chuàng)建對象的時(shí)候被初始化,存在多個(gè)副本,各個(gè)對象擁有的副本互不影響。
還有一點(diǎn)就是static成員變量的初始化順序按照定義的順序進(jìn)行初始化。
10、靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類之間存在一個(gè)最大的區(qū)別,我們知道非靜態(tài)內(nèi)部類在編譯完成之后會(huì)隱含地保存著一個(gè)引用,該引用是指向創(chuàng)建它的外圍內(nèi),但是靜態(tài)內(nèi)部類卻沒有。沒有這個(gè)引用就意味著:
1、它的創(chuàng)建是不需要依賴外圍類的創(chuàng)建。
2、它不能使用任何外圍類的非static成員變量和方法。
代碼舉例(靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式)
public class Singleton {// 聲明為 private 避免調(diào)用默認(rèn)構(gòu)造方法創(chuàng)建對象private Singleton() {}// 聲明為 private 表明靜態(tài)內(nèi)部該類只能在該 Singleton 類中被訪問private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getUniqueInstance() {return SingletonHolder.INSTANCE;} }當(dāng) Singleton 類加載時(shí),靜態(tài)內(nèi)部類 SingletonHolder 沒有被加載進(jìn)內(nèi)存。只有當(dāng)調(diào)用 getUniqueInstance()方法從而觸發(fā) SingletonHolder.INSTANCE 時(shí) SingletonHolder 才會(huì)被加載,此時(shí)初始化 INSTANCE 實(shí)例,并且 JVM 能確保 INSTANCE 只被實(shí)例化一次。
這種方式不僅具有延遲初始化的好處,而且由 JVM 提供了對線程安全的支持。
11、靜態(tài)導(dǎo)包
靜態(tài)導(dǎo)包格式:import static
這兩個(gè)關(guān)鍵字連用可以指定導(dǎo)入某個(gè)類中的指定靜態(tài)資源,并且不需要使用類名調(diào)用類中靜態(tài)成員,可以直接使用類中靜態(tài)成員變量和成員方法
// Math. --- 將Math中的所有靜態(tài)資源導(dǎo)入,這時(shí)候可以直接使用里面的靜態(tài)方法,而不用通過類名進(jìn)行調(diào)用 // 如果只想導(dǎo)入單一某個(gè)靜態(tài)方法,只需要將換成對應(yīng)的方法名即可import static java.lang.Math.; // 換成import static java.lang.Math.max;具有一樣的效果public class Demo {public static void main(String[] args) {int max = max(1,2);System.out.println(max);} }靜態(tài)導(dǎo)包在書寫代碼的時(shí)候確實(shí)能省一點(diǎn)代碼,可以直接調(diào)用里面的靜態(tài)成員,但是會(huì)影響代碼可讀性,所以開發(fā)中一般情況下不建議這么使用。
12、static注意事項(xiàng)
1、靜態(tài)只能訪問靜態(tài)。
2、非靜態(tài)既可以訪問非靜態(tài)的,也可以訪問靜態(tài)的。
13、final與static的藕斷絲連
到這里文章本該結(jié)束了的,但是static的使用始終離不開final字眼,二者可謂藕斷絲連,常常繁見,我覺得還是很有必要講講,那么一起來看看下面這個(gè)程序吧。
package Demo;class FinalDemo {public final double i = Math.random();public static double t = Math.random(); }public class DemoDemo {public static void main(String[] args) {FinalDemo demo1 = new FinalDemo();FinalDemo demo2 = new FinalDemo();System.out.println("final修飾的 i=" + demo1.i);System.out.println("static修飾的 t=" + demo1.t);System.out.println("final修飾的 i=" + demo2.i);System.out.println("static修飾的 t=" + demo2.t);System.out.println("t+1= "+ ++demo2.t ); // System.out.println( ++demo2.i );//編譯失敗} } 運(yùn)行結(jié)果:final修飾的 i=0.7282093281367935static修飾的 t=0.30720545678577604final修飾的 i=0.8106990945706758static修飾的 t=0.30720545678577604t+1= 1.307205456785776static修飾的變量沒有發(fā)生變化是因?yàn)閟tatic作用于成員變量只是用來表示保存一份副本,其不會(huì)發(fā)生變化。怎么理解這個(gè)副本呢?其實(shí)static修飾的在類加載的時(shí)候就加載完成了(初始化),而且只會(huì)加載一次也就是說初始化一次,所以不會(huì)發(fā)生變化!
至于final修飾的反而發(fā)生變化了?是不是巔覆你對final的看法?關(guān)于final詳細(xì)講解博主也準(zhǔn)備好了一篇文章程序員你真的理解final關(guān)鍵字嗎?
ok,文章就先到這里了,希望這篇文章能夠幫助到你對static的認(rèn)識(shí),若有不足或者不正之處,希望諒解并歡迎批評(píng)指正!
參考:
《java編程思想》
http://baijiahao.baidu.com/s?id=1601254463089390982&wfr=spider&for=pc
https://blog.csdn.net/qq_34337272/article/details/82766943
https://www.cnblogs.com/dolphin0520/p/3799052.html
總結(jié)
以上是生活随笔為你收集整理的深入理解static关键字的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试让HR都能听懂的MySQL锁机制,欢
- 下一篇: mxnet基础到提高(49)-使用免费的