中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

CodeReview常見代碼問題

2018-07-20    來源:importnew

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

路線圖

常見代碼問題

常見的潛在代碼問題是當前直接會導致BUG、故障或者產品功能不能正常工作的類別。

空值

空值恐怕是最容易出現(xiàn)的地方之一。 常見錯誤有: a. 值為NULL導致空指針異常; b. 參數(shù)字符串含有前導或后綴空格沒有Trim導致查詢?yōu)榭铡?導致以上結果的原因主要有: 無此記錄、有此記錄但由于SQL訪問異常而沒查到、網(wǎng)絡調用失敗、記錄中有臟數(shù)據(jù)、參數(shù)沒傳。

原則上,對于任何異常, 希望能夠打印出具體的錯誤信息,根據(jù)錯誤信息很快明白是什么原因, 而不是一個 null ,還要在代碼里去推敲為什么為空。這樣我們必須識別出程序中可能的null, 并及時檢測、捕獲和拋出異常。

對于空值,最好的防護是“防御式編程”。當獲取到對象之后, 使用之前總是判斷是否為空,并適當拋出異常、打錯誤日志或做其它處理。 有的人嫌檢測為空的 if 語句充斥在代碼里會破壞代碼的可維護性, 對此我的建議是:

  • 空值檢測一定要有, 有勝于無。
  • 在空值檢測總是存在的前提下, 可以優(yōu)化空值檢測的方法和存在形式。 比如集中于一個類 NullChecker 中管理,并與系統(tǒng)的整體錯誤處理設計保持一致。集中管理和處理一致性原則可以作為系統(tǒng)設計的一個準則。 這樣主流程中只要增加一行調用即可, 既可以天網(wǎng)恢恢疏而不漏地檢測對象為空, 也不會讓代碼顯得難看。
class NullChecker {
       public static void checkNull(Object obj, Error error) {
               if (obj == null)  { throw new BizException(error); }
       }
}
  • 在參數(shù)入口處統(tǒng)一做 trim。 如果在業(yè)務邏輯里做 trim , 就會導致有的業(yè)務邏輯做了 trim , 有的沒做, 體現(xiàn)在產品上就會有令用戶困惑的事情發(fā)生。 比如搜索和導出業(yè)務, 搜索能搜索出來, 導出卻沒有。

未捕獲潛在的異常

第二個容易出錯的地方是未捕獲潛在的異常。調用API接口、庫函數(shù)或系統(tǒng)服務等,只顧著享受便利卻不做防護,常導致因為局部失敗而影響整體的功能。最好的防護依然是“防御式編程”。 要么在當前方法捕獲異常并返回合適的空值或空對象,要么拋給高層處理。

切不可默默”吞掉錯誤和異常”。 如果這樣做了, 出問題了等著加班和耗費大量腦細胞吧!
在CodeReview的時候一定要仔細詢問:這里是否可能會拋出異常?如果拋異常會怎么處理?是否會影響整體服務和返回結果?

低性能

低性能會導致產品功能不好用、不可用,甚至導致產品失敗。

常見情況有:a. 循環(huán)地逐個調用單個接口獲取數(shù)據(jù)或訪問數(shù)據(jù)庫; b. 重復創(chuàng)建幾乎完全相同的(開銷大的)對象;c. 數(shù)據(jù)庫訪問、網(wǎng)絡調用等服務未處理超時的情況; d. 多重循環(huán)對于大數(shù)據(jù)量處理的算法性能低;e. 大量字符串拼接時使用了String而非StringBuilder.

對于 a,最好提供批量接口或批量并發(fā)獲取數(shù)據(jù); 對于 b, 將可復用對象抽離出循環(huán),一次創(chuàng)建多次使用; 對于 c,設置合理的超時時間并捕獲超時異常處理; 對于 d,使用預排序或預處理, 構造合適的數(shù)據(jù)結構, 使得算法平均性能在 O(n) 或 O(nlogn) ; 對于 e, 記。 少量字符串拼接使用String, 大量字符串拼接使用 StringBuilder, 通常不會使用到 StringBuffer.

