摘要:只針對(duì)異常的情況才使用異常下面展示兩種遍歷數(shù)組元素的實(shí)現(xiàn)方式。第一種方式在訪問(wèn)數(shù)組邊界之外的第一個(gè)數(shù)組元素時(shí),用拋出捕獲忽略異常的手段來(lái)達(dá)到終止無(wú)限循環(huán)的目的。這種做法被稱為異常轉(zhuǎn)譯。
只針對(duì)異常的情況才使用異常
下面展示兩種遍歷數(shù)組元素的實(shí)現(xiàn)方式。
try { int i = 0; while(true) range[i++].climb(); } catch(ArrayIndexOutOfBoundsException e) { }
for (Mountain m : range) m.climb();
第一種方式在訪問(wèn)數(shù)組邊界之外的第一個(gè)數(shù)組元素時(shí),用拋出、捕獲、忽略異常的手段來(lái)達(dá)到終止無(wú)限循環(huán)的目的。
第二種方式是數(shù)組循環(huán)的標(biāo)準(zhǔn)模式。
基于異常的循環(huán)模式不僅模糊了代碼的意圖,降低了性能,而且還不能保證正常工作。
異常應(yīng)該只用于異常的情況下,不應(yīng)該用于正常的控制流。
應(yīng)該優(yōu)先使用標(biāo)準(zhǔn)的、容易理解的模式,而不是那些聲稱可以提供更好性能的、弄巧成拙的方法。
設(shè)計(jì)良好的API不應(yīng)該強(qiáng)迫客戶端為了正常的控制流而使用異常。
如果類具有“狀態(tài)相關(guān)”的方法,即只有特定的不可預(yù)知的條件下才可以被調(diào)用的方法,這個(gè)類往往也應(yīng)該有個(gè)多帶帶的“狀態(tài)測(cè)試”方法,即指示是否可以調(diào)用這個(gè)狀態(tài)相關(guān)的方法。
例如,Iterator接口有一個(gè)“狀態(tài)相關(guān)”的next方法,和相應(yīng)的狀態(tài)測(cè)試方法hasNext方法。
for(Iteratori = collection.iterator(); i.hasNext(); ) { Foo foo = i.next(); }
如果Iterator缺少hasNext方法,客戶端將被迫改用下面的做法:
try { Iterator對(duì)可恢復(fù)的情況使用受檢異常,對(duì)編程錯(cuò)誤使用運(yùn)行時(shí)異常i = collection.iterator(); while(true) { Foo foo = i.next(); } } catch (NoSuchElementException e) { }
Java提供了三種可拋出結(jié)構(gòu):受檢的異常(checked exception)、運(yùn)行時(shí)異常(run-time exception)和錯(cuò)誤(error)。
如果期望調(diào)用者能夠適當(dāng)?shù)鼗謴?fù),使用受檢的異常。
兩種未受檢的可拋出結(jié)構(gòu):運(yùn)行時(shí)異常和錯(cuò)誤。
在行為上兩者是等同的:都是不需要也不應(yīng)該被捕獲的可拋出結(jié)構(gòu)。
用運(yùn)行時(shí)異常表明編程錯(cuò)誤。大多數(shù)的運(yùn)行時(shí)異常都表示前提違例(precondition violation)。前提違例是指API的客戶沒(méi)有遵守API規(guī)范建立的約定。例如,數(shù)組訪問(wèn)的約定指明了數(shù)組的下標(biāo)值必須在零和數(shù)組長(zhǎng)度減1之間。ArrayIndexOutOfBoundsException表明這個(gè)前提被違反了。
最好,所有未受檢的拋出結(jié)構(gòu)都應(yīng)該是RuntimeException的子類。
避免不必要地使用受檢的異常過(guò)分使用受檢的異常會(huì)使API使用起來(lái)非常不方便。
如果方法拋出一個(gè)或者多個(gè)受檢的異常,調(diào)用該方法的代碼就必須在一個(gè)或者多個(gè)catch塊中處理這些異常,或者聲明它拋出這些異常,并讓它們傳播出去。
如果正確地使用API并不能阻止這種異常條件的產(chǎn)生,并且一旦產(chǎn)生異常,使用API的程序員可以立即采取有用的動(dòng)作,這種負(fù)擔(dān)就被認(rèn)為是正當(dāng)?shù)摹3沁@兩個(gè)條件都成立,否則更適合于使用受檢的異常。
“把受檢的異常變成未受檢的異常”的一種方法是,把這個(gè)拋出異常的方法分成兩個(gè)方法,其中一個(gè)方法返回一個(gè)boolean,表明是否應(yīng)該拋出異常。
try { obj.action(args); } catch(TheCheckedException e) { // Handle exceptional condition ... }
重構(gòu)為:
if (obj.actionPermitted(args)) { obj.action(args); } else { // Handle exceptional condition ... }
如果對(duì)象在缺少外部同步的情況下被并發(fā)訪問(wèn),或者可被外界改變狀態(tài),這種重構(gòu)就是不恰當(dāng)?shù)摹R驗(yàn)樵赼ctionPermitted和action這兩個(gè)調(diào)用的時(shí)間間隔之中,對(duì)象的狀態(tài)有可能會(huì)發(fā)生變化。如果多帶帶的actionPermitted方法必須重復(fù)action方法的工作,出于性能的考慮,這種API重構(gòu)就不值得去做。
優(yōu)先使用標(biāo)準(zhǔn)的異常Java平臺(tái)類庫(kù)提供了一組基本的未受檢的異常,它們滿足了絕大多數(shù)API的異常拋出需要。
重用現(xiàn)有的異常有多方面的好處。其中最主要的好處是,使API更加易于學(xué)習(xí)和使用,因?yàn)樗c程序員已經(jīng)熟悉的習(xí)慣用法是一致的。第二個(gè)好處是,可讀性會(huì)更好,因?yàn)椴粫?huì)出現(xiàn)很多程序員不熟悉的異常。
常用的異常以及其使用場(chǎng)合:
IllegalArgumentException(非null的參數(shù)值不正確)
IllegalStateException(對(duì)于方法調(diào)用而言,對(duì)象狀態(tài)不合適)
NullPointerException(在禁止使用null的情況下參數(shù)值為null)
IndexOutOfBoundsException(下標(biāo)參數(shù)值越界)
ConcurrentModificationException(在禁止并發(fā)修改的情況下,檢測(cè)到對(duì)象的并發(fā)修改)
UnsupportedOperationException(對(duì)象不支持用戶請(qǐng)求的方法)
選擇重用哪個(gè)異常并不總是那么精準(zhǔn),因?yàn)樯媳碇械摹笆褂脠?chǎng)合”并不是相互排斥的。
拋出與抽象相對(duì)應(yīng)的異常如果方法拋出的異常與它所執(zhí)行的的任務(wù)沒(méi)有明顯的關(guān)系,這種情形將會(huì)使人不知所措。
為了避免這個(gè)問(wèn)題,更高層的實(shí)現(xiàn)應(yīng)該捕獲低層的異常,同時(shí)拋出可以按照高層抽象進(jìn)行解釋的異常。這種做法被稱為異常轉(zhuǎn)譯(exception translation)。
取自AbstractSequentialList類的異常轉(zhuǎn)譯例子:
/** * Returns the element at the specified position in this list. * *This implementation first gets a list iterator pointing to the * indexed element (with listIterator(index)). Then, it gets * the element using ListIterator.next and returns it. * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { try { return listIterator(index).next(); } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } }
一種特殊的異常轉(zhuǎn)譯形式稱為異常鏈(exception chaining)。如果低層的異常對(duì)于調(diào)試導(dǎo)致高層異常的問(wèn)題非常有幫助,使用異常鏈就很合適。
// Exception Chaining try { // use lower-level abstraction to do our bidding } catch(LowerLevelException cause) { throw new HigherLevelException(cause); }
// Exception with chaining-aware constructor class HigherLevelException extends Exception { HigherLevelException(Throwable cause) { super(cause); } }
異常鏈不僅讓你可以通過(guò)程序(用getCause)訪問(wèn)原因,還可以將原因的堆棧軌跡集成到更高層的異常中。
處理來(lái)自低層異常的最好做法是,在調(diào)用低層方法之前確保它們會(huì)成功執(zhí)行,從而避免它們拋出異常。有時(shí)候,在給低層傳遞參數(shù)之前,檢查更高層方法的參數(shù)的有效性,也能避免低層方法拋出異常。
如果無(wú)法避免低層異常,可以將異常記錄下來(lái)。這樣有助于調(diào)查問(wèn)題,同時(shí)又將客戶端代碼和最終用戶與問(wèn)題隔離開(kāi)。
總之,如果不能阻止或者處理來(lái)自低層的異常,一般做法是使用異常轉(zhuǎn)譯,除非低層方法碰巧可以保證它拋出的所有異常對(duì)高層也合適,才可以將異常從低層傳播到高層。異常鏈對(duì)高層和低層異常都提供了最佳的功能:允許拋出適當(dāng)?shù)母邔赢惓#瑫r(shí)又能捕獲低層的原因進(jìn)行分析。
每個(gè)方法拋出的異常都要有文檔始終要多帶帶地聲明受檢的異常,并且利用Javadoc的@throws標(biāo)記,準(zhǔn)確地記錄下拋出每個(gè)異常的條件。
使用Javadoc的@throws標(biāo)簽記錄下一個(gè)方法可能拋出的每個(gè)未受檢異常,但是不要使用throws關(guān)鍵字將未受檢的異常包含在方法的聲明中。
如果一個(gè)類中的許多方法出于同樣的原因而拋出同一個(gè)異常,在該類的文檔注釋中對(duì)這個(gè)異常建立文檔,這是可以接受的,而不是為每個(gè)方法多帶帶建立文檔。
在細(xì)節(jié)消息中包含能捕獲失敗的信息當(dāng)程序由于未被捕獲的異常而失敗的時(shí)候,系統(tǒng)會(huì)自動(dòng)地打印該異常的堆棧軌跡。在堆棧軌跡中包含該異常的字符串表示法,即它的toString方法的調(diào)用結(jié)果。異常類型的toString方法應(yīng)該盡可能多地返回有關(guān)失敗原因的信息。
為了捕獲失敗,異常的細(xì)節(jié)信息應(yīng)該包含所有“對(duì)該異常有貢獻(xiàn)”的參數(shù)和域的值。
為了確保在異常的細(xì)節(jié)信息中包含足夠的能捕獲失敗的信息,一種辦法是在異常的構(gòu)造器中引入這些信息。例如:
/** * Construct an IndexOutOfBoundsException. * * @param lowerBound the lowest legal index value. * @param upperBound the highest legal index value plus one. * @param index the actual index value. */ public IndexOutOfBoundsException(int lowerBound, int upperBound, int index) { super("Lower bound: " + lowerBound + ", Upper bound: " + upperBound + ", index: " + index); this.lowerBound = lowerBound; this.upperBound = upperBound; this.index = index; }努力使失敗保持原子性
一般而言,失敗的方法調(diào)用應(yīng)該使對(duì)象保持在被調(diào)用之前的狀態(tài)。具有這種屬性的方法被稱為具有失敗原子性(failure atomic)。
有四種途徑可以實(shí)現(xiàn)這種效果。
第一種是設(shè)計(jì)一個(gè)不可變的對(duì)象。
第二種是在執(zhí)行操作之前檢查參數(shù)的有效性。這可以使得在對(duì)象的狀態(tài)被修改之前,先拋出適當(dāng)?shù)漠惓!@纾琒tack.pop方法。
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; }
還有類似的辦法是,調(diào)整計(jì)算處理過(guò)程的順序,使得任何可能會(huì)失敗的計(jì)算部分都在對(duì)象狀態(tài)被修改之前發(fā)生。
第三種是編寫(xiě)一段恢復(fù)代碼,來(lái)攔截操作過(guò)程中發(fā)生的失敗,以及使對(duì)象回滾到操作開(kāi)始之前的狀態(tài)。這種辦法主要用于永久性的數(shù)據(jù)結(jié)構(gòu)。
第四種是在對(duì)象的一份臨時(shí)拷貝上執(zhí)行操作,當(dāng)操作完成之后再用臨時(shí)拷貝中的結(jié)果代替對(duì)象的內(nèi)容。
一般,作為方法規(guī)范的一部分,產(chǎn)生的任何異常都應(yīng)該讓對(duì)象保持在該方法調(diào)用之前的狀態(tài)。
不要忽略異常當(dāng)API的設(shè)計(jì)者聲明一個(gè)方法將拋出某個(gè)異常的時(shí)候,等于正在試圖說(shuō)明某些事情。所以,請(qǐng)不要忽略它。
有一種情形可以忽略異常,即關(guān)閉FileInputStream的時(shí)候。因?yàn)檫€沒(méi)有改變文件的狀態(tài),因此不必執(zhí)行任何恢復(fù)動(dòng)作,并且已經(jīng)從文件中讀取到所需要的信息,因此不必終止正在進(jìn)行的操作。即使在這種情況下,把異常記錄下來(lái)還是明智的做法。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/70002.html
摘要:來(lái)源前條來(lái)源一書(shū)英文版已經(jīng)出版,這本書(shū)的第二版想必很多人都讀過(guò),號(hào)稱四大名著之一,不過(guò)第二版年出版,到現(xiàn)在已經(jīng)將近年的時(shí)間,但隨著,,,甚至的發(fā)布,語(yǔ)言發(fā)生了深刻的變化。譯者在這里第一時(shí)間翻譯成中文版。供大家學(xué)習(xí)分享之用。 來(lái)源:sjsdfg/effective-java-3rd-chinese前 51 條來(lái)源:Effective Java, Third Edition 《Effec...
摘要:這本書(shū)是我第一次買的,從買來(lái)至今整本書(shū)還沒(méi)有看完,只看了一半,原因是個(gè)人比較懶,而且玩的心比較大,經(jīng)過(guò)這么多年的沉淀,終于可以偷點(diǎn)時(shí)間寫(xiě)下對(duì)于這本書(shū)的觀后感了整本書(shū)給我的感覺(jué)不像是一個(gè)技術(shù)書(shū),更多的是講解一些實(shí)用技巧,而對(duì)于我這個(gè)職場(chǎng)菜鳥(niǎo)來(lái) effective Java 這本書(shū)是我第一次買的, 從買來(lái)至今整本書(shū)還沒(méi)有看完, 只看了一半, 原因是個(gè)人比較懶,而且玩的心比較大,經(jīng)過(guò)這么多年...
摘要:本章中的大部分內(nèi)容適用于構(gòu)造函數(shù)和方法。第項(xiàng)其他方法優(yōu)先于序列化第項(xiàng)謹(jǐn)慎地實(shí)現(xiàn)接口第項(xiàng)考慮使用自定義的序列化形式第項(xiàng)保護(hù)性地編寫(xiě)方法第項(xiàng)對(duì)于實(shí)例控制,枚舉類型優(yōu)先于第項(xiàng)考慮用序列化代理代替序列化實(shí)例附錄與第版中項(xiàng)目的對(duì)應(yīng)關(guān)系參考文獻(xiàn) effective-java-third-edition 介紹 Effective Java 第三版全文翻譯,純屬個(gè)人業(yè)余翻譯,不合理的地方,望指正,感激...
摘要:推薦序前言致謝第一章引言第二章創(chuàng)建和銷毀對(duì)象第項(xiàng)用靜態(tài)工廠方法代替構(gòu)造器第項(xiàng)遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮使用構(gòu)建器第項(xiàng)用私有構(gòu)造器或者枚舉類型強(qiáng)化屬性第項(xiàng)通過(guò)私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力第項(xiàng)優(yōu)先考慮依賴注入來(lái)引用資源第項(xiàng)避免創(chuàng)建不必要的對(duì)象 推薦序 前言 致謝 第一章 引言 第二章 創(chuàng)建和銷毀對(duì)象 第1項(xiàng):用靜態(tài)工廠方法代替構(gòu)造器 第2項(xiàng):遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮使用構(gòu)建器 第...
摘要:如果采用抽象類,則屬性組合可能導(dǎo)致子類的組合爆炸。內(nèi)部類的設(shè)計(jì)靜態(tài)成員類用修飾的內(nèi)部類,可以不依賴于外部實(shí)例進(jìn)行創(chuàng)建。如下所示其構(gòu)造函數(shù)默認(rèn)是,并且無(wú)法修改。 對(duì)所有對(duì)象都通用的方法 equals和hashCode方法的關(guān)系 重寫(xiě)equals方法必須也要重寫(xiě)hashCode方法。 equals用的屬性沒(méi)變,則多次調(diào)用hashCode返回值也必須保持不變。 equals比較相等的對(duì)象,...
閱讀 3271·2021-11-23 10:09
閱讀 2070·2021-10-26 09:51
閱讀 984·2021-10-09 09:44
閱讀 3913·2021-10-08 10:04
閱讀 2752·2021-09-22 15:14
閱讀 3632·2021-09-22 15:02
閱讀 1071·2021-08-24 10:03
閱讀 1733·2019-12-27 12:14