亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

java解惑你知多少(八)

系統(tǒng) 2185 0

56.?惰性初始化

Java代碼?? 收藏代碼
  1. public ? class ?Lazy?{??
  2. ? private ? static ? boolean ?initial?=? false ;??
  3. ? static ?{??
  4. ??Thread?t?=? new ?Thread( new ?Runnable()?{??
  5. ??? public ? void ?run()?{??
  6. ????System.out.println( "befor..." ); //此句會(huì)輸出 ??
  7. ???? /* ?
  8. ?????*?由于使用Lazy.initial靜態(tài)成員,又因?yàn)長azy還未?初 ?
  9. ?????*?始化完成,所以該線程會(huì)在這里等待主線程初始化完成 ?
  10. ?????*/ ??
  11. ????initial?=? true ;??
  12. ????System.out.println( "after..." ); //此句不會(huì)輸出 ??
  13. ???}??
  14. ??});??
  15. ??t.start();??
  16. ?? try ?{??
  17. ???t.join(); //?主線程等待t線程結(jié)束 ??
  18. ??}? catch ?(InterruptedException?e)?{??
  19. ???e.printStackTrace();??
  20. ??}??
  21. ?}??
  22. ??
  23. ? public ? static ? void ?main(String[]?args)?{??
  24. ??System.out.println(initial);??
  25. ?}??
  26. }??

看看上面變態(tài)的程序,一個(gè)靜態(tài)變量的初始化由靜態(tài)塊里的線程來初始化,最后的結(jié)果怎樣?

?

當(dāng)一個(gè)線程訪問一個(gè)類的某個(gè)成員的時(shí)候,它會(huì)去檢查這個(gè)類是否已經(jīng)被初始化,在這一過程中會(huì)有以下四種情況:
1、?這個(gè)類尚未被初始化
2、?這個(gè)類正在被當(dāng)前線程初始化:這是對(duì)初始化的遞歸請(qǐng)求,會(huì)直接忽略掉(另,請(qǐng)參考《構(gòu)造器中靜態(tài)常量的引用問題》一節(jié))
3、?這個(gè)類正在被其他線程而不是當(dāng)前線程初始化:需等待其他線程初始化完成再使用類的Class對(duì)象,而不會(huì)兩個(gè)線程都會(huì)去初始化一遍(如果這樣,那不類會(huì)初始化兩遍,這顯示不合理)
4、?這個(gè)類已經(jīng)被初始化


當(dāng)主線程調(diào)用Lazy.main,它會(huì)檢查Lazy類是否已經(jīng)被初始化。此時(shí)它并沒有被初始化(情況1),所以主線程會(huì)記錄下當(dāng)前正在進(jìn)行的初始化,并開始對(duì)這個(gè)類進(jìn)行初始化。這個(gè)過程是:主線程會(huì)將initial的值設(shè)為false,然后在靜態(tài)塊中創(chuàng)建并啟動(dòng)一個(gè)初始化initial的線程t,該線程的run方法會(huì)將initial設(shè)為true,然后主線程會(huì)等待t線程執(zhí)行完畢,此時(shí),問題就來了。


由于t線程將Lazy.initial設(shè)為true之前,它也會(huì)去檢查Lazy類是否已經(jīng)被初始化。這時(shí),這個(gè)類正在被另外一個(gè)線程(mian線程)進(jìn)行初始化(情況3)。在這種情況下,當(dāng)前線程,也就是t線程,會(huì)等待Class對(duì)象直到初始化完成,可惜的是,那個(gè)正在進(jìn)行初始化工作的main線程,也正在等待t線程的運(yùn)行結(jié)束。因?yàn)檫@兩個(gè)線程現(xiàn)在正相互等待,形成了死鎖。

?

修正這個(gè)程序的方法就是讓主線程在等待線程前就完成初始化操作:

Java代碼?? 收藏代碼
  1. public ? class ?Lazy?{??
  2. ? private ? static ? boolean ?initial?=? false ;??
  3. ? static ?Thread?t?=? new ?Thread( new ?Runnable()?{??
  4. ?? public ? void ?run()?{??
  5. ???initial?=? true ;??
  6. ??}??
  7. ?});??
  8. ? static ?{??
  9. ??t.start();??
  10. ?}??
  11. ??
  12. ? public ? static ? void ?main(String[]?args)?{??
  13. ?? //?讓Lazy類初始化完成后再調(diào)用join方法 ??
  14. ?? try ?{??
  15. ???t.join(); //?主線程等待t線程結(jié)束 ??
  16. ??}? catch ?(InterruptedException?e)?{??
  17. ???e.printStackTrace();??
  18. ??}??
  19. ??System.out.println(initial);??
  20. ?}??
  21. }??