影響范圍過大

對多個模塊依賴的公共函數(shù)的修改,容易造成影響范圍超過當前業(yè)務改動,無意識地破壞依賴于該公共函數(shù)的其他業(yè)務。要特別慎重?煽康姆绞绞牵合炔榭丛摴埠瘮(shù)的調用, 如果只有自己的業(yè)務用,可適當大膽一些; 如果有多個地方依賴,抽離一個新的函數(shù),抽離原函數(shù)里的可復用部分,然后基于可復用部分構建新的函數(shù)。修改原則遵循“開閉”原則,才能盡可能使改動影響降低到最小化。

基類及實例字段和方法也屬于公共函數(shù)的范疇。 盡量不要修改基類的東西。

單測問題

單測是保證工程質量的第一道重要防線。單測問題一般包括: a. 單測未全部通過; b. 重要業(yè)務邏輯缺乏單測; c. 缺乏異常單測; d. 代碼變更或BUG修復缺乏單測。

單測全部通過應當是提交代碼到代碼庫以及代碼Review的前提條件。代碼提交者應當保證單測全部通過。沒有捷徑可走。僅當單測全部通過才提交到代碼庫, 可以通過工具自動化實現(xiàn)。 對于 maven 管理的工程, 只需一個命令: mvn test && git push origin branch_name 。 單測應當更注重質,而非單純追求覆蓋率。

缺乏單測的重要業(yè)務邏輯就像裸露在空氣中的電線一樣,雖然能跑起來,卻是很容易“觸電”的。 方法: 增加覆蓋比較全面的單測。

缺乏異常單測也是代碼提交者常忽略的問題。 異常也是一種實際的業(yè)務場景,反映系統(tǒng)的健壯性和友好性。異常應該有相應的單元測試覆蓋。創(chuàng)建條件使之拋出異常,并判斷異常是否是指定異常;若沒有拋出異;蛘卟皇侵付ó惓#瑒t應該 AssertFailed 而不是通過。

對于代碼變更和BUG修復,如果當時由于時間緊而沒有寫,后續(xù)應當補上。對于每個代碼變更和BUG,都可以抽離出相應的代碼部分, 并有相應單測覆蓋,并注明原因。

與原有業(yè)務邏輯不兼容

改動針對當前需求是合理的,卻與原有業(yè)務邏輯不兼容,也是常見的問題。比如增加一個搜索條件, 卻不能與原有條件聯(lián)合查詢。

與原有業(yè)務不兼容, 一般出現(xiàn)在:

  1. 一對一與一對多的變化。 比如原來的關系是一個訂單對應一個物流信息, 后來變化為一個訂單可能對應多個物流信息; 原來的邏輯是一個訂單顯示多個物流信息可以更改,后來要求一個訂單只展示最近一次的物流信息可以修改。
  2. 多個業(yè)務組合。 業(yè)務 A 與業(yè)務 B 原來是分開發(fā)展的, 后來開展一種活動,將業(yè)務A與業(yè)務B進行一種組合營銷。 此時,多半會出現(xiàn)很多 if-else 語句。

業(yè)務邏輯的兼容問題一般體現(xiàn)在系統(tǒng)的復用性和可擴展機制上。良好的系統(tǒng)可復用性和可擴展性可以更容易地做到業(yè)務邏輯兼容。 主要有如下幾種級別:

  1. 自動兼容。 增加一種類型, 只是 biz_type 的值多了一種, 系統(tǒng)自動將已有功能適配給新的 biz_type;
  2. 一點改動。增加一個分支語句, 對 biz_type 的某個特性進行擴展;
  3. 一些改動。 需要見縫插針地增加一個單獨的分支判斷和邏輯處理模塊, 對整體可擴展性沒有影響, 但會造成局部的復雜化;
  4. 一部分功能改動。 只需要對其中一個功能模塊做個擴展;
  5. 多處改動。 需要對多個功能模塊做相應的改造,不過更多是新增而不是修改;
  6. 難以改動。 需要深入到功能模塊內部做艱難的修改, 并要保證原有功能不受影響。

