Java开发中的常见危险信号
在開發(fā),閱讀,復(fù)審和維護(hù)成千上萬行Java代碼的幾年中,我已經(jīng)習(xí)慣于看到Java代碼中的某些“ 危險(xiǎn)信號(hào) ”,這些信號(hào)通常(但可能并非總是)暗示著代碼問題。 我不是在談?wù)摽偸清e(cuò)誤的做法,而是在有限的情況下談?wù)撨m當(dāng)?shù)淖龇?#xff0c;但通常都表明存在錯(cuò)誤。 這些“ 危險(xiǎn)信號(hào) ”有時(shí)可能是無辜的,但經(jīng)常警告不可避免地會(huì)不可避免地出現(xiàn)“ 傷害堆 ”。 在這里,我總結(jié)了其中的一些內(nèi)容,并簡(jiǎn)要討論了它們可能還不錯(cuò)的情況,并描述了它們通常不正常的原因。
這些“紅色標(biāo)志”中的許多標(biāo)志意義重大,足以保證可從諸如FindBugs之類的代碼分析工具發(fā)出警告。 流行的Java IDE也標(biāo)記了其中許多。 但是,我已經(jīng)看到開發(fā)人員錯(cuò)過了這些工具和IDE的更多文字標(biāo)記,這是因?yàn)樗麄冴P(guān)閉了這些選項(xiàng),或者是因?yàn)樗麄兞?xí)慣了或者由于不了解與標(biāo)志相關(guān)的風(fēng)險(xiǎn)而忽略了警告。
在引用中使用==(而不是.equals)
大多數(shù)Java開發(fā)人員都學(xué)會(huì)了使用==比較基元,以及使用.equals比較引用類型。 通常,這是一個(gè)容易記住的規(guī)則,通常可以很好地為Java開發(fā)人員服務(wù)。 有時(shí)使用==比較標(biāo)準(zhǔn)Java類型引用(String,Integer,Long等) 可以很好地工作 ,但是依靠要緩存的值來使它可行并不是一個(gè)好主意。 在某些情況下,可能需要檢查身份相等性而不是內(nèi)容相等性,然后==適合比較引用。 我在這里更喜歡Groovy的方法,其中==作用類似于.equals和===并且顯然傳達(dá)了開發(fā)人員更嚴(yán)格地比較身份的愿望。 相同的參數(shù)適用于使用!=將兩個(gè)引用比較為一個(gè)紅色標(biāo)記,因?yàn)槿绻槐容^的兩個(gè)對(duì)象即使共享相同的內(nèi)容,也不共享相同的標(biāo)識(shí)(內(nèi)存地址),則始終返回true。
在枚舉中使用.equals(而不是==)
坦白說,Java開發(fā)人員對(duì)枚舉使用==還是.equals并不重要。 但是,我更喜歡將==與enums一起使用 。 此首選項(xiàng)的最重要原因是將==與枚舉一起使用可避免將枚舉與某個(gè)不相關(guān)的對(duì)象(永遠(yuǎn)不會(huì)相等)進(jìn)行比較的可能錯(cuò)誤。 Object.equals(Object)方法必須必須接受任何Object,但這意味著編譯器無法強(qiáng)制傳入的對(duì)象實(shí)際上是要比較的類型。 與動(dòng)態(tài)運(yùn)行時(shí)檢測(cè)問題相比,我通常更喜歡靜態(tài)問題的靜態(tài)編譯時(shí)間檢測(cè),并且將==與枚舉一起使用可滿足此偏好。 當(dāng)然,在比較枚舉時(shí),同樣的參數(shù)適用于!=和!.equals使用。
幻數(shù)和文字字符串
我知道它是“計(jì)算機(jī)科學(xué)101”,但是我仍然經(jīng)常看到在Java代碼中經(jīng)常使用“ 魔術(shù)數(shù)字 ”和文字字符串。 這些作為將來可維護(hù)性的“危險(xiǎn)信號(hào)”而大聲疾呼,使我對(duì)當(dāng)前應(yīng)用程序的正確性產(chǎn)生嚴(yán)重懷疑。 在單個(gè)位置將它們表示為常量(或者在適當(dāng)時(shí)更好地表示為枚舉)可以提高將來的可維護(hù)性,并且也使我對(duì)使用這些值的所有代碼都使用相同的值具有更高的信心。 此外,集中定義的常量和枚舉使使用IDE的“查找用法”功能輕松查找這些常量的所有“客戶端”。
字符串常量
當(dāng)我看到一組有限的相關(guān)String常量時(shí),我??常常認(rèn)為枚舉會(huì)更好。 對(duì)于具有高度內(nèi)聚性的一組String常量,尤其如此,它允許枚舉很好地表示這些String構(gòu)成的概念。 與String常量相比,該枚舉提供了編譯時(shí)靜態(tài)類型的安全性和潛在的性能優(yōu)勢(shì)。 在程序正確性方面,最讓我感興趣的是編譯時(shí)安全。
使用Java的“ Goto”
在這篇文章中,我?guī)缀鯖]有在使用分支到帶標(biāo)簽的代碼上包含這個(gè)項(xiàng)目,因?yàn)槭聦?shí)是,我在生產(chǎn)代碼中看到的大多數(shù)實(shí)例都是合理的 。 換句話說,在這篇文章中列出該項(xiàng)目的原因更多是因?yàn)樗锌赡芤藻e(cuò)誤的方式濫用和使用,而不是我實(shí)際看到的。 在大多數(shù)情況下,我看到Java開發(fā)人員使用Java的“ goto”是為了避免過于混亂和難以閱讀的代碼。 很少使用此事實(shí)可能部分歸因于其正確使用。 如果經(jīng)常使用,那么這些用途中的大多數(shù)可能會(huì)變味。
取決于適用范圍的同名變量的適當(dāng)引用
在我看來,該項(xiàng)目屬于絕對(duì)不合適的類別,但絕對(duì)可行,甚至某些時(shí)候某些Java開發(fā)人員有意地完成了。 最好的例子是Java開發(fā)人員在執(zhí)行方法期間將傳遞給方法的變量指向另一個(gè)引用。 指向方法參數(shù)的變量暫時(shí)指向它所分配的任何替代方法,直到方法結(jié)束為止,此時(shí)該方法超出范圍。 在這種情況下,將final關(guān)鍵字放在方法定義的參數(shù)定義之前會(huì)導(dǎo)致編譯器錯(cuò)誤,這也是我喜歡在所有方法參數(shù)之前使用final的原因之一。 對(duì)我來說,簡(jiǎn)單地在該方法的局部聲明一個(gè)新變量更容易理解,因?yàn)闊o論如何該變量?jī)H在該方法局部使用。 更重要的是,作為代碼的讀者,我無法知道開發(fā)人員是否有意讓該參數(shù)的名稱僅在本地用于不同的值,還是他們引入了一個(gè)錯(cuò)誤,認(rèn)為將參數(shù)重新分配給新的引用會(huì)實(shí)際上在主叫方進(jìn)行更改。 當(dāng)我看到這些內(nèi)容時(shí),我要么與原始開發(fā)人員合作,要么從單元測(cè)試和生產(chǎn)中搜集信息,然后使用意圖并使其更加清晰(或者如果打算更改調(diào)用方客戶的價(jià)值,則進(jìn)行修復(fù))。
equals(Object)和hashCode()方法不匹配
盡管我相信應(yīng)該為幾乎所有編寫的Java類都編寫一個(gè)toString()方法,但是我對(duì)equals(Object)和hashCode()重寫的感覺并不相同。 我認(rèn)為只有在打算將類用于需要這些方法的情況下,才應(yīng)編寫這些內(nèi)容,因?yàn)樗鼈兊拇嬖趹?yīng)暗示它們?cè)谠O(shè)計(jì)和開發(fā)中具有一定程度的額外徹底性。 特別是,equals和hashCode方法需要滿足它們的意圖并進(jìn)行廣告宣傳(在Object的API文檔中),并且必須彼此對(duì)齊。 大多數(shù)IDE和分析工具會(huì)確定何時(shí)存在一種方法而沒有另一種方法。 但是,我想確保將用于equals的相同屬性用于hashCode,并且我希望在兩種方法中以相同的順序考慮它們。
缺少Javadoc注釋
我喜歡將所有合同方法(尤其是公共方法)與Javadoc注釋一起注釋。 我還發(fā)現(xiàn)用屬性要存儲(chǔ)的內(nèi)容進(jìn)行注釋非常有用。 我已經(jīng)聽過當(dāng)代碼“自我記錄”時(shí)不使用Javadoc注釋的借口,但是我?guī)缀蹩偸强梢愿嬖V提出該要求的人一個(gè)或多個(gè)示例,以了解簡(jiǎn)單的Javadoc注釋如何能夠傳遞與傳遞給您相同的信息。比解密代碼花費(fèi)的時(shí)間更長(zhǎng)。 甚至更長(zhǎng)的方法名稱通常也不能足夠長(zhǎng)以指定給定方法的所有預(yù)期輸入條件和輸出預(yù)期。 我認(rèn)為像我一樣,許多Java開發(fā)人員都喜歡在使用JDK時(shí)閱讀Javadoc注釋,而不是閱讀JDK代碼。 為什么我們自己的內(nèi)部代碼應(yīng)該有所不同? 當(dāng)注釋不足或行為與所宣傳的不一樣時(shí),或者當(dāng)我有理由認(rèn)為注釋可能陳舊或偽劣時(shí),我會(huì)閱讀源代碼。
我還希望看到Javadoc對(duì)類屬性的注釋。 有些人喜歡用屬性信息注釋公共的get / set方法,但是我也不喜歡這種方法,因?yàn)樗俣╣et / set方法將始終可用(我不喜歡這樣的假設(shè))。
方法的Javadoc注釋中的實(shí)現(xiàn)詳細(xì)信息
盡管我覺得沒有Javadoc注釋是一個(gè)危險(xiǎn)信號(hào),但是使用錯(cuò)誤類型的Javadoc注釋也是一個(gè)危險(xiǎn)信號(hào)。 Javadoc注釋不應(yīng)解釋實(shí)現(xiàn)細(xì)節(jié),而應(yīng)側(cè)重于客戶端對(duì)方法的期望(參數(shù))和客戶端對(duì)方法的期望(返回類型和可能引發(fā)的異常)。 在真正的面向?qū)ο蟮南到y(tǒng)中,實(shí)現(xiàn)應(yīng)該可以在公共接口下進(jìn)行修改,因此將這些實(shí)現(xiàn)詳細(xì)信息放在接口文檔中似乎是不合適的。 這是一個(gè)“危險(xiǎn)信號(hào)”,因?yàn)镴avadoc中存在實(shí)現(xiàn)細(xì)節(jié)使我懷疑注釋的及時(shí)性。 換句話說,這些類型的注釋通常會(huì)隨著代碼的發(fā)展而Swift過時(shí)并且完全錯(cuò)誤。
源代碼中的注釋
盡管在接口上缺少注釋(通常在Javadoc中)是一個(gè)危險(xiǎn)信號(hào),但方法和其他源代碼體內(nèi)是否存在注釋也是一個(gè)指示潛在問題的危險(xiǎn)信號(hào)。 如果需要對(duì)代碼進(jìn)行注釋以了解其功能,則代碼可能比需要的復(fù)雜得多(“注釋是臭代碼的除臭劑”通常是正確的,也很有趣)。 就像這篇文章中的許多“紅色標(biāo)記”一樣,當(dāng)我看到代碼中的注釋內(nèi)容豐富時(shí),也有一些例外,在這些注釋中,我無法想到一種更好的方式來呈現(xiàn)代碼以消除對(duì)這些注釋的需要。 但是,我認(rèn)為代碼內(nèi)(而不是接口描述性Javadoc注釋)應(yīng)該相對(duì)較少,并且應(yīng)該關(guān)注“為什么”做某事,而不是“怎么做”。 該代碼應(yīng)該說明“如何”的細(xì)節(jié),但通常不能被編寫為隱式地解釋“為什么”(由于客戶/管理方向,設(shè)計(jì)決策,正式接口要求,正式算法要求等)。
實(shí)現(xiàn)繼承(擴(kuò)展)
我已經(jīng)看到許多情況下可以很好地使用extends (實(shí)現(xiàn)繼承),并且適合這種情況。 我已經(jīng)看到了很多情況相反的情況,實(shí)現(xiàn)繼承帶來的麻煩多于好處。 艾倫·霍魯布(Allen Holub)曾寫過“ 擴(kuò)展是邪惡的” ,《 四種 設(shè)計(jì)模式的幫派》 一書非常著重于為什么組合通常比實(shí)現(xiàn)繼承更可取的原因。 開發(fā)Java代碼的時(shí)間越長(zhǎng),更重要的是,我維護(hù)Java代碼的時(shí)間越長(zhǎng),我就越相信組合的優(yōu)點(diǎn)和實(shí)現(xiàn)繼承的弊端。 正如我所說,我已經(jīng)看到實(shí)現(xiàn)繼承可以起到很好的作用,但是通常情況下,類會(huì)“苦苦掙扎”以適應(yīng)繼承層次結(jié)構(gòu),并且子類會(huì)遇到UnsupportedOperationException或“ noop ”實(shí)現(xiàn),因?yàn)樗鼈兝^承的方法沒有實(shí)現(xiàn)。真的適用。 拋出一些在有效Java中概述的繼承問題(例如,用于繼承具體類的equals和hashCode方法實(shí)現(xiàn)),它可能會(huì)變成一團(tuán)糟。 實(shí)現(xiàn)繼承并不總是不好的,但是它經(jīng)常被不好地使用,因此是一個(gè)危險(xiǎn)信號(hào)。
死碼
閑置未使用的代碼永遠(yuǎn)不是一件好事。 人們很快就會(huì)忘記如何使用它或?yàn)槭裁词褂盟?此后不久,開發(fā)人員開始懷疑它是否由于某種原因而遺留了下來。 死代碼不僅增加了必須讀取和可能維護(hù)的代碼,而且在通過IDE完成編碼的世界中,僅基于死代碼的名稱和參數(shù)列表,就可以輕松,意外地調(diào)用死方法。 死代碼通常也是被忽略的代碼庫的癥狀。
注釋代碼
已注釋掉的代碼可能不像可執(zhí)行的死代碼那樣糟糕,因?yàn)橹辽俨荒芤馔獾卣{(diào)用它,并且更明顯的是未使用它,但是它仍然是一個(gè)危險(xiǎn)信號(hào),因?yàn)樗砻鳚撛诘拇a庫被忽視了。 就像死掉的可執(zhí)行代碼一樣,在注釋掉代碼之間經(jīng)過的時(shí)間越長(zhǎng),就越難知道為什么代碼在那里,為什么注釋掉了以及為什么不再不再刪除代碼了需要。 開發(fā)人員可能會(huì)害怕刪除它,因?yàn)樗@然很重要,因此必須先離開,但沒人記得為什么。
待辦事項(xiàng)
在代碼中添加“待辦事項(xiàng)”語句變得非常普遍,以至于現(xiàn)代Java IDE為它們提供了特殊的支持和功能。 這些功能之所以有用,是因?yàn)樗鼈兘?jīng)常將待辦事項(xiàng)標(biāo)記放在列表中以供查看,但是“待辦事項(xiàng)”注釋仍然是危險(xiǎn)標(biāo)記,并且可能帶來一些與死代碼和注釋掉代碼相同的問題。 我肯定會(huì)使用“做”注釋來進(jìn)行短期提醒,但是我認(rèn)為最好在“做注釋”中包括“有效期”和聯(lián)系信息以及可能的人員或事件(如果有)需要蒸蒸日上,以使“工作”得以完成。 擁有到期日期,聯(lián)系信息以及解決“待辦事項(xiàng)”所必需的事件或人員,可以減少在代碼中進(jìn)行“待辦事項(xiàng)”注釋的可能性,因?yàn)闆]人記得確切的用途,但沒人敢刪除它們,因?yàn)槭且患匾氖虑椤?當(dāng)我在代碼中看到“要做”的陳述時(shí),我不禁想知道代碼是否某種程度上缺乏功能。
編譯器警告和IDE /工具警告/提示/發(fā)現(xiàn)
Java紅旗的明顯示例是javac編譯器發(fā)出的警告以及IDE和其他代碼分析工具提供的發(fā)現(xiàn)和提示。 這些發(fā)現(xiàn)和警告中的每一個(gè)都可能是其自己的危險(xiǎn)信號(hào)。 實(shí)際上,javac編譯器或工具和IDE會(huì)警告或暗示我在本文中引用的許多危險(xiǎn)信號(hào)。 這些警告,提示和發(fā)現(xiàn)不僅直接對(duì)應(yīng)于許多Java代碼危險(xiǎn)信號(hào),而且它們的合計(jì)存在是一個(gè)巨大的危險(xiǎn)信號(hào),表明潛在的被忽略的代碼庫可能會(huì)在其中丟失嚴(yán)重的警告(甚至是某些定義的錯(cuò)誤)。大量的警告和提示。
編譯器警告和IDE /工具警告/提示/發(fā)現(xiàn)已關(guān)閉
我絕對(duì)不認(rèn)為FindBugs在我的Java代碼中發(fā)現(xiàn)的每個(gè)問題都必然是錯(cuò)誤或缺陷。 實(shí)際上,在某些情況下,我什至禁用了一些發(fā)現(xiàn),因?yàn)槲也煌膺@些發(fā)現(xiàn),并且它們使FindBugs輸出變得混亂。 話雖如此,對(duì)發(fā)現(xiàn)的這種選擇應(yīng)該謹(jǐn)慎進(jìn)行,以確保只忽略真正的非發(fā)現(xiàn),并且對(duì)開發(fā)團(tuán)隊(duì)重要的事情是顯而易見的。 同樣,我希望打開許多IDE警告,但確實(shí)要關(guān)閉一些警告。 我也不認(rèn)為在禁用Javac警告的情況下構(gòu)建Java應(yīng)用程序不是一個(gè)好主意。 禁用這些警告和發(fā)現(xiàn)可能會(huì)刪除警告所代表的危險(xiǎn)信號(hào),但是將其關(guān)閉的動(dòng)作可能會(huì)導(dǎo)致更大的危險(xiǎn)信號(hào),因?yàn)檫@些警告和提示指出了可以解決的許多潛在危險(xiǎn)信號(hào)。
太聰明了/ Science Fair項(xiàng)目/過度設(shè)計(jì)/過早的優(yōu)化
我最后一類危險(xiǎn)信號(hào)是一大類過于復(fù)雜的軟件,這通常是常見的開發(fā)人員功能失常行為之一的結(jié)果。 過于聰明以致于無法閱讀的代碼,或者在不需要時(shí)以靈活性或(過早的)優(yōu)化為 重點(diǎn)的代碼,但往往以可讀性為代價(jià),這通常會(huì)帶來其他問題。 對(duì)代碼進(jìn)行過度工程化的開發(fā)人員可能正在這樣做,以查看他們是否可以或嘗試一些新的東西,或者只是想改變一些東西,但是這些行為很少會(huì)不利于高質(zhì)量的軟件。 一旦軟件變得過于復(fù)雜且難以理解,就不太可能對(duì)其進(jìn)行正確維護(hù),并且更有可能對(duì)其進(jìn)行不正確的更改。
通常,在閱讀代碼并想知道為什么開發(fā)人員沒有以更明顯和直接的方式實(shí)現(xiàn)此功能時(shí),這種危險(xiǎn)信號(hào)的例子就是一個(gè)明顯的例子。 一方面,您可能會(huì)對(duì)他們知道并能夠應(yīng)用某些高級(jí)功能印象深刻,但另一方面,您知道這可能比原本應(yīng)該的復(fù)雜。 這類危險(xiǎn)信號(hào)有許多表現(xiàn)形式,但我似乎在一些地區(qū)普遍看到它們。 特別是其中一個(gè)領(lǐng)域是通過反射,Spring或其他依賴項(xiàng)注入,動(dòng)態(tài)代理,觀察者等,將過多的功能(這些功能在靜態(tài)Java代碼中能很好地發(fā)揮作用)推向更多的動(dòng)態(tài)構(gòu)造。 所有這些東西在正確應(yīng)用時(shí)都是方便且有用的,但我也看到所有這些東西都被過度使用和濫用,這使開發(fā)人員難以跟蹤代碼中發(fā)生的事情。
結(jié)論
在本文中,我研究了在Java代碼和Java開發(fā)環(huán)境中看到的一些常見的Java紅色標(biāo)記。 如果使用不當(dāng),這些事情不一定是錯(cuò)誤的或負(fù)面的,但如果使用不當(dāng),則可以視為對(duì)潛在有害做法的警告。 當(dāng)我看到這些危險(xiǎn)信號(hào)時(shí),我不會(huì)急于做出判斷,直到我有機(jī)會(huì)更深入地研究以確定在這些危險(xiǎn)信號(hào)下進(jìn)行釀造是否有困難,或者它們是否處于可以采用該策略的情況下。 通常,這些危險(xiǎn)信號(hào)是即將發(fā)生問題的早期指標(biāo)。 在某些情況下,它們是對(duì)嚴(yán)重的現(xiàn)有問題(也許以前無法解釋的問題)的解釋或指示。
翻譯自: https://www.javacodegeeks.com/2013/06/common-red-flags-in-java-development.html
總結(jié)
以上是生活随笔為你收集整理的Java开发中的常见危险信号的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软糯是什么意思 怎么理解软糯的意思
- 下一篇: 设置MongoDB副本集分为4个步骤