應(yīng)用
47.?不可變的引用類(lèi)型
- BigInteger?total?=?BigInteger.ZERO;??
- total.add( new ?BigInteger( "1" ));??
- total.add( new ?BigInteger( "10" ));??
- System.out.println(total); //0 ??
上面程序的結(jié)果為11嗎?答案是0。
?
BigInteger實(shí)例是不可變的。String、BigDecimal以及包裝類(lèi)型:Integer、Long、Short、Byte、Character、Boolean、Float和Double也是如此。對(duì)這些類(lèi)型的操作將返回新的實(shí)例。
?
不可變類(lèi)型更容易設(shè)計(jì)、實(shí)現(xiàn)與作用;它們出錯(cuò)的可能性更小,并且更加安全。
?
本程序修改如下:
- BigInteger?total?=?BigInteger.ZERO;??
- total=total.add( new ?BigInteger( "1" ));??
- total=total.add( new ?BigInteger( "10" ));??
- System.out.println(total); //11 ??
?
48.?請(qǐng)同時(shí)重寫(xiě)equals()與hashCode()
- class ?T?{??
- ? private ?String?str;??
- ??
- ?T(String?str)?{??
- ?? this .str?=?str;??
- ?}??
- ??
- ? public ? boolean ?equals(Object?obj)?{??
- ?? if (!(obj? instanceof ?T)){??
- ??? return ? false ;??
- ??}??
- ??T?t?=?(T)obj;??
- ?? return ?t.equals( this .str);??
- ?}??
- ??
- ? public ? static ? void ?main(String[]?args)?{??
- ??Set?set?=? new ?HashSet();??
- ??set.add( new ?T( "str" ));??
- ??System.out.println(set.contains( new ?T( "str" ))); //false ??
- ?}??
- }??
上面的程序不會(huì)打印true,而是false,為什么?
?
hashCode約定要求相等的對(duì)象要具有相同的散列碼。
?
無(wú)論何時(shí),只要你重寫(xiě)了equals方法,你就必須同時(shí)重寫(xiě)hashCode方法。
?
如果將自定的類(lèi)型對(duì)象放入HashSet、HashMap、Hashtable、LinkedHashSet、LinkedHashMap這此散列集合時(shí),一定需要重寫(xiě)equals與hashCode方法,這樣在放入進(jìn)去之后還能查找出來(lái)。如果放入其他非散列類(lèi)型的集合時(shí),其實(shí)只需要
重寫(xiě)equals就可以了。
?
本程序解決辦法重寫(xiě)hashCode()方法:
- public ? int ?hashCode()?{??
- ? return ? 37 ?*? this .str.hashCode();??
- }??
?
49.?日期設(shè)置
- Calendar?c?=?Calendar.getInstance();??
- c.set( 2010 ,? 12 ,? 31 ); //?月是從0開(kāi)始的,11其實(shí)表示12月 ??
- System.out.println(c.get(Calendar.YEAR)?+? "?" ?+?c.get(Calendar.MONTH));??
- c?=?Calendar.getInstance();??
- c.set( 2010 ,? 11 ,? 31 );??
- System.out.println(c.get(Calendar.YEAR)?+? "?" ?+?c.get(Calendar.MONTH));??
本程序較簡(jiǎn)單,只需注意月是從0開(kāi)始的就可以了,如果你設(shè)置月為12,則會(huì)自動(dòng)轉(zhuǎn)換為下一年。
50.?IdentityHashMap
?
- class ?T?{??
- ? private ?String?str;??
- ??
- ?T(String?str)?{??
- ?? this .str?=?str;??
- ?}??
- ??
- ? public ? int ?hashCode()?{??
- ?? return ? 37 ?*? this .str.hashCode();??
- ?}??
- ??
- ? public ? boolean ?equals(Object?obj)?{??
- ?? return ? this .str.equals(((T)?obj).str);??
- ?}??
- ??
- ? public ? static ? void ?put(Map?m)?{??
- ??m.put( "str" ,? "1" );??
- ?? /* ?
- ???*?由于上面程序?qū)?"str"?放入了字符串常量池, ?
- ???*?所以str是同一個(gè)對(duì)象,不管是什么樣類(lèi)型的 ?
- ???*?Map,即使使用IdentityHashMap都只放入一次 ?
- ???*/ ??
- ??m.put( "str" ,? "2" );??
- ??m.put( new ?T( "str" ),? "3" );??
- ??m.put( new ?T( "str" ),? "4" );??
- ?}??
- ??
- ? public ? static ? void ?main(String[]?args)?{??
- ??Map?m?=? new ?HashMap();??
- ??put(m);??
- ??System.out.println(m.size()); //?2 ??
- ?? //IdentityHashMap比較時(shí)使用==替換equals()方法 ??
- ??m?=? new ?IdentityHashMap();??
- ??put(m);??
- ??System.out.println(m.size()); //?3 ??
- ?}??
- }??
?
51.?靜態(tài)導(dǎo)入的優(yōu)先權(quán)
- import ? static ?java.util.Arrays.toString;??
- import ?java.util.Arrays;??
- public ? class ?T?{??
- ? public ? static ? void ?main(String[]?args)?{??
- ??prt( 1 ,? 2 ,? 3 );??
- ?}??
- ? static ? void ?prt(Object...?args)?{??
- ?? //?自身繼承至Object類(lèi)的toString的優(yōu)先級(jí)高于靜態(tài)導(dǎo)入的方法 ??
- ?? //!!?System.out.println(toString(args));//不能編譯 ??
- ??System.out.println(Arrays.toString(args));??
- ?}??
- }??
本身就屬于某個(gè)范圍的成員在該范圍內(nèi)與靜態(tài)導(dǎo)入相比具有優(yōu)先權(quán)。
52.?PrintStream對(duì)輸出結(jié)果的緩沖
- public ? static ? void ?main(String[]?args)?{??
- ?String?str?=? "Hello?World" ;??
- ? for ?( int ?i?=? 0 ;?i?<?str.length();?i++)?{??
- ??System.out.write(str.charAt(i));??
- ?}??
- }??
上面的程序沒(méi)有輸出結(jié)果。
?
這里的問(wèn)題在于System.out是帶有緩沖的。輸出的結(jié)果被寫(xiě)入了System.out的緩沖區(qū),但是緩沖區(qū)從來(lái)都沒(méi)有被刷新。大多數(shù)人認(rèn)為,當(dāng)有輸出產(chǎn)生的時(shí)候System.out和System.err會(huì)自動(dòng)地進(jìn)制刷新,但這并不完全正確,這兩個(gè)流都屬于PrintStream類(lèi)型,請(qǐng)看API DOC描述:一個(gè)PrintStream被創(chuàng)建為自動(dòng)刷新,這意味著當(dāng)一個(gè)字節(jié)數(shù)組(byte[])被寫(xiě)入、或者某個(gè)println方法被調(diào)用、或者一個(gè)換行字符或字節(jié)('\n')被寫(xiě)入之后,PrintStream類(lèi)型的flush方法就會(huì)被自動(dòng)調(diào)用。
?
令人奇怪的是,如果這個(gè)程序用print(char)去替代write(int),它就會(huì)刷新System.out并輸出結(jié)果,這種行為與print(char)的文檔是矛盾的,因?yàn)槠湮臋n敘述道:“打印一個(gè)字符,這個(gè)字符將根據(jù)平臺(tái)缺省的字符編碼方式翻譯成一個(gè)或多個(gè)字節(jié),并且這些字節(jié)將完全按照write(int)方法的方式輸出。”,但這里沒(méi)有換行符卻也自動(dòng)的刷新了。
?
類(lèi)似的,如果程序改用print(String),它也會(huì)對(duì)流進(jìn)行刷新。所以調(diào)用print方法也是會(huì)自動(dòng)刷新的。
?
53.?調(diào)用操作系統(tǒng)命令時(shí)被阻塞問(wèn)題
- public ? static ? void ?main(String[]?args)? throws ?IOException,??
- ??InterruptedException?{??
- ?String?command?=? "java?ProcessTest?exc" ;??
- ? if ?(args.length?!=? 0 )?{??
- ?? for ?( int ?i?=? 0 ;?i?<? 200 ;?i++)?{??
- ???System.out.println(command);??
- ???System.err.println(command);??
- ??}??
- ?}? else ?{??
- ??Process?process?=?Runtime.getRuntime().exec(command);????
- ?? int ?exitValue?=?process.waitFor();??
- ??System.out.println( "exit?value?=?" ?+?exitValue);??
- ?}??
- }??
執(zhí)行java ProcessTest發(fā)現(xiàn)程序阻塞。
?
Process文檔描述:由于某些本地平臺(tái)只提供有限大小的緩沖,所以如果不能迅速地讀取子進(jìn)程的輸出流,就有可能會(huì)導(dǎo)致子進(jìn)程的阻塞,甚至是死鎖。這恰好就是這里所發(fā)生的事情:沒(méi)有足夠的緩沖空間來(lái)保存這些輸出結(jié)果。為了結(jié)子進(jìn)程(Process線(xiàn)程),父進(jìn)程(Main線(xiàn)程)必須排空它的輸出流(標(biāo)準(zhǔn)流與錯(cuò)誤流都需要排空),即要去緩存中讀取結(jié)果:
- static ? void ?readResult( final ?InputStream?is)?{??
- ? new ?Thread( new ?Runnable()?{??
- ?? public ? void ?run()?{??
- ??? try ?{??
- ???? //?排空緩存內(nèi)容 ??
- ???? while ?(is.read()?>=? 0 );??
- ???}? catch ?(IOException?e)?{??
- ????e.printStackTrace();??
- ???}??
- ??}??
- ?}).start();??
- }??
?
然后在process.waitFor()之前加上
- readResult(process.getErrorStream());??
- readResult(process.getInputStream());??
即可輸出exit value = 0。
?
另外,只能根據(jù)process.waitFor返回的結(jié)果來(lái)判斷操作系統(tǒng)命令執(zhí)行是否成功(成功:0,失敗:1),我們不能根據(jù)
錯(cuò)誤流中是否有內(nèi)容來(lái)判斷是否執(zhí)行成功。
54.?實(shí)現(xiàn)Serializable的單例問(wèn)題
- class ?Dog? implements ?Serializable{??
- ? public ? static ? final ?Dog?INSTANCE?=? new ?Dog();??
- ? private ?Dog(){}??
- }??
?上面能控制只生成一個(gè)單實(shí)例嗎?
?
如果對(duì)實(shí)現(xiàn)了Serializable的對(duì)象進(jìn)行序列化后,再反序列化,內(nèi)中會(huì)不只一個(gè)實(shí)例了,因?yàn)榉葱蛄谢瘯r(shí)會(huì)重新生成一個(gè)對(duì)象。
?
既然INSTANCE為靜態(tài)域,那序列化時(shí)返回的對(duì)象如果也是INSTANCE就可以解決問(wèn)題了,而打開(kāi)API我們發(fā)現(xiàn)Serializable接口確實(shí)有這樣兩個(gè)特殊的方法描述:
??將對(duì)象寫(xiě)入流時(shí)需要指定要使用的替代對(duì)象的可序列化類(lèi),應(yīng)使用準(zhǔn)確的簽名來(lái)實(shí)現(xiàn)此特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
此 writeReplace 方法將由序列化調(diào)用,前提是如果此方法存在,而且它可以通過(guò)被序列化對(duì)象的類(lèi)中定義的一個(gè)方法訪問(wèn)。因此,該方法可以擁有私有 (private)、受保護(hù)的 (protected) 和包私有 (package-private) 訪問(wèn)。子類(lèi)對(duì)此方法的訪問(wèn)遵循 java 訪問(wèn)規(guī)則。?
??在從流中讀取類(lèi)的一個(gè)實(shí)例時(shí)需要指定替代的類(lèi)應(yīng)使用的準(zhǔn)確簽名來(lái)實(shí)現(xiàn)此特殊方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
此 readResolve 方法遵循與 writeReplace 相同的調(diào)用規(guī)則和訪問(wèn)規(guī)則。
?
上述兩個(gè)方法的只要出現(xiàn),就會(huì)履蓋以下兩個(gè)方法(這兩個(gè)方法本質(zhì)的意義就是用來(lái)替換序列與反序列的對(duì)象),雖然會(huì)執(zhí)行它們,但最后得到的結(jié)果卻是writeReplace、readResolve兩個(gè)方法寫(xiě)入或讀出的對(duì)象:
??private void writeObject(java.io.ObjectOutputStream out) throws IOException
??private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException;
?
另外,writeObject與readObject需成對(duì)實(shí)現(xiàn),而writeReplace與readResolve則不需要成對(duì)出現(xiàn),一般單獨(dú)使用。如果同時(shí)出現(xiàn)這四個(gè)方法,最后寫(xiě)入與讀出的結(jié)果以writeReplace和readResolve方法的結(jié)果為準(zhǔn)。
?
所以下要解決真真單實(shí)例問(wèn)題,我們?nèi)缦滦拚?
- class ?Dog? implements ?Serializable?{??
- ? public ? static ? final ?Dog?INSTANCE?=? new ?Dog();??
- ? private ?Dog()?{}??
- ? private ?Object?readResolve()?{??
- ?? return ?INSTANCE;??
- ?}??
- }??
- ??
- public ? class ?SerialDog?{??
- ? public ? static ? void ?main(String[]?args)? throws ?IOException,??
- ???ClassNotFoundException?{??
- ??ByteArrayOutputStream?bos?=? new ?ByteArrayOutputStream();??
- ?? new ?ObjectOutputStream(bos).writeObject(Dog.INSTANCE);??
- ??ByteArrayInputStream?bin?=? new ?ByteArrayInputStream(bos.toByteArray());??
- ??Dog?dog?=?(Dog)? new ?ObjectInputStream(bin).readObject();??
- ??System.out.println(dog?==?Dog.INSTANCE); //true ??
- ?}??
- }??
一個(gè)實(shí)現(xiàn)了Serializable的單例類(lèi),必須有一個(gè)readResolve方法,用以返回它的唯一實(shí)例。
55.?thread. isInterrupted()與Thread.interrupted()
- public ? class ?SelfInerruption?{??
- ? public ? static ? void ?main(String[]?args)?{??
- ??Thread.currentThread().interrupt();??
- ?? if ?(Thread.interrupted())?{??
- ??? //?Interruped:false ??
- ???System.out.println( "Interruped:" ?+?Thread.interrupted());??
- ??}? else ?{??
- ???System.out.println( "Not?interruped:" ?+?Thread.interrupted());??
- ??}??
- ?}??
- }??
上面結(jié)果走的是第一個(gè)分支,但結(jié)果卻不是Interruped:true?
?
Thread.interrupted()為T(mén)hread的靜態(tài)方法,調(diào)用它首先會(huì)返回當(dāng)前線(xiàn)程的中斷狀態(tài)(如果當(dāng)前線(xiàn)程上調(diào)用了interrupt()方法,則返回true,否則為false),然后再清除當(dāng)前線(xiàn)程的中斷狀態(tài),即將中斷狀態(tài)設(shè)置為false。換句話(huà)說(shuō),如果連續(xù)兩次調(diào)用該方法,則第二次調(diào)用將返回 false。
?
而isInterrupted()方法為實(shí)例方法,測(cè)試線(xiàn)程是否已經(jīng)中斷,并不會(huì)清除當(dāng)前線(xiàn)程中斷狀態(tài)。
?
所以這里應(yīng)該使用isInterrupted()實(shí)例方法,就可以修復(fù)該問(wèn)題。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