如何應對呢?

  1. 針對關聯(lián)關系, 在項目之初, 可以詢問清楚: 將來在產品上是否有可擴展的變化? 及早預留空間, 或者確定產品上的對策; 在代碼實現(xiàn)上, 兼顧考慮一對一到一對多,或一對多到一對一的關聯(lián)變化。比如使用列表來表達單個信息, 使用索引從列表中獲取單個信息。
  2. 針對業(yè)務組合, 明確各業(yè)務的核心部分, 抽離出業(yè)務的可復用的部分,形成 API ; 考慮組合模式和裝飾器模式來進行擴展。

核心不變, 外圍定制化。

缺乏必要日志

對于重要而關鍵的實例狀態(tài)、代碼路徑及API調用,應當添加適當?shù)腎NFO日志;對于異常,應當捕獲并添加Error日志。缺乏日志并不會影響業(yè)務功能,但出現(xiàn)問題排查時,就會非常不方便,甚至錯失極寶貴的機會(不易重現(xiàn)的情況尤其如此)。此外,缺乏日志也會導致可控性差,難以做數(shù)據(jù)統(tǒng)計和分析。

錯誤碼不符合規(guī)范

錯誤碼本身不算是代碼問題,不過基于整個組織和工程的可維護性來說,可以將錯誤碼不符合規(guī)范作為一種錯誤加以避免。方法: 對錯誤碼進行可控的管理和遵循規(guī)范使用?梢允褂霉参臋n維護, 也可以開發(fā)錯誤碼管理系統(tǒng)來避免相同的錯誤碼。

參數(shù)檢測缺乏或不足

參數(shù)檢測是對業(yè)務處理的第一層重要過濾。如果參數(shù)檢測不足夠,就會導致臟數(shù)據(jù)進入服務處理,輕則導致異常,重則插入臟數(shù)據(jù)到數(shù)據(jù)庫,對后續(xù)維護都會造成很多維護成本。方法: 采用“契約式編程”,規(guī)定前置條件,并使用單測進行覆蓋。

對于復雜的業(yè)務應用, 優(yōu)雅的參數(shù)檢測處理尤為重要。 根據(jù) “集中管理和處理一致性原則”, 可以建立一個 paramchecker 包, 設計一個可復用的微框架來對應用中所有的參數(shù)進行統(tǒng)一集中化檢測。參數(shù)檢測主要包括: (1) 參數(shù)的值類型, 可以根據(jù)不同值類型做基礎的檢測; (2) 參數(shù)的業(yè)務類型, 有基礎非業(yè)務參數(shù), 基礎業(yè)務參數(shù)和具體業(yè)務參數(shù)。 不同的參數(shù)業(yè)務類型有不同的處理。 將參數(shù)值類型與參數(shù)業(yè)務類型結合起來, 結合一致性的異常捕獲處理, 就可以實現(xiàn)一個可復用的參數(shù)檢測框架。參數(shù)檢測既可以采用普通的分支語句,也可以采用注解方式。采用注解方式更可讀,不過單測編寫更具技巧。

引用錯誤

對于動態(tài)語言, 由于缺乏強大的靜態(tài)代碼檢測,修改了類引用的地方尤其要注意,很可能導致依賴的其他業(yè)務出錯; 尤其是修改重名引用時。有線上故障教訓。PHP工程中含有兩個 Format 類, 一個基礎的一個業(yè)務相關的, 被改動的類文件里開始沒有指明引用,默認采用了基礎 Format 類的實現(xiàn), 然后提交者在改動文件頭增加了對業(yè)務 Format 的引用, 導致依賴于基礎Format類的其他業(yè)務不能正常工作。避免引用錯誤的方法: 當要在文件里增加新的類引用時, 先在文件里搜索是否有重名類的引用。如果有, 就要格外小心了。

細節(jié)錯誤

