應(yīng)用
47.?不可變的引用類型
- 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以及包裝類型:Integer、Long、Short、Byte、Character、Boolean、Float和Double也是如此。對這些類型的操作將返回新的實(shí)例。
?
不可變類型更容易設(shè)計、實(shí)現(xiàn)與作用;它們出錯的可能性更小,并且更加安全。
?
本程序修改如下:
- BigInteger?total?=?BigInteger.ZERO;??
- total=total.add( new ?BigInteger( "1" ));??
- total=total.add( new ?BigInteger( "10" ));??
- System.out.println(total); //11 ??
?
48.?請同時重寫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 ??
- ?}??
- }??
上面的程序不會打印true,而是false,為什么?
?
hashCode約定要求相等的對象要具有相同的散列碼。
?
無論何時,只要你重寫了equals方法,你就必須同時重寫hashCode方法。
?
如果將自定的類型對象放入HashSet、HashMap、Hashtable、LinkedHashSet、LinkedHashMap這此散列集合時,一定需要重寫equals與hashCode方法,這樣在放入進(jìn)去之后還能查找出來。如果放入其他非散列類型的集合時,其實(shí)只需要
重寫equals就可以了。
?
本程序解決辦法重寫hashCode()方法:
- public ? int ?hashCode()?{??
- ? return ? 37 ?*? this .str.hashCode();??
- }??
?
49.?日期設(shè)置
- Calendar?c?=?Calendar.getInstance();??
- c.set( 2010 ,? 12 ,? 31 ); //?月是從0開始的,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));??
本程序較簡單,只需注意月是從0開始的就可以了,如果你設(shè)置月為12,則會自動轉(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是同一個對象,不管是什么樣類型的 ?
- ???*?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比較時使用==替換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類的toString的優(yōu)先級高于靜態(tài)導(dǎo)入的方法 ??
- ?? //!!?System.out.println(toString(args));//不能編譯 ??
- ??System.out.println(Arrays.toString(args));??
- ?}??
- }??
本身就屬于某個范圍的成員在該范圍內(nèi)與靜態(tài)導(dǎo)入相比具有優(yōu)先權(quán)。
52.?PrintStream對輸出結(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));??
- ?}??
- }??
上面的程序沒有輸出結(jié)果。
?
這里的問題在于System.out是帶有緩沖的。輸出的結(jié)果被寫入了System.out的緩沖區(qū),但是緩沖區(qū)從來都沒有被刷新。大多數(shù)人認(rèn)為,當(dāng)有輸出產(chǎn)生的時候System.out和System.err會自動地進(jìn)制刷新,但這并不完全正確,這兩個流都屬于PrintStream類型,請看API DOC描述:一個PrintStream被創(chuàng)建為自動刷新,這意味著當(dāng)一個字節(jié)數(shù)組(byte[])被寫入、或者某個println方法被調(diào)用、或者一個換行字符或字節(jié)('\n')被寫入之后,PrintStream類型的flush方法就會被自動調(diào)用。
?
令人奇怪的是,如果這個程序用print(char)去替代write(int),它就會刷新System.out并輸出結(jié)果,這種行為與print(char)的文檔是矛盾的,因為其文檔敘述道:“打印一個字符,這個字符將根據(jù)平臺缺省的字符編碼方式翻譯成一個或多個字節(jié),并且這些字節(jié)將完全按照write(int)方法的方式輸出。”,但這里沒有換行符卻也自動的刷新了。
?
類似的,如果程序改用print(String),它也會對流進(jìn)行刷新。所以調(diào)用print方法也是會自動刷新的。
?
53.?調(diào)用操作系統(tǒng)命令時被阻塞問題
- 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文檔描述:由于某些本地平臺只提供有限大小的緩沖,所以如果不能迅速地讀取子進(jìn)程的輸出流,就有可能會導(dǎo)致子進(jìn)程的阻塞,甚至是死鎖。這恰好就是這里所發(fā)生的事情:沒有足夠的緩沖空間來保存這些輸出結(jié)果。為了結(jié)子進(jìn)程(Process線程),父進(jìn)程(Main線程)必須排空它的輸出流(標(biāo)準(zhǔn)流與錯誤流都需要排空),即要去緩存中讀取結(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é)果來判斷操作系統(tǒng)命令執(zhí)行是否成功(成功:0,失敗:1),我們不能根據(jù)
錯誤流中是否有內(nèi)容來判斷是否執(zhí)行成功。
54.?實(shí)現(xiàn)Serializable的單例問題
- class ?Dog? implements ?Serializable{??
- ? public ? static ? final ?Dog?INSTANCE?=? new ?Dog();??
- ? private ?Dog(){}??
- }??
?上面能控制只生成一個單實(shí)例嗎?
?
如果對實(shí)現(xiàn)了Serializable的對象進(jìn)行序列化后,再反序列化,內(nèi)中會不只一個實(shí)例了,因為反序列化時會重新生成一個對象。
?
既然INSTANCE為靜態(tài)域,那序列化時返回的對象如果也是INSTANCE就可以解決問題了,而打開API我們發(fā)現(xiàn)Serializable接口確實(shí)有這樣兩個特殊的方法描述:
??將對象寫入流時需要指定要使用的替代對象的可序列化類,應(yīng)使用準(zhǔn)確的簽名來實(shí)現(xiàn)此特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
此 writeReplace 方法將由序列化調(diào)用,前提是如果此方法存在,而且它可以通過被序列化對象的類中定義的一個方法訪問。因此,該方法可以擁有私有 (private)、受保護(hù)的 (protected) 和包私有 (package-private) 訪問。子類對此方法的訪問遵循 java 訪問規(guī)則。?
??在從流中讀取類的一個實(shí)例時需要指定替代的類應(yīng)使用的準(zhǔn)確簽名來實(shí)現(xiàn)此特殊方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
此 readResolve 方法遵循與 writeReplace 相同的調(diào)用規(guī)則和訪問規(guī)則。
?
上述兩個方法的只要出現(xiàn),就會履蓋以下兩個方法(這兩個方法本質(zhì)的意義就是用來替換序列與反序列的對象),雖然會執(zhí)行它們,但最后得到的結(jié)果卻是writeReplace、readResolve兩個方法寫入或讀出的對象:
??private void writeObject(java.io.ObjectOutputStream out) throws IOException
??private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException;
?
另外,writeObject與readObject需成對實(shí)現(xiàn),而writeReplace與readResolve則不需要成對出現(xiàn),一般單獨(dú)使用。如果同時出現(xiàn)這四個方法,最后寫入與讀出的結(jié)果以writeReplace和readResolve方法的結(jié)果為準(zhǔn)。
?
所以下要解決真真單實(shí)例問題,我們?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 ??
- ?}??
- }??
一個實(shí)現(xiàn)了Serializable的單例類,必須有一個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é)果走的是第一個分支,但結(jié)果卻不是Interruped:true?
?
Thread.interrupted()為Thread的靜態(tài)方法,調(diào)用它首先會返回當(dāng)前線程的中斷狀態(tài)(如果當(dāng)前線程上調(diào)用了interrupt()方法,則返回true,否則為false),然后再清除當(dāng)前線程的中斷狀態(tài),即將中斷狀態(tài)設(shè)置為false。換句話說,如果連續(xù)兩次調(diào)用該方法,則第二次調(diào)用將返回 false。
?
而isInterrupted()方法為實(shí)例方法,測試線程是否已經(jīng)中斷,并不會清除當(dāng)前線程中斷狀態(tài)。
?
所以這里應(yīng)該使用isInterrupted()實(shí)例方法,就可以修復(fù)該問題。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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