Java中需要知道的關(guān)鍵字

Java中有一些或常用蚯瞧,或不常用锅睛,但卻不得不知關(guān)鍵字煌茴,本篇文章將討論這些關(guān)鍵字的作用忽舟。

transient

transient關(guān)鍵字可能用的不是那么頻繁双妨,但卻是一個(gè)很重要的關(guān)鍵字,它的作用是在對(duì)象序列化過程中體現(xiàn)的叮阅。如果一個(gè)類的變量被transient修飾刁品,那么這個(gè)對(duì)象在序列化過程中,不會(huì)序列化這個(gè)變量浩姥,同時(shí)哑诊,在反序列化過程中,也不會(huì)去反序列這個(gè)變量及刻。

筆者有時(shí)候會(huì)遇到這樣一種情況镀裤,可能是因?yàn)楣P者經(jīng)驗(yàn)不足竞阐。在使用JPA進(jìn)行數(shù)據(jù)庫操作的時(shí)候,某些對(duì)象在序列化到數(shù)據(jù)庫的時(shí)候暑劝,會(huì)有一些變量并不想要去存到數(shù)據(jù)庫中骆莹,這個(gè)時(shí)候就可以用transient來修飾變量,解決這個(gè)問題担猛。

instanceof

這個(gè)關(guān)鍵字就比較常用了幕垦,尤其是在一些大量采用反射的框架中,在需要判斷某一個(gè)對(duì)象是否是某一類型(可以是接口傅联,父類先改,父類的父類等)的時(shí)候,可以采用這個(gè)關(guān)鍵字蒸走。比如說仇奶,判斷UserServiceImpl是否是UserService接口的類型,可以這樣做:

if(userServiceImpl instanceof UserService){
    //do some thing
}

筆者在自己的IOC框架中就用到了這個(gè)關(guān)鍵字比驻,在將進(jìn)行接口依賴注入的時(shí)候该溯,使用該關(guān)鍵字判斷容器中是否有相應(yīng)接口的實(shí)例,然后將實(shí)例注入别惦。

final

final關(guān)鍵字可以用來修飾變量狈茉、類、方法掸掸。

final修飾的變量氯庆,一旦在被賦值之后,將不能再次賦值扰付,也就是說這個(gè)變量在后續(xù)的使用過程中堤撵,只能采取讀的方式,變量具有不可變性悯周。這里需要做一下區(qū)別粒督,就是基本數(shù)據(jù)類型和引用數(shù)據(jù)類型在被final修飾后的情況∏菀恚基本數(shù)據(jù)類型的不可變性往往體現(xiàn)在變量的值永遠(yuǎn)不會(huì)再變化屠橄,而引用類型則不是,引用類型的不變性是體現(xiàn)在引用的不變性闰挡。引用類型的變量一旦為一個(gè)引用數(shù)據(jù)類型賦值锐墙,那么變量就會(huì)指向?qū)ο笤趦?nèi)存中的地址,final關(guān)鍵字修飾過后长酗,變量所指向的地址就不能再被改變溪北,但對(duì)象本身的狀態(tài)還是可以改變的,可以看一下下面這段代碼:

final int i = 0;

i = 1; //error

final int[] j = new int[10];

j = new int[20]; //error

j[0] = 1; //right

final修飾過的變量有一個(gè)好處,即它會(huì)是線程安全的之拨。在Java中茉继,final變量會(huì)進(jìn)行指令重排序,確保所有線程在訪問該變量的時(shí)候蚀乔,變量已經(jīng)被初始化過了烁竭。雖然在舊的版本中,會(huì)出現(xiàn)對(duì)象引用在構(gòu)造函數(shù)中“逸出”的情況吉挣,但自從jsr133增強(qiáng)了final的內(nèi)存語義之后派撕,所有線程在看到final變量時(shí),看到的都是已經(jīng)初始化之后的值睬魂。final變量初始化之后又不會(huì)再改變终吼,所以它是線程安全的。

final修飾的類氯哮,將具有不可繼承性际跪,即不能有子類。

final修飾的方法蛙粘,將不可被重寫垫卤,但可以重載威彰。