比如數(shù)組越界、JSON解析出錯、函數(shù)參數(shù)傳遞出錯、API 版本不對、使用網(wǎng)上拷貝的未經(jīng)測試的代碼、不成熟的算法、傳值與傳引用、相等性比較等。

對于數(shù)組越界錯誤, 通常要對空數(shù)組、針對數(shù)組大小的邊界值+1和-1寫單測來避免; 使用網(wǎng)上拷貝的代碼,誠然可節(jié)省時間,也一定要加工一下并用單測覆蓋; 傳值和傳引用可通過單測來避免錯誤; 對象的相等性比較切忌使用等號=。

多重條件

類似 if ((!A || !B) && C || (D && E)) 的多重條件要仔細推敲。方法: 最好拆分成多個有含義變量。 isNotDelay = !A || !B ; isNormal = C ; isAllow = D && E ; cond = isNotDelay && isNormal || isAllow 。

文不符實

文不符實是一種可能導致線上故障的錯誤。比如一個 getXXX 的函數(shù),結果里面還做了 add, update 的操作。對問題排查、產品運維等都有非常大的殺傷力。因此命名一定要用實質內容相符,除非是故意搞破壞。

跨語言或跨系統(tǒng)交互

稍具規(guī)模的互聯(lián)網(wǎng)創(chuàng)業(yè)公司通常會采用多語言開發(fā),比如PHP作為前端,Java作為后臺服務。當動態(tài)類型語言與靜態(tài)類型語言交互時,會有一些問題產生。比如PHP的對象通常是一個Map, 如果是空對象就會寫成 [], 然而 [] 會被 Java 解析成列表。這樣, 如果數(shù)據(jù)庫的值是通過 PHP 寫入,那么這個值既有可能是JSON對象字符串,也可能是空數(shù)組字符串, Java 來解析就有點尷尬了。 同樣,當 Java 調用 PHP 接口時, 不規(guī)范的PHP接口既可能返回列表,也可能返回 true or false , Java 解析返回結果也會比較尷尬。 因此, 在跨語言交互的邊界處,要特別注意這些類型轉換的差異。

跨系統(tǒng)交互則主要是接口設計與約定的問題。同一個項目里不同業(yè)務團隊之間的業(yè)務接口設計與約定, 不同企業(yè)里開放接口的設計與約定, 要在最初深思熟慮,一旦開放,在后期很少有接口設計改動的空間。開放接口設計要符合小而美、正交的特性, 命名要貼切一致, 參數(shù)取值要指明約束,枚舉參數(shù)要給出列表, 結果返回要規(guī)范一致,可以采用通用的 {“code”:200, “msg”: “success”, “data”: xxx} 。跨系統(tǒng)交互也要統(tǒng)一對術語和接口的理解的一致。

可維護性問題

可維護性問題是“在當前業(yè)務變更的范圍內通常不會導致BUG、故障,卻會在日后埋下地雷,引發(fā)BUG、故障、維護成本大幅增加”的類別。

硬編碼

硬編碼主要有三種情況: a. “魔數(shù)”; b. 寫死的配置; c. 臨時加的邏輯和文案。

“魔數(shù)”與重復代碼類似,當前或許不會引發(fā)問題,時間一長,為了弄清楚其代表的含義,增加很多溝通維護成本,且分散在各處很容易導致修改的時候遺漏不一致。務必清清除。方法也比較簡單:定義含義明顯的枚舉或常量,代表這個魔數(shù)在代碼中發(fā)言。

“寫死的配置”不會影響業(yè)務功能, 不過在環(huán)境變更或系統(tǒng)調優(yōu)的時候,就顯得很不方便了。 方法: 盡量將配置抽離出來做成配置項放到配置文件里。

“臨時加的邏輯和文案”也是一種破壞系統(tǒng)可維護性的做法。方法: 抽離出來放在單獨的函數(shù)或方法里,并特別加以注釋。

重復代碼

重復代碼在當前可能不會造成 BUG,但上線后,需要維護多處的事實一致性;時間一長,后續(xù)修改的時候就特別容易遺漏或處理不一致導致 BUG;重復代碼是公認的“代碼壞味”,必當盡力清除。方法: 抽離通用的部分,定制差異。重復代碼還有一種情況出現(xiàn),即創(chuàng)造新函數(shù)時,先看看是否既有方法已經(jīng)實現(xiàn)過。