雖然修正了該程序掛起問題,但如果還有另一線程要訪問Lazy的initial時(shí),則還是很有可能不等initial最后賦值就被使用了。

?

總之,在類的初始化期間等待某個(gè)線程很可能會(huì)造成死鎖,要讓類初始化的動(dòng)作序列盡可能地簡單。


57.?繼承內(nèi)部類

一般地,要想實(shí)例化一個(gè)內(nèi)部類,如類Inner1,需要提供一個(gè)外圍類的實(shí)例給構(gòu)造器。一般情況下,它是隱式地傳遞給內(nèi)部類的構(gòu)造器,但是它也是可以以 expression.super(args) 的方式即通過調(diào)用超類的構(gòu)造器顯式的傳遞。

Java代碼?? 收藏代碼
  1. public ? class ?Outer?{??
  2. ? class ?Inner1? extends ?Outer{??
  3. ??Inner1(){??
  4. ??? super ();??
  5. ??}??
  6. ?}??
  7. ? class ?Inner2? extends ?Inner1{??
  8. ??Inner2(){??
  9. ???Outer. this . super ();??
  10. ??}??
  11. ??Inner2(Outer?outer){??
  12. ???outer. super ();??
  13. ??}??
  14. ?}??
  15. }??
Java代碼?? 收藏代碼
  1. class ?WithInner?{??
  2. ? class ?Inner?{}??
  3. }??
  4. class ?InheritInner? extends ?WithInner.Inner?{??
  5. ? //?!?InheritInner()?{}?//?不能編譯 ??
  6. ? /* ?
  7. ??*?這里的super指InheritInner類的父類WithInner.Inner的默認(rèn)構(gòu)造函數(shù),而不是 ?
  8. ??*?WithInner的父類構(gòu)造函數(shù),這種特殊的語法只在繼承一個(gè)非靜態(tài)內(nèi)部類時(shí)才用到, ?
  9. ??*?表示繼承非靜態(tài)內(nèi)部類時(shí),外圍對(duì)象一定要存在,并且只能在?第一行調(diào)用,而且一 ?
  10. ??*?定要調(diào)用一下。為什么不能直接使用?super()或不直接寫出呢?最主要原因就是每個(gè) ?
  11. ??*?非靜態(tài)的內(nèi)部類都會(huì)與一個(gè)外圍類實(shí)例對(duì)應(yīng),這個(gè)外圍類實(shí)例是運(yùn)行時(shí)傳到內(nèi) ?
  12. ??*?部類里去的,所以在內(nèi)部類里可以直接使用那個(gè)對(duì)象(比如Outer.this),但這里 ?
  13. ??*?是在外部內(nèi)外?,使用時(shí)還是需要存在外圍類實(shí)例對(duì)象,所以這里就顯示的通過構(gòu)造 ?
  14. ??*?器傳遞進(jìn)來,并且在外圍對(duì)象上顯示的調(diào)用一下內(nèi)部類的構(gòu)造器,這樣就確保了在 ?
  15. ??*?繼承至一個(gè)類部類的情況下?,外圍對(duì)象一類會(huì)存在的約束。 ?
  16. ??*/ ??
  17. ?InheritInner(WithInner?wi)?{??
  18. ??wi. super ();??
  19. ?}??
  20. ??
  21. ? public ? static ? void ?main(String[]?args)?{??
  22. ??WithInner?wi?=? new ?WithInner();??
  23. ??InheritInner?ii?=? new ?InheritInner(wi);??
  24. ?}??
  25. }??

?

58.?Hash集合序列化問題