static

static關(guān)鍵字可以用來修飾變量出牧,方法。static修飾的變量和方法歇盼,將只屬于類舔痕,可以通過類名.變量名(或方法名)的方式來引用,使用方式如下:

public class Demo{
    public static int i = 0;
    public static void hello(){
        System.out.println(i);
    }
}
//可以這么訪問
int j = Demo.i;
Demo.hello();

其中static變量將會(huì)常駐內(nèi)存中豹缀,不會(huì)在垃圾回收的過程中被回收掉伯复,甚至?xí)蔀槔厥罩械腉C Root。static變量的初始化只能在static代碼塊中執(zhí)行邢笙,它先于構(gòu)造函數(shù)執(zhí)行啸如,使用方式如下:

public class Demo{
    public static int i;
    static{
        i=0;
    }
}

volatile

這個(gè)關(guān)鍵字是一個(gè)很重要的關(guān)鍵字,可以修飾變量氮惯。如果要進(jìn)行多線程編程叮雳,那么這個(gè)關(guān)鍵字將會(huì)是一個(gè)重點(diǎn)。因?yàn)樗袃蓚€(gè)特性:可見性和原子性妇汗。

可見性是指帘不,對(duì)一個(gè)volatile變量的讀,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入杨箭。volatile變量再被修改的時(shí)候寞焙,如果有其他線程讀取了該變量,它會(huì)通知其他線程變量已經(jīng)實(shí)現(xiàn),重新讀取最新的值捣郊。

原子性是指辽狈,對(duì)任意單個(gè)volatile變量的讀/寫具有原子性,但類似于volatile++這種符合操作不具有原子性呛牲。其原理是因?yàn)関olatile具有一定程度上的鎖的語義稻艰,但并沒有像synchronized那么重量級(jí)。

它的使用方式如下侈净,以最常見的單例模式為示例尊勿。一般來說,我們比較喜歡使用double-check的方式實(shí)現(xiàn)單例模式畜侦,因?yàn)樗瓤梢宰鲅舆t初始化又可以保證線程的安全元扔,代碼如下:

public class Demo{
    private static Demo instance;
    private Demo(){
        
    }
    public static Demo getInstance(){
        if(instance == null){ //第一步,判斷instance是否為null
            synchronized(Demo.class){ //第二步旋膳,加鎖
                if(instance == null){ //第三步澎语,再判斷instance是否為null
                    instance = new Demo(); //第四步,實(shí)例化
                }
            }
        }
        return instance;
    }
}

這段代碼看似線程安全验懊,但卻存在一個(gè)很大的缺陷擅羞。如果有兩個(gè)線程,兩個(gè)線程都執(zhí)行到了第二步义图,一個(gè)線程在拿到鎖并進(jìn)行實(shí)例化之后减俏,另一個(gè)線程繼續(xù)進(jìn)入同步代碼塊,這個(gè)線程讀到的instance可能還是null碱工,之后就會(huì)導(dǎo)致線程安全問題娃承。為什么會(huì)這樣呢?其實(shí)在Java中怕篷,對(duì)象的實(shí)例化可以分為以下三個(gè)子階段:

memory = allocate();  // 1:分配對(duì)象的內(nèi)存空間
ctorInstance(memory); // 2:初始化對(duì)象
instance = memory;  // 3:設(shè)置instance指向剛分配的內(nèi)存地址

由于Java會(huì)對(duì)指令進(jìn)行重排序历筝,可能會(huì)導(dǎo)致3和2的指令順序相反,即

memory = allocate();  // 1:分配對(duì)象的內(nèi)存空間
instance = memory;  // 3:設(shè)置instance指向剛分配的內(nèi)存地址
ctorInstance(memory); // 2:初始化對(duì)象

問題就出現(xiàn)在這里廊谓,如果線程一的還沒有執(zhí)行完初始化對(duì)象這個(gè)子階段梳猪,另一個(gè)線程將會(huì)認(rèn)為這個(gè)實(shí)例為空,將會(huì)導(dǎo)致線程安全蒸痹。要解決這個(gè)問題春弥,可以加上volatile關(guān)鍵字,代碼如下:

public class Demo{
    private volatile static Demo instance;
    private Demo(){
        
    }
    public static Demo getInstance(){
        if(instance == null){ //第一步电抚,判斷instance是否為null
            synchronized(Demo.class){ //第二步惕稻,加鎖
                if(instance == null){ //第三步,再判斷instance是否為null
                    instance = new Demo(); //第四步蝙叛,實(shí)例化
                }
            }
        }
        return instance;
    }
}

volatile會(huì)禁止指令重排序俺祠,使得這個(gè)操作變得具有原子性,這樣線程就可以讀取到變量最新的狀態(tài),保證了線程的安全蜘渣。

synchronized

synchronized可以說是一個(gè)非常常見的關(guān)鍵字了淌铐,在jdk1.6之前,它被稱為重量級(jí)鎖蔫缸,不過jdk1.6之后經(jīng)過優(yōu)化和升級(jí)腿准,已經(jīng)沒有那么夸張了。通過使用synchronized可以使代碼同步拾碌,解決多線程環(huán)境下的一些問題吐葱。一般來說,它可以使用下面三種形式的鎖:

  • 對(duì)于普通同步方法校翔,鎖是當(dāng)前實(shí)例對(duì)象弟跑。
  • 對(duì)于靜態(tài)同步方法,鎖是當(dāng)前類的Class對(duì)象防症。
  • 對(duì)于同步方法塊孟辑,鎖是synchronized括號(hào)里配置的對(duì)象。

當(dāng)一個(gè)線程試圖去訪問同步代碼塊時(shí)蔫敲,它必須先得到鎖饲嗽,退出或拋出異常時(shí)必須釋放鎖。其底層原理是通過Monitor對(duì)象來實(shí)現(xiàn)方法同步和代碼塊同步奈嘿。代碼塊同步是使用monitorenter和monitorexit指令實(shí)現(xiàn)的貌虾,方法同步是使用另一種方法實(shí)現(xiàn)的。

monitorenter方法是在編譯后插入到同步代碼塊的開始位置指么,而monitorexit是插入到方法結(jié)束處和異常處酝惧,JVM要保證每個(gè)monitorenter都有一個(gè)monitorexit與之配對(duì)榴鼎。任何對(duì)象都有一個(gè)monitor與之關(guān)聯(lián)伯诬,當(dāng)且一個(gè)monitor被持有后,它將處于鎖定狀態(tài)巫财。線程執(zhí)行到monitorenter指令時(shí)盗似,將會(huì)嘗試獲取對(duì)象所對(duì)應(yīng)的monitor所有權(quán),即嘗試獲得對(duì)象的鎖平项。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赫舒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子闽瓢,更是在濱河造成了極大的恐慌接癌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扣讼,死亡現(xiàn)場(chǎng)離奇詭異缺猛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門荔燎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耻姥,“玉大人,你說我怎么就攤上這事有咨∷龃兀” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵座享,是天一觀的道長婉商。 經(jīng)常有香客問我,道長渣叛,這世上最難降的妖魔是什么据某? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮诗箍,結(jié)果婚禮上癣籽,老公的妹妹穿的比我還像新娘。我一直安慰自己滤祖,他們只是感情好筷狼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匠童,像睡著了一般埂材。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汤求,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天俏险,我揣著相機(jī)與錄音,去河邊找鬼扬绪。 笑死竖独,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的挤牛。 我是一名探鬼主播莹痢,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼墓赴!你這毒婦竟也來了竞膳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤诫硕,失蹤者是張志新(化名)和其女友劉穎坦辟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體章办,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锉走,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年滔吠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挠日。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疮绷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚣潜,到底是詐尸還是另有隱情冬骚,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布懂算,位于F島的核電站只冻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏计技。R本人自食惡果不足惜喜德,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垮媒。 院中可真熱鬧舍悯,春花似錦、人聲如沸睡雇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽它抱。三九已至秕豫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間观蓄,已是汗流浹背混移。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侮穿,地道東北人歌径。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像撮珠,于是被迫代替她去往敵國和親沮脖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容