通用邏輯與定制業(yè)務邏輯耦合

這大概是每個媛猿們在開發(fā)生涯中遇到的最惡心的事情之一了。通用邏輯與具體的各種業(yè)務邏輯混雜交錯,想插根針都難。遇到這種情況,只能先祈福,然后抽離一個新的函數(shù),嚴格判斷相應條件滿足后去調用它。

如果是新創(chuàng)建邏輯,可以使用函數(shù)式編程或基于接口的編程,將通用處理流程抽離出來,而將具體業(yè)務邏輯以回調函數(shù)的形式傳入處理。

不要讓不同的業(yè)務共用相同的函數(shù),然后在函數(shù)里一堆 if-else plus switch , 而是每個業(yè)務都有各自的函數(shù), 并可復用相同的通用邏輯和流程處理; 或者各個業(yè)務可以覆寫同樣命名的函數(shù)。

復用,而非混雜。

直接在原方法里加邏輯

有業(yè)務改動時,猿媛們圖方便傾向于直接在原方法里加判斷和邏輯。這樣做是很不好的習慣。一方面,增加了原方法的長度,破壞了其可維護性;另一方面,有可能對原方法的既有邏輯造成破壞。 可靠的方式是: 新增一個函數(shù),然后在原方法中調用并說明原因。

多業(yè)務耦合

在業(yè)務邊界未仔細劃分清晰的情況下出現(xiàn),一個業(yè)務過多深入和摻雜另一個非相關業(yè)務的實現(xiàn)細節(jié)。在項目和系統(tǒng)設計之初,特別要注意先劃分業(yè)務邊界,定義好接口設計和服務依賴關系,再著手開發(fā);否則,延遲到后期做這些工作,很可能會導致重復的工作量,含糊復雜的交互、增加后期系統(tǒng)維護和問題排查的許多成本。磨刀不誤砍柴工。劃分清晰的業(yè)務、服務、接口邊界就屬于磨刀的功夫。

代碼層次不合理

代碼改動邏輯是正確的,然而代碼的放置位置不符合當前架構設計約定,導致后續(xù)維護成本增加。

代碼層次不合理可能導致重復代碼。比如獲取操作人和操作記錄,如果寫在類 XController 里, 那么類 YController 就面臨尷尬局面: 如果寫在 YController , 就會導致重復代碼; 如果跨層去調用 XController 方法,又是非常不推薦的做法。因此, 獲取操作人和操作記錄,最好寫在 Service 層, Controller 層只負責參數(shù)傳入、檢測和結果轉譯、返回。

不用多余的代碼

工程中常常會有一些不用的代碼;蛘呤且恍⿻簳r未用到的Util工具或庫函數(shù),或者是由于業(yè)務變更導致已經(jīng)廢棄不用的代碼,或者是由于一時寫出后來又重寫的代碼。盡量清除掉不用多余的代碼,對系統(tǒng)可維護性是一種很好的改善,同時也有利于CodeReview。

使用全局變量

使用全局變量并沒有“錯”,錯的是,一旦出現(xiàn)問題,排查和調試問題起來,真的會讓人“一夜之間白了頭”,耗費數(shù)個小時是輕微懲罰。此外,全局變量還能“順手牽羊”地破壞函數(shù)的通用性,導致可維護性變差。務必消除全局變量的使用。當然,全局常量是可以的。

缺乏必要的注釋

對重要和關鍵點的代碼缺乏必要的注釋,使用到的重要算法缺乏必要的引用出處,對特別的處理缺乏必要的說明。

原則上, 每個方法至少要用一個簡短的單行注釋, 適宜地描述了方法的用途、業(yè)務邏輯、作者及日期。對于特殊甚至奇葩的需求的特別實現(xiàn),要加一些注釋。 這樣后續(xù)維護時有個基礎。