Java代碼?? 收藏代碼
  1. class ?Super? implements ?Serializable{??
  2. ? //?HashSet要放置在父類中會(huì)百分百機(jī)率出現(xiàn) ??
  3. ? //?放置到子類中就不一定會(huì)出現(xiàn)問題了 ??
  4. ? final ?Set?set?=? new ?HashSet();???
  5. }??
  6. class ?Sub? extends ?Super?{??
  7. ? private ? int ?id;??
  8. ? public ?Sub( int ?id)?{??
  9. ?? this .id?=?id;??
  10. ??set.add( this );??
  11. ?}??
  12. ? public ? int ?hashCode()?{??
  13. ?? return ?id;??
  14. ?}??
  15. ? public ? boolean ?equals(Object?o)?{??
  16. ?? return ?(o? instanceof ?Sub)?&&?(id?==?((Sub)?o).id);??
  17. ?}??
  18. }??
  19. ??
  20. public ? class ?SerialKiller?{??
  21. ? public ? static ? void ?main(String[]?args)? throws ?Exception?{??
  22. ??Sub?sb?=? new ?Sub( 888 );??
  23. ??System.out.println(sb.set.contains(sb)); //?true ??
  24. ????
  25. ??ByteArrayOutputStream?bos?=? new ?ByteArrayOutputStream();??
  26. ?? new ?ObjectOutputStream(bos).writeObject(sb);??
  27. ????
  28. ??ByteArrayInputStream?bin?=? new ?ByteArrayInputStream(bos.toByteArray());??
  29. ??sb?=?(Sub)? new ?ObjectInputStream(bin).readObject();??
  30. ????
  31. ??System.out.println(sb.set.contains(sb)); //?false ??
  32. ?}??
  33. }??

Hash一類集合都實(shí)現(xiàn)了序列化的writeObject()與readObject()方法。這里錯(cuò)誤原因是由HashSet的readObject方法引起的。在某些情況下,這個(gè)方法會(huì)間接地調(diào)用某個(gè)未初始化對(duì)象的被覆寫的方法。為了組裝正在反序列化的HashSet,HashSet.readObject調(diào)用了HashMap.put方法,而put方法會(huì)去調(diào)用鍵的hashCode方法。由于整個(gè)對(duì)象圖正在被反序列

化,并沒有什么可以保證每個(gè)鍵在它的hashCode方法被調(diào)用時(shí)已經(jīng)被完全初始化了,因?yàn)镠ashSet是在父類中定義的,而在序列化HashSet時(shí)子類還沒有開始初始化(這里應(yīng)該是序列化)子類,所以這就造成了在父類中調(diào)用還沒有初始完成(此時(shí)id為0)的被子類覆寫的hashCode方法,導(dǎo)致該對(duì)象重新放入hash表格的位置與反序列化前不一樣了。hashCode返回了錯(cuò)誤的值,相應(yīng)的鍵值對(duì)條目將會(huì)放入錯(cuò)誤的單元格中,當(dāng)id被初始化為888時(shí),一切都太遲了。

?

這個(gè)程序的說明,包含了HashMap的readObject方法的序列化系統(tǒng)總體上違背了不能從類的構(gòu)造器或偽構(gòu)造器(如序列化的readObject)中調(diào)用可覆寫方法的規(guī)則。

?

如果一個(gè)HashSet、Hashtable或HashMap被序列化,那么請(qǐng)確認(rèn)它們的內(nèi)容沒有直接或間接地引用它們自身,即正在被序列化的對(duì)象。

?

另外,在readObject或readResolve方法中,請(qǐng)避免直接或間接地在正在進(jìn)行反序列化的對(duì)象上調(diào)用任何方法,因?yàn)檎诜葱蛄谢膶?duì)象處于不穩(wěn)定狀態(tài)。


59.?迷惑的內(nèi)部類

Java代碼?? 收藏代碼
  1. public ? class ?Twisted?{??
  2. ? private ? final ?String?name;??
  3. ?Twisted(String?name)?{??
  4. ?? this .name?=?name;??
  5. ?}??
  6. ? //?私有的不能被繼承,但能被內(nèi)部類直接訪問 ??
  7. ? private ?String?name()?{??
  8. ?? return ?name;??
  9. ?}??
  10. ? private ? void ?reproduce()?{??
  11. ?? new ?Twisted( "reproduce" )?{??
  12. ??? void ?printName()?{??
  13. ???? //?name()為外部類的,因?yàn)闆]有被繼承過來 ??
  14. ????System.out.println(name()); //?main ??
  15. ???}??
  16. ??}.printName();??
  17. ?}??
  18. ??
  19. ? public ? static ? void ?main(String[]?args)?{??
  20. ?? new ?Twisted( "main" ).reproduce();??
  21. ?}??
  22. }??

在頂層的類型中,即本例中的Twisted類,所有的本地的、內(nèi)部的、嵌套的長匿名的類都可以毫無限制地訪問彼此的成員。

?

另一個(gè)原因是私有的不能被繼承。


60.?編譯期常量表達(dá)式

第一個(gè)PrintWords代表客戶端,第二個(gè)Words代表一個(gè)類庫:

