文章已經收錄在 Github.com/niumoo/JavaNotes ,更有 Java 程式設計師所需要掌握的核心知識,歡迎Star和指教。
歡迎關注我的公眾號,文章每週更新。
很多 Java 初學者在開始程式設計時會出現一些問題,這些問題並不是指某個特定領域的問題,也不是指對某個業務不熟悉而導致的問題,而是對基礎知識不夠熟悉導致的問題。而就是這些問題讓我們編寫了一些不夠健壯的程式碼。
這篇文章會列舉幾種程式設計初學者常常出現的一些問題,我相信這些問題多多少少也曾困擾著現在或曾經的你。如果覺得文章不錯,不妨點贊分享,讓更多人跳過這些開發中的坑。
隨處可見的 Null 值
我見過很多的程式碼會把 Null 值作為返回值,當你預期是一個字串時,意外得到了一個 Null 值;當你預期得到一個 List 時,意外又得到了一個 Null 值,如果你不進行處理,那麼你還會意外得到 NullPointerException
.
就像下面這樣。
// 情況1
String userTag = getUserTag();
if (userTag.equals("admin")) { // NullPointerException
// ...
}
// 情況2
List<String> carList = getCarList();
for (String car : carList) { // NullPointerException
// ...
}
為了防止這種情況,你可以在 List 返回時給出一個空的集合而不是 Null,如果是字串,你可以把要確定有值物件放在比較的前面。
if ("admin".equals(userTag)) {
// ...
}
// 或者
if (Objects.equals(userTag,"admin")){
// ...
}
沒有進行空值檢查
可能你考慮到了上面的 Null 值情況,但是在實際處理時沒有考慮空值情況,比如字元空串空串 "",或者集合為空。那麼在後續處理時又有可能得到一個 NullPointerException
. 所以你應該進行空值判斷。
String userTag = getUserTag();
if (userTag != null && userTag.trim() != "") {
// ...
}
List<String> carList = getCarList();
if (carList != null && !carList.isEmpty()) {
// ...
}
忽略的異常處理
異常處理總是一件煩人的事,而忽略異常似乎總有一種吸引人的魔力。我見過像下面這樣的程式碼。
try {
List<String> result= request();
// ...
}catch (Exception e){
}
你沒有看錯,catch 中沒有任何內容,後來出現了問題,看著日誌檔案一片太平無跡可尋。異常是故意丟擲來的,你應該正確處理它們或者繼續丟擲。而且同時,你該輸出一行日誌用來記錄這個異常,方便以後的問題追蹤。
沒有釋放資源
在讀取檔案或者請求網路資源時,總是需要進行 close 操作,這很重要,否則可能會阻塞其他執行緒的使用。但是初學者可能會忘記這一步操作。其實在 Java 7 開始,就提供了 try-with-resources
自動關閉資源的特性,只需要把開啟的資源放入 try
中。
try (FileReader fileReader = new FileReader("setting.xml")) {
// fileReader.read();
// ...
} catch (Exception e) {
e.printStackTrace();
}
像上面這樣,不需要在 finally
裡手動呼叫 fileReader
的 close
方法關閉資源,因為放在 try
裡的資源呼叫會在使用完畢時自動呼叫 close
. 而且不管是否有異常丟擲,這很實用。
ConcuretModificationException
總有一天你會遇到 ConcuretModificationException
,然後開始百度搜尋它的解決方式,這個異常最常見的場景是你在遍歷一個集合時進行更新操作,比如像下面這樣。
List<String> list = new ArrayList<>();
list.add("a1");
list.add("b1");
list.add("b2");
list.add("c1");
for (String s : list) {
if ("b1".equals(s)) {
list.remove(s);
}
}
這個異常很有用處,因為 ArrayList 不是執行緒安全的集合,假設你這邊一邊遍歷,另一個執行緒不斷更新,非執行緒安全集合會導致你的遍歷結果不正確,所以這個異常的存在是合理的。同理 HashMap 也是如此,關於 HashMap 之前已經有一篇文章詳細介紹了,可以參考 最通俗易懂的 HashMap 原始碼分析解讀。
缺少註釋
準確的註釋可以救人於水火,這點有時候一點也不誇張。雖然說優秀的程式碼本身就是非常好的註釋,但是這實際開發起來,很少發生。註釋並不需要你事無鉅細的一一記錄,但是你該在核心邏輯新增應有的註釋,比如複雜邏輯的實現思路,當前邏輯業務需求。某個判斷的新增原因,某個異常的發生情況等等。這可以讓你在未來的某一天需要回看現在的程式碼時感謝自己。更可以讓你在某天的甩鍋中輕鬆勝出。
不進行程式碼測試
我見過有些同事在功能開發完畢後直接扔給對接同事使用,而自己卻沒有經過任何測試,或者只是測試了某個簡單的情況。測試是開發過程中的重要環節,沒有經過嚴格測試的程式碼很難說沒有問題,我覺得在功能開發完畢後至少需要單元測試,特殊用例測試,整合測試以及其他形式的測試。嚴格的測試不僅可以第一時間發現問題,更可以減少後面不必要的對接除錯時間。
重複造輪子
你知道的,Java 社群非常活躍,存在著大量的第三方類庫,開源作者可能花費了數年時間去維護和完善類庫,這些類庫非常優秀。同時 JDK 也提供了大量的常用的功能封裝。這些都可以為我們的開發速度插上翅膀。所以,當你需要一個功能時候,應該首先看下 JDK 和已經引入的類庫中是否已經存在相同功能,而不是自己重複造輪子,而且大部分情況下你造的輪子還不如別人好。
下面舉些例子。
- 你需要日誌記錄,可以使用 logback.
- 你需要網路操作,可以使用 netty.
- 你需要解析 JSON,可以使用 gson.
- 你需要解析表格,可以使用 apache poi.
- 你需要通用操作,可以使用 apache commons.
另外一種情況是,你可能不知道某個功能在 JDK 中已經實現,這時候你應該多多檢視 JDK Document. 我就在工作中見到過同事手寫字串 split,為了獲取時間戳把 Date 物件轉換到 Calendar.
缺少必要的溝通
這個部分是和開發沒有關係的,但是這個環節往往會影響最終的開發結果。進行具體的開發之前,你應該詳細的溝通並理解功能的需求,這樣你才能針對具體的需求寫出不偏離實際需要的程式碼。有時候你很有可能因為缺少必要的溝通,錯誤了理解了需求,最終在開發完畢後發現自己寫的功能完全沒有用處。
沒有程式碼規範
程式碼規範性非常重要,如果一個專案裡充斥著各種稀奇古怪的程式碼規範,會讓維護者十分頭疼。而且軟體行業高速發展,對開發者的綜合素質要求也越來越高,優秀的程式設計習慣也可以提高軟體的最終質量。比如:標新立異的命名風格挑戰閱讀習慣;五花八門的錯誤碼人為地 增加排查問題的難度;工程結構混 亂導致後續專案維護艱難;沒有鑑權的漏洞程式碼易被黑客攻擊;質量低下的程式碼上線之後漏洞百出等等。因為沒有統一的程式碼規範,開發中的問題可能層出不窮。
下面簡單列舉些應該統一的開發規範。如命名風格如何是好;常量名稱結構怎樣;程式碼格式怎麼統一;日期時間格式如何處理;集合處理注意事項;日誌列印有無規範;前後互動具體規約等。
上面所說的開發規範程式碼規範推薦阿里推出的 《Java 開發手冊》,裡面詳細列舉了在 Java 開發中各個方面應該遵守的規約和規範。最新版本在 8月 3日已經發布,可以在公眾號 “未讀程式碼” 直接回復 "java " 獲取最新版 pdf.
總結
Bug 和技術上的誤解都是美麗的謎團,福爾摩斯般的我們終將解決這些問題。命運自己掌握,每一次探清的這些技術誤解,都會增加我們對開發編碼的理解。盡情接招吧,色彩斑斕才有趣,萬般體驗才是人生,不管是多樣的技術,還是多樣的問題,我都想看見。
參考:
[1] A beginner’s guide to Java programming nightmares
[2] Java™ Platform, Standard Edition 8 API Specification
最後的話
文章已經收錄在 Github.com/niumoo/JavaNotes ,歡迎Star和指教。更有一線大廠面試點,Java程式設計師需要掌握的核心知識等文章,也整理了很多我的文字,歡迎 Star 和完善,希望我們一起變得優秀。
文章有幫助可以點個「贊」或「分享」,都是支援,我都喜歡!
文章每週持續更新,要實時關注我更新的文章以及分享的乾貨,可以關注「 未讀程式碼 」公眾號或者我的部落格。