更難發(fā)現(xiàn)的錯誤

更難發(fā)現(xiàn)的錯誤是指“復雜并發(fā)場景下的有一定技術難度的、需要豐富開發(fā)與設計經(jīng)驗才能看出來的錯誤”。

并發(fā)

并發(fā)的問題更難檢測、復現(xiàn)和調試。常見的問題有:a. 在可能由多線程并發(fā)訪問的對象中含有共享變量卻沒有同步保護;b. 在代碼中手動創(chuàng)建缺乏控制的線程或線程池;c. 并發(fā)訪問數(shù)據(jù)庫時沒有做任何同步措施;d. 多個線程對同一對象的互斥操作沒有同步保護。

對于 a, 在大部分Java應用中,通常由Spring框架來控制和創(chuàng)建請求和服務實例,因此,保證“Controller, Service 類中的實例變量只允許 Service, DAO 的單例,不允許業(yè)務變量實例”基本確保沒有并發(fā)不正確更新的問題;不過,包含緩存策略的對象要特別注意多線程并發(fā)訪問的問題,出于性能考量, 盡量只對共享實例部分加鎖。

對于 b, 禁止在應用中手動創(chuàng)建線程或線程池,失控的線程池很容易導致應用崩潰(有線上應用崩潰的教訓)。

對于 c, 并發(fā)訪問數(shù)據(jù)庫時,要特別注意時序和狀態(tài)同步。如果時序控制不對,會導致狀態(tài)同步和更新出錯。

對于 d, 對同一對象的互斥操作需要加分布式鎖同步。

使用線程池、并發(fā)庫、并發(fā)類、同步工具而不是線程對象、并發(fā)原語。在復雜并發(fā)場景下,還需注意多個同步對象上的鎖是否按合適的順序獲得和釋放以避免死鎖,相應的錯誤處理代碼是否合理。

事務

事務方面常出現(xiàn)的問題是:多個緊密關聯(lián)的業(yè)務操作和 SQL 語句沒有事務保證。 在資金業(yè)務操作或數(shù)據(jù)強一致性要求的業(yè)務操作中,要注意使用事務,保證數(shù)據(jù)更新的一致性和完整性。

SQL問題

SQL的正確性通常可以通過 DAO 測試來保證。 SQL問題主要是指潛在的性能問題和安全問題。

要避免SQL性能問題, 在表設計的時候就要做好索引工作。在表數(shù)據(jù)量非常大的情況下,SQL語句編寫要非常小心。查詢SQL需要添加必要索引,添加合適的查詢條件和查詢順序,加快查詢效率, 避免慢查; 盡量避免使用 Join, 子查詢;避免SQL注入。

SQL優(yōu)秀書籍推薦:?SQL語言藝術

安全問題

安全問題一向是互聯(lián)網(wǎng)產品研發(fā)中極容易被忽視、而在爆發(fā)后又極引發(fā)熱議的議題。安全和隱私是用戶的心理紅線之一。應用、數(shù)據(jù)、資金的安全性應當僅次于產品功能的準確性和使用體驗。

安全問題的CodeReview可參見檢查點清單:信息安全?。主要是如下措施: a. 嚴格檢查和屏蔽非法輸入; b. 對含敏感信息的請求加密通信; c. 業(yè)務處理后消除任何敏感私密信息的任何痕跡; d. 結果返回前在反序列化中清除敏感私密信息; e. 敏感私密信息在數(shù)據(jù)存儲設備中應當加密存儲; f. 應用有嚴格的角色、權限、操作、數(shù)據(jù)訪問分級和控制; g. 切忌暴露服務器的重要的安全性信息,防止服務器被攻擊影響正常服務運行。

設計問題

設計問題通常體現(xiàn)在: a. 是否有潛在的性能問題; b. 是否有安全問題; c. 業(yè)務變化時是否容易擴展; d. 是否有遺漏的點。

較輕微的問題

較輕微問題是指“沒有技術難度、通過良好習慣即可避免的問題”。