Java代碼?? 收藏代碼
  1. class ?PrintWords?{??
  2. ? public ? static ? void ?main(String[]?args)?{??
  3. ??System.out //引用常量變量 ??
  4. ????.println(Words.FIRST?+? "?" ???
  5. ??????+?Words.SECOND?+? "?" ???
  6. ??????+?Words.THIRD);??
  7. ?}??
  8. }??
  9. ??
  10. class ?Words?{??
  11. ? //?常量變量 ??
  12. ? public ? static ? final ?String?FIRST?=? "the" ;??
  13. ? //?非常量變量 ??
  14. ? public ? static ? final ?String?SECOND?=? null ;??
  15. ? //?常量變量 ??
  16. ? public ? static ? final ?String?THIRD?=? "set" ;??
  17. }??

現(xiàn)在假設(shè)你像下面這樣改變了那個(gè)庫類并且重新編譯了這個(gè)類,但并不重新編譯客戶端的程序PrintWords:

Java代碼?? 收藏代碼
  1. class ?Words?{??
  2. ? public ? static ? final ?String?FIRST?=? "physics" ;??
  3. ? public ? static ? final ?String?SECOND?=? "chemistry" ;??
  4. ? public ? static ? final ?String?THIRD?=? "biology" ;??
  5. }??

此時(shí),端的程序會(huì)打印出什么呢?結(jié)果是 the chemistry set,不是the null set,也不是physics chemistry biology,為什么?原因就是 null 不是一個(gè)編譯期常量表達(dá)式,而其他兩個(gè)都是。

?

對(duì)于常量變量(如上面Words類中的FIRST、THIRD)的引用(如在PrintWords類中對(duì)Words.FIRST、Words.THIRD的引用)會(huì)在編譯期被轉(zhuǎn)換為它們所表示的常量的值(即PrintWords類中的Words.FIRST、Words.THIRD引用會(huì)替換成"the"與"set")。

?

一個(gè)常量變量(如上面Words類中的FIRST、THIRD)的定義是,一個(gè)在編譯期被常量表達(dá)式(即編譯期常量表達(dá)式)初

始化的final的原生類型或String類型的變量。

?

那什么是“編譯期常量表達(dá)式”?精確定義在[JLS 15.28]中可以找到,這樣要說的是null不是一個(gè)編譯期常量表達(dá)式。

?

由于常量變量會(huì)編譯進(jìn)客戶端,API的設(shè)計(jì)者在設(shè)計(jì)一個(gè)常量域之前應(yīng)該仔細(xì)考慮一下是否應(yīng)該定義成常量變量。

?

如果你使用了一個(gè)非常量的表達(dá)式去初始化一個(gè)域,甚至是一個(gè)final或,那么這個(gè)域就不是一個(gè)常量。下面你可以通過將一個(gè)常量表達(dá)式傳給一個(gè)方法使用得它變成一個(gè)非常量:

Java代碼?? 收藏代碼
  1. class ?Words?{??
  2. ? //?以下都成非常量變量 ??
  3. ? public ? static ? final ?String?FIRST?=?ident( "the" );??
  4. ? public ? static ? final ?String?SECOND?=?ident( null );??
  5. ? public ? static ? final ?String?THIRD?=?ident( "set" );??
  6. ? private ? static ?String?ident(String?s)?{??
  7. ?? return ?s;??
  8. ?}??
  9. }??

總之,常量變量將會(huì)被編譯進(jìn)那些引用它們的類中。一個(gè)常量變量就是任何常量表達(dá)式初始化的原生類型或字符串變量。且null不是一個(gè)常量表達(dá)式。


61.?打亂數(shù)組