較輕微問題一般不會造成負面影響的BUG或故障,不過建立一些好的習慣,主動使用代碼檢測工具,消除這些較輕微錯誤,也是一種修行。

命名不貼切

命名不貼切不會影響功能實現(xiàn),卻會誤導理解或增加理解難度。

方法:先查查字典,找個通俗易懂而且比較貼近的名字?梢詤⒖ jdk 的命名、通用詞匯和行業(yè)詞匯; 作用域小的采用短命名,作用域大的采用長命名。取名字是一種重要技能,—— 多少父母為此愁灰了頭!

聲明時未初始化

聲明時未初始化通常情況下都不會是問題,因為后面會進行賦值。不過,如果賦值的過程中出現(xiàn)異常,那么可能會返回空值,從而導致空值異常。通常,變量聲明時賦予默認初始值是個好習慣。

風格與整體有不一致

工程通常求穩(wěn),一致性能更好地維護。在工程項目中,最好能夠遵循工程約定的風格,在個人項目中可以凸顯個性風格。Java編程一般要遵循《Java編程規(guī)范》,有追求的程序猿媛還會追求更高層次的,比如《Google Java 規(guī)范》等。

類型轉換錯誤

編程語言的類型系統(tǒng)是非常重要的。如何在不同類型之間可靠地互轉,尤其是在父子類型之間相互賦值,也是一個微技能。濫用類型轉換,也會導致BUG 。

Java 中容易出現(xiàn)的錯誤是:a. 字符串轉數(shù)值,字符串含有非數(shù)字部分;b. JSON字符串轉對象,某個字段含有不兼容的值類型導致解析出錯;c. 子類型轉不兼容的父類型,滋生運行時異常 ClassCastException;d. 相同特質的類型不兼容。比如 Long 與 Integer 都是數(shù)值型,卻不能互轉。

類型轉換中最容易出BUG的地方是非布爾類型取反。受C語言的影響,很多高級語言支持各種數(shù)據(jù)類型轉布爾類型,比如 PHP 字符串、數(shù)組、數(shù)字等都可以轉布爾類型,相應的就喜歡寫 if (!notBoolVar) 這種表達式, 容易隱藏看不出的BUG甚至錯誤。

否定式風格

變量含義、表達式語句傾向于使用否定式風格,可能不知不覺耗費大量腦細胞,因為每次理解的時候都要繞個彎子。 比如 isNoExpress 是否無需物流, 就有點繞。 為什么呢? 無需物流是針對快遞發(fā)貨的, 如果快遞發(fā)貨占發(fā)貨的90%, 無需物流只占10%,那么, isNoExpress = false 幾乎總為真。 涉及到判斷的時候,可能不得不寫 if (!isNoExpress) , 雙重否定足夠弄暈你。

容器遍歷的結構變更

絕大多數(shù)語言都承襲了 C 語言的 for(int i=0;i<N;i++) 循環(huán)形式。不過,現(xiàn)代編程語言通常都提供了迭代器遍歷、或 foreach 遍歷。 foreach 遍歷通常基于迭代器遍歷實現(xiàn)。 只要對容器結構不做變更,推薦使用 foreach ; 若要遍歷的同時做修改或更新,推薦迭代器模式。 遍歷容器的時候同時做刪除元素操作,要特別留意,很可能導致越界錯誤。更可靠的方式時,直接生成新的容器,如果不涉及空間效率的話。

API參數(shù)傳遞錯誤

如果API參數(shù)有多個,而且相鄰參數(shù)的類型相同,那么要特別留意是否參數(shù)順序是正確的,而不會張冠李戴。

當然,在設計API參數(shù)的時候,就可以仔細用更精準類型進行區(qū)分,并將相同類型的參數(shù)錯開。比如 calc(int accountNo, int pay, int timestamp) , 就容易傳錯,比較可靠的是 calc(int accountNo, Currency pay, Timestamp now) ,這樣是不可能將參數(shù)傳遞錯誤的。

單行調用括號過多