Java代碼?? 收藏代碼
  1. class ?Shuffle?{??
  2. ? private ? static ?Random?rd?=? new ?Random();??
  3. ? public ? static ? void ?shuffle(Object[]?a)?{??
  4. ?? for ?( int ?i?=? 0 ;?i?<?a.length;?i++)?{??
  5. ???swap(a,?i,?rd.nextInt(a.length));??
  6. ??}??
  7. ?}??
  8. ? public ? static ? void ?swap(Object[]?a,? int ?i,? int ?j)?{??
  9. ??Object?tmp?=?a[i];??
  10. ??a[i]?=?a[j];??
  11. ??a[j]?=?tmp;??
  12. ?}??
  13. ? public ? static ? void ?main(String[]?args)?{??
  14. ??Map?map?=? new ?TreeMap();??
  15. ?? for ?( int ?i?=? 0 ;?i?<? 9 ;?i++)?{??
  16. ???map.put(i,? 0 );??
  17. ??}??
  18. ????
  19. ?? //?測試數(shù)組上的每個(gè)位置放置的元素是否等概率 ??
  20. ?? for ?( int ?i?=? 0 ;?i?<? 10000 ;?i++)?{??
  21. ???Integer[]?intArr?=? new ?Integer[]?{? 0 ,? 1 ,? 2 ,? 3 ,? 4 ,? 5 ,? 6 ,? 7 ,? 8 ?};??
  22. ???shuffle(intArr);??
  23. ??? for ?( int ?j?=? 0 ;?j?<? 9 ;?j++)?{??
  24. ????map.put(j,(Integer)map.get(j)+intArr[j]);??
  25. ???}??
  26. ??}??
  27. ??System.out.println(map);??
  28. ?? for ?( int ?i?=? 0 ;?i?<? 9 ;?i++)?{??
  29. ???map.put(i,(Integer)?map.get(i)/10000f);??
  30. ??}??
  31. ??System.out.println(map);??
  32. ?}??
  33. }??

上面的算法不是很等概率的讓某個(gè)元素打亂到其位置,程序運(yùn)行了多次,大致的結(jié)果為:
{0=36031, 1=38094, 2=39347, 3=40264, 4=41374, 5=41648, 6=41780, 7=41188, 8=40274}
{0=3.6031, 1=3.8094, 2=3.9347, 3=4.0264, 4=4.1374, 5=4.1648, 6=4.178, 7=4.1188, 8=4.0274}

?

如果某個(gè)位置上等概率出現(xiàn)這9個(gè)值的話,則平均值會(huì)趨近于4,但測試的結(jié)果表明:開始的時(shí)候比較低,然后增長超過了平均值,最后又降下來了。

?

如果改用下面算法:

Java代碼?? 收藏代碼
  1. public ? static ? void ?shuffle(Object[]?a)?{??
  2. ? for ?( int ?i?=? 0 ;?i?<?a.length;?i++)?{??
  3. ??swap(a,?i,?i?+?rd.nextInt(a.length?-?i));??
  4. ?}??
  5. }??

多次測試的結(jié)果大致如下:
{0=40207, 1=40398, 2=40179, 3=39766, 4=39735, 5=39710, 6=40074, 7=39871, 8=40060}
{0=4.0207, 1=4.0398, 2=4.0179, 3=3.9766, 4=3.9735, 5=3.971, 6=4.0074, 7=3.9871, 8=4.006}
所以修改后的算法是合理的。

?

另一種打亂集合的方式是通過Api中的Collections工具類:

Java代碼?? 收藏代碼
  1. public ? static ? void ?shuffle(Object[]?a)?{??
  2. ?Collections.shuffle(Arrays.asList(a));??
  3. }??

其實(shí)算法與上面的基本相似,當(dāng)然我們使用API中提供的會(huì)更好,會(huì)在效率上獲得最大的受益。

java解惑你知多少(八)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 欧美性色黄大片一级毛片视频 | 亚洲国产成人91精品 | 久久高清一级毛片 | 亚洲国产精品国产自在在线 | 国产成人综合久久综合 | 中文久久 | 亚洲在线高清 | 性色视频 | 激情国产视频 | 黄色网址视频在线播放 | 青草免费免费观看视频在线 | 亚洲欧美日韩中文字幕在线 | 欧美一级免费观看 | 亚洲欧美一区二区三区久久 | 国产一区二区视频在线播放 | 亚洲久久久| 操美女穴 | 亚洲国产精品一区二区久 | 射婷婷 | 色综合久久综合网 | 久草在线新免费 | 成人久久18免费网址 | 国产成人麻豆精品 | 97免费观看 | 国产高清对白国产露脸91 | 99色99| 国内精品久久久久久久999下 | 中国欧美一级毛片免费 | 国产午夜不卡在线观看视频666 | 香蕉国产在线观看免费 | 欧美日韩国产三级 | 亚洲产在线精品第一站不卡 | 久久精品成人欧美大片免费 | 九九九九热精品免费视频 | 爆操波多野结衣 | 色偷偷亚洲精品一区二区 | 亚洲欧美日韩网站 | 中文字幕一级毛片视频 | 国产级a爱做片免费观看 | 日本不卡高清免费v | 日本不卡视频免费的 |