為了簡便,常常會寫出 wapper(calc(now, String.format(“%s\n”, new BufferedFileReader(filename, “UTF-8″).readLines() ))) 的語句 , 嗯,你得好好瞧瞧和算算右邊的括號數(shù)量是否正確了。更糟糕的時候,結合API參數(shù)傳遞錯誤,IDE 可能沒有報錯, 而你很可能沒有意識到自己的參數(shù)傳遞錯誤了。 可靠的方式是, 拆出一部分變量,并將調用之間的括號用空格隔開,顯示出層次感。

String fileContent = new BufferedFileReader(filename, "UTF-8").readLines();
wapper( calc( now,  String.format("%s\n", fileContent) ) )

修改方法簽名

對某個方法有業(yè)務改動時,程序猿媛們傾向直接修改原方法的簽名。這時,要特別注意:a. 不要修改原方法的參數(shù)順序; b. 在最后面增加可選參數(shù)。 從另一個角度來看,復雜的業(yè)務方法應當分兩層: 最外層負責調度,方法參數(shù)具有包容性,里面包含的字段比較多 ; 內層方法負責特定業(yè)務邏輯的實現(xiàn),方法參數(shù)少而精。

修改原方法簽名本身就是容易產生問題的習慣, 篡改原方法的參數(shù)順序更是大忌。 最好的方法是新建一個方法去復用原方法, 然后調用新的方法。代碼變更始終銘記“開閉”原則。

打印日志太多

打印過多的日志并不好。一方面遮掩真正需要的信息,導致排查耗費時間, 另一方面造成服務器空間浪費、影響性能。生產環(huán)境日志一般只開放 INFO及以上級別的日志; Debug 日志只在調試或排錯的時候使用,生產環(huán)境可以禁止debug日志。

多級數(shù)據(jù)結構

使用多級數(shù)據(jù)結構時,要確定父級數(shù)據(jù)一定有值,或者進行檢測。比如 $order['baole']['ump']['money'],必須確保 $order['baole'], $order['baole']['money'] 一定有值或做非空檢測。

作用域過大

由于C語言的影響,猿媛們會在開頭就定義好一些變量或要返回的對象,在很靠后的地方才使用到。不必要的過大的作用域對變量和對象的變化產生不可測的影響,并增大理解的成本?煽康姆椒ㄊ,僅當在使用時才定義,并盡快返回結果。

另一種情況是,暴露的訪問域過大,比如 public 字段。 盡可能地縮小可訪問的范圍,可以增大變更和重構的空間; 減少可變性,則可以自然地獲得并發(fā)安全性,降低CodeReview的理解成本。

比如,不可變的類和字段定義成 final , 最小化包,類,接口,方法和域的可訪問性,默認為 private , 若需要繼承,可定義為 protected , 僅當需要作為 API 服務暴露出去時,使用 public.

分支與循環(huán)

條件與循環(huán)偶爾也會導致錯誤, 不過通常錯誤可以在發(fā)布前解決掉。

對于 if-else 嵌套條件, 需要仔細檢查是否符合業(yè)務邏輯; 如果嵌套太深,是否可以使用另一種方式“解結” ; 對于 switch 語句, 大多數(shù)語言的 case 有 fall through 問題, 要注意加上 break ; 最好加上 default 的處理。

對于 for 循環(huán), 編寫合理的結束條件避免死循環(huán); 對于循環(huán)變量的控制, 避免出現(xiàn) -1或 +1 錯誤, 消除越界錯誤; for 循環(huán)也要特別注意對空值和空容器的處理,避免拋出空值異常。可以通過單測來確保 for 循環(huán)的準確性。

標簽: Google 安全 大數(shù)據(jù) 代碼 服務器 服務器空間 互聯(lián)網(wǎng) 互聯(lián)網(wǎng)產品 權限 數(shù)據(jù)庫 搜索 通信 網(wǎng)絡 信息安全

版權申明:本站文章部分自網(wǎng)絡,如有侵權,請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:細說 Java 中的字符和字符串( 二 )

下一篇:ActionView 1.5.0 發(fā)布,更好用的問題需求跟蹤工具