分享工作中的點點滴滴,貫徹千里之行,始於足下,最終以微不足道的量變引起化蝶的質變精神。以自己為例拒絕在舒適的中央區域安逸的躺著,以便在不知不覺中被社會所淘汰,也不盲目的直接躍遷進困哪區域,在受挫的同時又跌回原有的舒適區域內,造成這樣一個渾渾噩噩的讓人無法進步的迴圈怪圈內,保持在舒適邊緣的拉伸區,既跳出了舒適區又具有一定的挑戰性,使得自己在保持快速進步的同時還能夠漸漸樹立起自信心,可謂是一舉多得,系統的維護改造也是如此道理。如果大家覺得有用,歡迎大家點贊、收藏+關注,哇咔咔😀!
一、什麼是編碼規範
在Java的世界裡,編碼規範不僅僅是一堆乏味的規則和條款,它們是通往程式碼優雅之路的黃磚路。想象一下,沒有編碼規範的Java程式碼庫就像是一場沒有裁判的足球賽,混亂不堪,每個人都在按自己的規則踢球。但別擔心,今天我們將帶你走進編碼規範的奇妙世界,探索那些讓你的程式碼從"看起來還行"升級為"哇,這是誰寫的神仙程式碼"的秘密。
我們將一起探索為什麼命名一個變數為temp123可能會讓你的同事在code review時掉頭就跑,為何空格和縮排在程式碼中的角色比配角還重要,以及如何透過簡單的規則讓你的程式碼變得更加清晰、易讀,就像是在閱讀一篇暢銷小說。
準備好了嗎?讓我們一起開始我們的編碼規範之旅,讓你的Java程式碼不僅僅是執行的藝術,更是視覺的享受。 Buckle up(繫好安全帶),這將是一次有趣的旅程!
二、為什麼要重視編碼規範
想要成為程式碼界的建築大師嗎?嗯,讓我們從最基礎的部分講起——基礎的重要性。你有沒有聽過那句老話,“只要功夫深,鐵杵磨成針”?如果你想讓你的程式碼像針一樣鋒利、精確,那麼你得開始磨練你的基礎功夫了。
想象一下,你正在建造一座高樓。如果地基不牢固,那麼不管你的建築有多華麗,最終都會成為傾斜的比薩塔的親戚。同樣的,程式設計也是這樣。如果你的基礎不牢固,那麼不管你用了多少高階技巧,你的程式碼最終可能就是一個功能混亂的軟體比薩塔。
記住,“千里之行,始於足下”。每一行程式碼,每一個函式,都是你軟體高樓的一磚一瓦。而這些磚瓦的質量,取決於你對基礎知識的掌握程度。正如“滴水石穿”,持之以恆的練習和對基礎的不斷打磨,最終會讓你的程式設計技能堅不可摧。
再來,你聽說過“冰凍三尺,非一日之寒”嗎?優秀的程式碼庫也是如此,它們的優秀並非一蹴而就,而是基於堅實的基礎,經過長時間的積累和迭代。
最後,讓我們以“愚公移山”的精神結束這段討論。面對看似無窮無盡的程式設計知識,我們可能會感到力不從心。但只要我們堅持不懈,就沒有什麼山是移不走的,沒有什麼基礎是打不牢的。
所以,親愛的程式碼工匠們,讓我們從今天開始,把握好每一個學習的機會,把基礎打得牢牢的。記住,偉大的軟體建築,都是從一行簡單的程式碼開始的。
三、編碼細則極其背後的深因
命名規範
1.類名使用UpperCamelCase風格,必須遵從駝峰形式,但以下情形例外:(領 域模型的相關命名)DO / BO / DTO / VO / DAO
深因:
一致性:類名遵循UpperCamelCase(大駝峰式)增加了程式碼的一致性,使得類名容易識別和區分。
可讀性:大駝峰式命名使得多個單詞的組合在視覺上更為清晰,有助於理解類的用途。
領域模型例外:DO (Data Object), BO (Business Object), DTO (Data Transfer Object), VO (Value Object), DAO (Data Access Object) 是業界廣泛認可的縮寫,代表了特定的設計模式和概念。它們的使用在領域驅動設計中具有特定含義,保持這些縮寫可以讓開發人員快速理解類的職責。
正例(遵循規範):
UserProfile - 明確遵從大駝峰式命名。
UserDTO - 表示一個用於資料傳輸的物件,DTO作為普遍接受的縮寫被保留。
反例(違反規範):
userProfile - 類名以小寫字母開頭,不符合大駝峰式命名規則。
UserDataObject - 應縮寫為UserDO,因為DO是一個被廣泛認可的領域模型命名縮寫。
透過此規範,可以確保程式碼的整潔性、一致性和專業性,同時也尊重了行業內的共識和最佳實踐。
2.抽象類命名使用Abstract或Base開頭
深因:
明確性:以Abstract或Base開頭的命名立即明確了該類的抽象性質,讓其他開發者一眼就能識別出這是一個不應被直接例項化的類。
可維護性:當專案規模擴大時,清晰的命名規範有助於維持程式碼的可維護性,減少查詢和理解各個類之間關係的時間。
一致性:統一的命名規範有助於保持程式碼庫的一致性,使得新加入的開發人員能更快地熟悉程式碼庫。
避免命名衝突:在有些情況下,抽象類和具體實現類可能會有相似的功能描述,以Abstract或Base開頭可以減少命名上的衝突。
正例(遵循規範):
AbstractVehicle:一個定義了交通工具通用屬性和方法的抽象類,正確地使用了Abstract字首。
BaseService:定義了服務層基本功能的抽象類,使用了Base字首,明確表示這是一個基礎類,用於被繼承。
反例(違反規範):
Vehicle:假如這是一個抽象類,但沒有使用Abstract或Base字首,這使得它看起來像是一個可直接例項化的類。
Service:如果這是一個旨在被其他服務類繼承的基礎抽象類,但命名中缺少了表明其抽象性質或基礎性質的字首。
透過引入這條規範,可以提高程式碼的可讀性和維護性,同時減少因命名不當引起的混淆。
3.異常類命名使用Exception結尾
深因:
清晰性:命名中使用Exception結尾能立即明確該類是一個異常類,有助於開發者快速識別其用途和性質。
一致性:這一命名規範與Java標準異常類的命名保持一致,如NullPointerException、IndexOutOfBoundsException等,有助於保持程式碼的一致性,減少學習成本。
可讀性:在閱讀程式碼時,能夠透過異常類名直接瞭解到該異常的大致型別,提高了程式碼的可讀性。
預防錯誤:明確的命名有助於防止將異常類與普通類混淆,減少因錯誤處理異常或誤用類而引發的bug。
正例(遵循規範):
UserNotFoundException:明確表示尋找使用者失敗時丟擲的異常,正確地使用了Exception結尾。
InvalidInputException:表示輸入無效時丟擲的異常,使用了Exception結尾,清晰地表明瞭其是一個異常類。
反例(違反規範):
UserNotFound:雖然意圖表達尋找使用者失敗的情況,但由於沒有使用Exception結尾,使得它看起來更像是一個普通類而非異常類。
InvalidInput:這個名字沒有明確表明它是一個異常類,可能會被誤解為一個方法名或變數名,而不是一個應該被丟擲和捕獲的異常型別。
透過遵循這條規範,開發者可以更容易地編寫和維護清晰、一致且易於理解的異常處理程式碼。
4.單元測試類命名以它要測試的類的名稱開始,以Test結尾
深因:
直觀性:當測試類以被測試類的名稱開始,緊隨其後加上Test作為字尾,這種命名方式直觀地表明瞭測試類的目的和它所測試的具體類,提高了可讀性和易理解性。
可查詢性:這種命名約定使得開發者可以輕鬆地透過類名找到對應的測試類,或透過測試類推斷出它測試的目標類,從而提高開發效率。
一致性:遵循這一命名規範可以在整個專案或團隊中保持一致性,減少因個人偏好導致的命名混亂,使程式碼庫更加整潔。
組織性:在大型專案中,可能會有大量的測試類。這種命名規則有助於在專案結構中保持組織性,使測試包結構清晰、有序。
正例(遵循規範):
假設有一個類名為UserService,那麼對應的單元測試類應該命名為UserServiceTest。這明確了UserServiceTest是用於測試UserService類的功能。
對於類PaymentProcessor,其單元測試類應該命名為PaymentProcessorTest,這樣一來,僅透過名字就能清楚地知道這是PaymentProcessor類的測試。
反例(違反規範):
如果有一個類名為OrderManager,而其測試類被命名為TestOrderManager或僅僅是OrderTests,這雖然在一定程度上表明瞭測試目標,但不符合“以被測試類的名稱開始,以Test結尾”的規範,可能會導致查詢和理解上的不便。
對於InventoryService類,如果其測試類被命名為InventoryChecks,這種命名雖然描述了測試的一般內容,但沒有遵循規定的命名模式,降低了命名的一致性和直觀性。
遵循這條規範,有助於維護程式碼的清晰度和組織性,同時也方便團隊成員之間的協作和溝通。
5.方法名、引數名、成員/區域性變數都統一使用lowerCamelCase,必須遵從駝峰形式
深因:
一致性:在整個專案中統一使用lowerCamelCase(小駝峰命名法)可以保持程式碼的一致性,使得程式碼更加整潔和統一。
可讀性:lowerCamelCase透過在單詞之間使用大小寫來區分,無需額外的分隔符,從而提高了程式碼的可讀性和易於理解。
遵循約定:在多數程式語言中,lowerCamelCase是方法名、引數名、成員變數和區域性變數的普遍約定,遵循這些約定有助於維持程式碼風格的一致性,同時也方便其他開發者閱讀和理解程式碼。
減少錯誤:統一的命名規範有助於減少由於命名不一致導致的混淆和錯誤。
正例(遵循規範):
方法名:calculateTotalPrice,清晰地表明這是一個計算總價的方法。
引數名:customerName,明確指出傳入的是顧客的名字。
成員變數:shoppingCart,代表購物車物件。
區域性變數:itemCount,表示物品數量。
反例(違反規範):
方法名:CalculateTotalPrice或calculate_total_price。前者使用了PascalCase(大駝峰命名法),後者使用了snake_case(下劃線命名法),都不符合lowerCamelCase的規範。
引數名:CustomerName或customer_name,同樣違反了使用lowerCamelCase的規範。
成員變數:ShoppingCart或shopping_cart,沒有遵循小駝峰命名法。
區域性變數:ItemCount或item_count,同樣違反了小駝峰命名法的規定。
透過遵循lowerCamelCase命名規範,可以使程式碼更加統一和易於理解,促進團隊內部和跨團隊之間的有效溝通。
6.程式碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束
深因:
清晰性:避免使用下劃線或美元符號作為命名的開始或結束,可以使得程式碼命名更加清晰,易於閱讀和理解。這些符號在很多語言中有特殊含義,過度使用可能導致混淆。
一致性:統一的命名規範有助於保持程式碼的一致性,減少因個人命名偏好導致的風格差異,使程式碼庫整體更加規範和整潔。
可維護性:清晰和一致的命名規範有助於提高程式碼的可維護性,便於團隊協作和程式碼的長期維護。
避免衝突:在某些程式語言中,下劃線或美元符號用於特殊變數或內部語言機制,避免使用這些符號作為普通命名的一部分,可以減少與語言特性的衝突。
正例(遵循規範):
變數名:userName,明確且易於理解,且沒有使用下劃線或美元符號作為開頭或結尾。
方法名:calculateTotal,遵循了規範,清晰表達了方法的功能。
反例(違反規範):
變數名:_userName或userName_,以及$userName或userName$,這些命名都違反了不使用下劃線或美元符號開頭或結尾的規範。
方法名:_calculateTotal或calculateTotal_,以及$calculateTotal或calculateTotal$,同樣違反了規範。
透過遵守這條規範,可以使程式碼更加清晰和規範,減少潛在的混淆,促進程式碼的健康發展和團隊間的有效溝通。
7.常量命名應該全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長
深因:
可識別性:全部大寫字母加下劃線的命名方式使常量在程式碼中非常容易識別,區分於變數和其他型別的命名。
清晰性:強調語義表達的完整性有助於提高程式碼的清晰度,即使名稱較長,也能確保其意圖和用途一目瞭然。
一致性:遵循此規範能在整個專案或團隊中保持命名的一致性,減少因個人偏好導致的風格差異。
避免衝突:通常變數和函式使用小駝峰或大駝峰命名法,常量使用全大寫與下劃線的方式可以有效避免命名衝突。
正例(遵循規範):
MAX_USER_COUNT:表明了這是一個表示使用者數量上限的常量,名稱完整表達了其意圖。
DEFAULT_PAGE_SIZE:清楚地指出這是預設頁面大小的常量,語義明確。
反例(違反規範):
MaxUser或maxUser:雖然意圖指代最大使用者數,但不符合全大寫和單詞間用下劃線隔開的規範,且看起來更像是變數而非常量。
defaultsize或defaultSize:名稱不僅沒有全部大寫,而且單詞間沒有使用下劃線隔開,語義表達也不夠清晰。
透過遵循這條規範,可以顯著提高程式碼中常量的可識別性和清晰性,有助於維護和理解程式碼。
8.對於Service和DAO類,基於SOA的理念,暴露出來的服務一定是介面,內部的實現類用Impl的字尾與介面區別
深因:
解耦: 透過定義介面,將實現與呼叫解耦,便於在不同實現間切換,提高了程式碼的靈活性和可維護性。
易於擴充套件: 介面定義了一組規範,使得未來擴充套件或修改功能時,只需新增或修改具體實現類,而不需要修改呼叫方程式碼。
便於測試: 介面使得可以使用Mock物件來替代具體實現,便於進行單元測試。
清晰的結構: 介面和實現類的命名規範有助於快速識別類的作用,增加了程式碼的可讀性。
正例(遵循規範):
介面命名為UserService,表明這是一個使用者服務的介面。
實現類命名為UserServiceImpl,清楚地表明這是UserService介面的一個具體實現。
public interface UserService {
void addUser(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void addUser(User user) {
// 實現新增使用者的邏輯
}
}
反例(違反規範):
介面和實現類命名為UserService和UserServiceImplementation,或者僅僅是在實現類上使用Service作為字尾。這種命名方式不夠簡潔,且可能導致命名的不一致性。
實現類沒有明確使用Impl字尾,例如只是UserManager,這樣就不容易區分哪些是介面,哪些是實現類。
透過遵循這條規範,可以提高程式碼的結構清晰度,便於維護和擴充套件,同時也符合SOA(面向服務的架構)的設計理念。
9.包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使用單數形式,但是類名如果有複數含義,類名可以使用複數形式
深因:
避免平臺差異:不同的作業系統對檔名的大小寫敏感性不同。統一使用小寫可以避免跨平臺開發時的混淆和錯誤。
提高可讀性:點分隔符和自然語義的英語單片語合,使得包路徑易於理解,反映了專案的結構和內容。
保持一致性:使用單數形式的包名,保持了命名的簡潔和一致性,避免了複數形式可能帶來的混淆。
靈活性和準確性:允許類名使用複數形式,為表達“集合”或“多個實體”的概念提供了靈活性,使得類的命名更加準確和直觀。
正例(遵循規範):
包名:com.example.project.user,使用了小寫,點分隔符後是單數形式的自然語義英語單詞。
類名:若類表示多個使用者,可以命名為Users。
package com.example.project.user;
public class Users {
// 類實現
}
反例(違反規範):
包名:com.Example.Project.Users,使用了大寫字母和複數形式,違反了包名全小寫和單數形式的規範。
包名:com.example.project.user_info,使用了下劃線而不是點分隔符,且包含了多於一個自然語義的英語單詞。
package com.Example.Project.Users; // 錯誤的包命名
public class UserList {
// 類實現
}
透過遵循這條規範,可以使得包結構更加清晰,易於管理,同時也提高了程式碼的可讀性和一致性。
10.POJO類中的任何布林型別的變數,都不要加is,否則部分框架解析會引起序列化錯誤
深因:
相容性問題:在Java Bean規範中,布林型別的屬性通常透過is字首的getter方法來訪問。但是,在使用某些序列化框架時,如果欄位名本身以is開頭,可能會導致框架在生成的getter/setter方法命名上產生混淆,引起序列化或反序列化錯誤。
清晰的命名約定:避免在變數名中使用is字首,可以使得命名約定更加清晰。透過getter和setter方法的命名來表達屬性的意圖,而不是透過變數名本身。
提高程式碼的可讀性和維護性:統一的命名規範有助於提高程式碼的可讀性和維護性,特別是在團隊協作中。
正例(遵循規範):
假設有一個布林型別的變數,表示使用者是否已經啟用:
public class User {
private boolean active; // 不使用is字首
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
反例(違反規範):
在變數命名中使用is字首,可能會與自動生成的getter方法衝突,導致序列化框架解析錯誤:
public class User {
private boolean isActive; // 使用了is字首
public boolean isActive() {
return isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
}
在這個反例中,某些序列化框架可能期望訪問器方法為getIsActive()而不是isActive(),因此,遵循不在布林型別變數名中使用is字首的規範,可以避免這類問題。
11.型別與中括號緊挨相連來表示陣列
深因:
一致性:將型別和中括號緊挨相連有助於保持程式碼的一致性,使得陣列型別的宣告更加清晰和一致。
提高可讀性:這種宣告方式明確了陣列的型別,使得閱讀和理解程式碼變得更加容易。
避免誤解:在某些情況下,將中括號放在變數名而不是型別名旁邊可能會導致誤解,尤其是在宣告多個變數時。
正例(遵循規範):
int[] numbers;String[] names;
在這個例子中,型別(int、String)與中括號緊挨相連,清晰地表示了變數是陣列型別。
反例(違反規範):
雖然這種宣告方式在Java中是合法的,但它不符合“型別與中括號緊挨相連來表示陣列”的規範。特別是在宣告多個陣列或非陣列變數時,可能會導致混淆:
int numbers[], size; // size不是陣列型別,但這種宣告方式可能會導致誤解。
透過遵循這條規範,可以提高程式碼的清晰度和一致性,避免可能的誤解,使得程式碼更加易於閱讀和維護。
12.禁止在POJO類中,同時存在對應屬性xxx的isXxx()和getXxx()方法
深因:
避免混淆: 同一個屬性存在isXxx()和getXxx()方法會造成混淆,不清楚哪個方法應該被使用,尤其是在框架利用反射時。
框架相容性: 一些框架在處理布林屬性時,對isXxx()和getXxx()方法有特定的預期和處理邏輯。同時存在這兩種方法可能導致框架的反射機制工作不正常,進而影響序列化和反序列化行為。
程式碼清晰性: 保持每個屬性只有一個訪問器方法可以使得程式碼更加清晰,易於理解和維護。
Java Bean規範: 根據Java Bean規範,布林型別的屬性應當使用isXxx()形式的訪問器方法,而非布林型別的屬性應使用getXxx()方法。
正例(遵循規範):
對於布林型別的屬性active,只提供isActive()方法:
public class User {
private boolean active;
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
反例(違反規範):
對於同一個布林型別的屬性active,同時提供了isActive()和getActive()方法:
public class User {
private boolean active;
public boolean getActive() {
return active;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
在這個反例中,同一個屬性active提供了兩個訪問器方法,這可能會導致在使用Java Bean相關框架時出現問題。遵循此規範,確保每個屬性只有一種符合其型別的訪問器方法,有助於提高程式碼質量和減少潛在的錯誤。
註釋規範
1.所有的欄位和方法必須要用javadoc註釋
深因:
提高程式碼的可讀性和可維護性:透過Javadoc註釋,開發者可以快速理解每個欄位和方法的用途、引數、返回值等,無需深入閱讀實現程式碼。
促進團隊協作:在團隊開發中,詳細的文件可以幫助新成員快速理解專案,減少溝通成本。
自動生成文件:Javadoc註釋可以被工具用來生成標準的HTML格式的API文件,便於分享和查閱。
規範編碼風格:強制要求使用Javadoc註釋可以規範開發者的編碼風格,使得程式碼整體質量更高。
正例(遵循規範):
/**
* 使用者類。
*/
public class User {
/**
* 使用者的名字。
*/
private String name;
/**
* 獲取使用者的名字。
*
* @return 使用者的名字
*/
public String getName() {
return name;
}
/**
* 設定使用者的名字。
*
* @param name 使用者的新名字
*/
public void setName(String name) {
this.name = name;
}
}
反例(違反規範):
public class User {
private String name; // 缺少Javadoc註釋
public String getName() { // 缺少Javadoc註釋
return name;
}
public void setName(String name) { // 缺少Javadoc註釋
this.name = name;
}
}
在反例中,欄位和方法都沒有Javadoc註釋,這使得其他開發者或使用者難以快速理解其用途和功能。
遵循這條規範,可以顯著提升程式碼的可讀性和易維護性,同時也有助於自動生成文件。
2.所有的列舉型別欄位必須要有註釋,說明每個資料項的用途
深因:
明確列舉項的含義:列舉型別通常用來表示一組固定的常量,每個列舉項都有其特定的用途和意義。註釋可以幫助開發者快速理解每個列舉項的具體含義,提高程式碼的可讀性。
提高程式碼的可維護性:隨著時間的推移,專案中的列舉型別可能會被多次修改或擴充套件。有註釋的列舉項可以讓後來者更容易理解每個列舉項的用途,減少因誤解造成的錯誤。
促進團隊協作:在團隊協作中,清晰的註釋可以減少成員之間的溝通成本,特別是對於新加入的團隊成員,有助於他們更快地理解專案程式碼。
正例(遵循規範):
/**
* 表示使用者狀態的列舉。
*/
public enum UserStatus {
/**
* 啟用狀態。使用者已啟用且可以正常使用系統。
*/
ACTIVE,
/**
* 禁用狀態。使用者已被禁用,不能登入或使用系統。
*/
DISABLED,
/**
* 等待啟用。使用者已註冊但尚未啟用。
*/
PENDING_ACTIVATION
}
反例(違反規範):
public enum UserStatus {
ACTIVE, // 缺少註釋
DISABLED, // 缺少註釋
PENDING_ACTIVATION // 缺少註釋
}
在反例中,UserStatus列舉的每個項都沒有註釋,這使得其他開發者難以理解每個列舉項的具體含義和用途。
遵循這條規範,透過為每個列舉項新增清晰的註釋,可以顯著提升程式碼的可讀性和可維護性。
3.方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋使用/**/註釋,注意與程式碼對齊
深因:
提高可讀性:透過在被註釋語句上方另起一行進行單行註釋,可以使得註釋更加突出,易於閱讀。多行註釋同樣需要對齊,以保持程式碼的整潔和一致性。
區分註釋型別:使用//進行單行註釋和使用/**/進行多行註釋可以幫助區分註釋的用途和長度,使得程式碼更加清晰。
維護程式碼風格一致性:統一的註釋風格有助於維護程式碼的整體風格一致性,無論是在同一專案內還是跨專案。
正例(遵循規範):
public void updateUserInfo() {
// 檢查使用者是否登入
if (user.isLoggedIn()) {
user.updateLastLoginTime(); // 更新最後登入時間
}
/*
* 多行註釋
* 更新使用者狀態和時間
*/
user.setStatus("active");
user.setUpdateTime(System.currentTimeMillis());
}
在這個例子中,單行註釋使用//並放在被註釋語句的上方,而多行註釋使用/**/並與程式碼對齊,保持了程式碼的清晰和整潔。
反例(違反規範):
public void updateUserInfo() {
user.updateLastLoginTime(); // 檢查使用者是否登入並更新最後登入時間
/* 更新使用者狀態
和時間 */
user.setStatus("active");
user.setUpdateTime(System.currentTimeMillis());
}
在這個反例中,單行註釋和被註釋的語句在同一行,多行註釋沒有與程式碼對齊,這些做法都降低了程式碼的可讀性和整潔性。
遵循這條規範,透過在方法內部恰當地使用單行和多行註釋,可以大大提高程式碼的可讀性和維護性。
常量定義規範
1.long或者Long初始賦值時,必須使用大寫的L,不能是小寫的l,小寫容易跟數字
深因:
提高程式碼可讀性:小寫的l和數字1在很多字型中非常相似,這可能導致閱讀程式碼時的混淆和錯誤。使用大寫的L可以明顯區分,提高程式碼的可讀性。
減少錯誤:在長整型數值的賦值過程中,使用清晰明確的標識可以避免由於誤讀導致的錯誤,尤其是在涉及到數值計算的場景中。
統一編碼風格:規定在所有場合下使用大寫的L為long或Long型別賦值,可以統一程式碼風格,減少團隊內部的差異。
正例(遵循規範):
long count = 1000L;
Long total = 5000L;
在這個例子中,所有的long或Long型別賦值都使用了大寫的L,清晰且易於區分。
反例(違反規範):
long count = 1000l;
Long total = 5000l;
在這個反例中,long或Long型別賦值使用了小寫的l,這在某些字型中可能與數字1混淆,降低了程式碼的可讀性。
遵循這條規範,透過使用大寫的L為long或Long型別賦值,可以有效避免混淆和錯誤,提高程式碼的整體可讀性。
2.不允許任何魔法值(即未經定義的常量)直接出現在程式碼中
深因:
提高程式碼的可讀性:使用有意義的常量名代替魔法值可以讓程式碼更易於理解。讀者可以透過常量名瞭解其用途,而不是試圖解釋一個裸露的數值或字串的含義。
便於維護:當需要修改一個在多處使用的值時,使用常量可以讓你只需要修改定義常量的地方,而不需要逐個修改多處的硬編碼值。
減少錯誤:直接在程式碼中使用硬編碼值,特別是在多處使用時,容易引入錯誤。例如,如果需要更改該值,可能會遺漏某些例項,導致不一致。
正例(遵循規範):
public class Config {
public static final int MAX_USER_COUNT = 100;
}
// 使用常量
if (userCount > Config.MAX_USER_COUNT) {
// 處理超出使用者數限制的情況
}
在這個例子中,100被定義為一個有意義的常量MAX_USER_COUNT,使用這個常量來代替魔法值。
反例(違反規範):
// 直接使用魔法值
if (userCount > 100) {
// 處理超出使用者數限制的情況
}
在這個反例中,100直接硬編碼在判斷語句中,這是一個魔法值的典型使用場景,它降低了程式碼的可讀性和可維護性。
遵循這條規範,透過將魔法值替換為有意義的常量,可以顯著提高程式碼的可讀性、可維護性,並減少因硬編碼引入的錯誤。
集合規範
1. 使用集合轉陣列的方法,必須使用toArray(T[] array),傳入型別完全一樣的陣列,大小list.size()
深因:
型別安全:使用toArray(T[] array)方法並傳入型別完全一樣的陣列可以保證轉換結果的型別安全。這樣可以避免在執行時因型別不匹配而丟擲異常。
效能最佳化:如果傳入的陣列大小與集合大小一致(list.size()),則該陣列將被直接使用,避免了額外的陣列分配和複製,提高了效率。
避免使用反射:與toArray()方法相比,toArray(T[] array)避免了在內部使用反射來建立返回陣列,因此可以提高效能。
正例(遵循規範):
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
// 使用toArray(T[] array)傳入型別完全一樣的陣列,大小為list.size()
String[] array = list.toArray(new String[list.size()]);
在這個例子中,傳入了一個型別和大小都符合要求的陣列給toArray方法,這是符合規範的做法。
反例(違反規範):
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
// 使用無參toArray()方法
Object[] array = list.toArray();
// 或使用大小不符合list.size()的陣列
String[] smallerArray = list.toArray(new String[0]);
在這兩個反例中,第一個示例使用了無參的toArray()方法,返回了一個Object[]型別的陣列,這不是型別安全的。第二個示例雖然使用了toArray(T[] array)方法,但傳入的陣列大小不是list.size(),這可能導致額外的陣列分配和複製,降低了效率。
遵循這條規範,透過使用toArray(T[] array)方法並傳入一個大小為list.size()的型別匹配陣列,可以在型別安全的同時提高效能。
2.使用工具類Arrays.asList()把陣列轉換成集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會丟擲UnsupportedOperationException異常
深因:
避免執行時異常:Arrays.asList()返回的是一個固定大小的列表,它基於原陣列,僅支援那些不會改變陣列大小的操作。嘗試執行add、remove或clear等修改操作會丟擲UnsupportedOperationException。避免使用這些操作可以防止執行時異常。
提醒開發者注意返回型別限制:明確這一規範可以提醒開發者,透過Arrays.asList()獲得的列表在功能上與ArrayList等完全實現了List介面的集合類有所不同,需要謹慎處理。
促進正確的集合操作使用:引導開發者在需要進行元素增刪的場景中,選擇更合適的集合型別,如直接使用ArrayList等,或者在需要對Arrays.asList()返回的列表進行修改操作時,先將其轉換為一個支援所有List操作的新列表。
正例(遵循規範):
String[] array = {"apple", "banana", "cherry"};
List<String> list = new ArrayList<>(Arrays.asList(array));
// 修改集合
list.add("date");
list.remove("banana");
在這個例子中,透過將Arrays.asList()的結果放入一個新的ArrayList中,我們可以自由地對返回的集合進行修改。
反例(違反規範):
String[] array = {"apple", "banana", "cherry"};
List<String> list = Arrays.asList(array);
// 嘗試修改集合
list.add("date"); // 丟擲UnsupportedOperationException
list.remove("banana"); // 丟擲UnsupportedOperationException
在這個反例中,直接使用Arrays.asList()返回的列表進行修改操作,將會導致UnsupportedOperationException異常。
遵循這條規範,可以避免在執行時因嘗試修改不支援修改操作的集合而產生異常,確保程式碼的健壯性。
3.ArrayList的subList結果不可強轉成ArrayList,否則會丟擲ClassCastException異常
深因:
型別不相容:ArrayList的subList方法返回的是java.util.List介面的一個檢視,而不是ArrayList例項。這個檢視是內部類(如ArrayList$SubList),並不是ArrayList類本身。因此,嘗試將其強制轉換為ArrayList會因型別不匹配而丟擲ClassCastException。
保持檢視的動態性:subList返回的列表檢視提供了對原列表的部分檢視,對這個檢視的所有操作都會反映在原列表上(反之亦然)。這種設計意味著檢視與原列表緊密相連,而直接轉換為ArrayList不僅不可能,也會誤導開發者忽視subList與原列表的動態關係。
促進正確的API使用:明確這一規範可以鼓勵開發者使用正確的型別和方法來處理集合和子集合,避免不必要的型別轉換錯誤,提高程式碼質量和可維護性。
正例(遵循規範):
ArrayList<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date"));
List<String> subList = list.subList(1, 3);
// 正確使用subList結果
System.out.println(subList);
在這個例子中,subList的結果被正確地處理為List型別,沒有進行不當的型別轉換。
反例(違反規範):
ArrayList<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date"));
// 嘗試將subList結果強轉為ArrayList
ArrayList<String> subList = (ArrayList<String>)list.subList(1, 3); // 丟擲ClassCastException
在這個反例中,嘗試將subList的結果強制轉換為ArrayList型別,這將導致ClassCastException異常。
遵循這條規範,可以避免不必要的型別轉換異常,更加準確地使用Java集合框架提供的API,提高程式碼的健壯性和可讀性。
4.在subList場景中,高度注意對原列表的修改,會導致子列表的遍歷、增加、刪除均產生ConcurrentModificationException異常
深因:
維護一致性:subList方法返回的子列表是原列表的一個檢視,這意味著對原列表或子列表的任何修改都會反映在另一方。如果在遍歷子列表的同時修改原列表,將破壞列表的結構,因此為了維護操作的一致性和預期行為,Java會丟擲ConcurrentModificationException。
防止不可預見的行為:在對原列表進行修改後繼續操作子列表可能會導致不可預見的行為,因為子列表的內容、大小和預期操作結果可能已經由於原列表的修改而發生變化。
提升程式碼穩定性和可靠性:透過避免在子列表操作過程中修改原列表,可以防止執行時異常,從而提高程式碼的穩定性和可靠性。
正例(遵循規範):
ArrayList<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date"));
List<String> subList = list.subList(1, 3);
// 在操作子列表之前不修改原列表
subList.remove("banana"); // 安全操作
System.out.println(list); // 輸出修改後的原列表和子列表
在這個例子中,在對子列表操作之前沒有對原列表進行修改,遵循了規範。
反例(違反規範):
ArrayList<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date"));
List<String> subList = list.subList(1, 3);
// 在遍歷子列表時修改原列表
list.add("fig"); // 修改原列表
subList.get(0); // 嘗試訪問子列表,可能丟擲ConcurrentModificationException
在這個反例中,修改原列表後嘗試訪問子列表,這種操作違反了規範,因為它可能導致ConcurrentModificationException異常。
遵循這條規範,可以避免因為列表的併發修改而導致的異常,保證程式碼的穩定性和預期行為。
5.不要在foreach迴圈裡進行元素的remove/add操作,remove元素請使用Iterator方式
深因:
避免ConcurrentModificationException異常:在foreach迴圈中對集合進行修改(如新增或刪除元素)會導致快速失敗(fail-fast)機制觸發,丟擲ConcurrentModificationException。這是因為foreach迴圈基於集合的Iterator,而直接修改集合會導致迭代器的狀態與集合的狀態不一致。
保持程式碼的穩定性和可預測性:使用迭代器的remove方法可以安全地在遍歷過程中刪除元素,因為它會正確地更新迭代器的狀態,避免異常發生,從而保持程式碼的穩定性和可預測性。
提高程式碼可讀性和維護性:遵循此規範有助於提高程式碼的可讀性和維護性,因為使用迭代器進行元素的刪除是一種更明確和安全的方式,其他開發者能夠更容易理解程式碼的意圖。
正例(遵循規範):
List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if ("banana".equals(fruit)) {
iterator.remove(); // 使用Iterator的remove方法安全刪除元素
}
}
System.out.println(list); // 輸出: [apple, cherry]
在這個例子中,透過使用Iterator的remove方法安全地在遍歷過程中刪除元素,遵循了規範。
反例(違反規範):
List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry"));
for (String fruit : list) {
if ("banana".equals(fruit)) {
list.remove(fruit);
}
}
在這個反例中,嘗試在foreach迴圈中直接從集合中刪除元素,這違反了規範,因為這樣做可能會導致ConcurrentModificationException異常。
遵循這條規範,可以避免在遍歷集合時因修改集合而導致的異常,保證程式碼執行的穩定性和安全性。
6.集合初始化時,指定集合初始值大小
深因:
提高效能:指定集合初始值大小可以減少在新增元素時動態擴容的次數,從而減少記憶體分配和複製舊陣列到新陣列的開銷,特別是在我們預先知道將要儲存多少元素時。
減少記憶體浪費:透過精確或接近精確指定初始容量,可以避免分配比實際需要更多的記憶體空間,從而減少記憶體浪費。
提升程式碼的可讀性和意圖明確性:在初始化時指定集合大小,可以讓後來的程式碼維護者更清楚地瞭解開發者的意圖,即對集合大小的預期。
正例(遵循規範):
// 假設我們已知需要儲存5個元素
List<String> list = new ArrayList<>(5);
在這個例子中,我們預先知道列表將儲存5個元素,因此在初始化時指定了初始容量為5。這樣可以減少動態擴容的次數。
反例(違反規範):
// 未指定初始值大小
List<String> list = new ArrayList<>();
// 假設後續程式碼中新增了大量元素
for (int i = 0; i < 1000; i++) {
list.add("element" + i);
}
在這個反例中,初始化時沒有指定集合的初始大小。如果後續程式碼需要新增大量元素,這將導致多次動態陣列擴容,從而影響效能。
遵循這條規範,可以幫助提高集合操作的效能,減少記憶體浪費,同時使程式碼更加清晰和高效。
不行了,不行了太水了,我裝不下去了哈哈,搞點容易被忽略但又確實很重要的規範來講吧😀
大雜燴
1.禁止使用構造方法BigDecimal(double)的方式把double值轉化為BigDecimal物件
做過對數字敏感業務的大佬們應該對這個不陌生吧,尤其是做過財務的大佬,想必體驗會更深刻,計算值不對,看程式碼沒問題,但是最終結果就是不對,直到你發現精度丟失😃
深因:
精度問題:直接使用 BigDecimal(double) 構造方法可能會導致精度不準確。double 型別的值在轉換為 BigDecimal 物件時,可能無法精確表示原始 double 值,因為 double 是一個浮點型別,其表示方式和精度與 BigDecimal 的不同。
可預測性:使用 String 引數或 BigDecimal.valueOf(double) 方法建立 BigDecimal 物件可以避免因浮點表示導致的預料之外的精度問題,使結果更加可預測。
避免隱藏的bug:不精確的轉換可能在數值處理中引入難以發現的錯誤,特別是在財務計算或需要高精度的場景中,這些錯誤可能導致嚴重後果。
正例(遵循規範):
BigDecimal correct = new BigDecimal("0.1");
// 或者
BigDecimal alsoCorrect = BigDecimal.valueOf(0.1);
這兩種方式都能準確地表示 0.1,而不會引入由 double 型別的精度問題導致的誤差。
反例(違反規範):
BigDecimal problematic = new BigDecimal(0.1);
這種方式使用 double 構造方法建立 BigDecimal 例項,可能無法精確表示 0.1,因為 0.1 在 double 型別中是一個近似值。
遵循這條規範,可以確保使用 BigDecimal 時的精度和可預測性,避免在財務計算和需要高度精確的應用中引入隱蔽的錯誤。
2.避免採用取反邏輯運算子,'!'運算子不利於快速理解
這個規則看起來沒什麼特別深奧的,但是確實是容易被大家忽略的,在下就曾經踩過此雷,註釋和邏輯中的!用的都是非常規的反向註釋和邏輯,結果導致理解起來容易出錯,甚至在下看見過一個if裡邊包了很多個判斷條件,各種小括號,而且都是用的反向!邏輯,搞得一點也看不懂,真是耗時又耗力,而且極其容易出錯
深因:
提高程式碼可讀性:避免使用取反邏輯運算子 '!' 可以使程式碼邏輯更直觀,更易於理解。對於一些人來說,直接處理肯定的情況比處理否定的情況更為直接和易懂。
減少理解錯誤:在複雜的邏輯表示式中,使用 '!' 可能會增加理解和解析表示式所需的認知負擔,尤其是在多重否定(如 !!)或者是在多個邏輯運算混合使用時。
避免邏輯錯誤:簡化邏輯表示式有助於減少邏輯錯誤的發生,特別是在進行條件判斷時,直接的條件判斷比間接的取反判斷更不容易出錯。
正例(遵循規範):
if (isAvailable) {
// 執行操作
}
在這個例子中,直接檢查條件是否滿足,而不是它的否定形式,這使得邏輯更直接、更清晰。
反例(違反規範):
if (!isUnavailable) {
// 執行操作
}
這個例子使用了取反邏輯運算子 '!' 來檢查條件,這要求閱讀程式碼的人需要進行雙重否定的邏輯推理,增加了理解程式碼的難度。
遵循這條規範有助於提高程式碼的可讀性和直觀性,減少因邏輯理解錯誤而引入的bug,特別是在條件判斷複雜或多重邏輯運算時尤為重要。
3.Mybatis自帶的queryForList(String statementName,int start,int size)不推薦使用
深因:
效能問題:queryForList(String statementName, int start, int size) 方法在處理分頁時,會先查詢出所有符合條件的記錄,然後在返回結果列表中根據 start 和 size 引數返回子列表。這意味著,如果資料量很大,即使只需要很少的資料,也會先載入全部資料到記憶體中,這將導致嚴重的效能問題。
資源浪費:由於該方法首先載入所有資料到記憶體,對於資料庫和應用伺服器來說,這無疑增加了額外的負擔,可能導致記憶體溢位或響應時間變長,影響使用者體驗。
現代替代方案:隨著MyBatis版本的更新,更推薦使用分頁外掛來進行分頁查詢。這些方法更加高效,因為它們能夠直接在資料庫層面上限制查詢的範圍,避免不必要的資料載入和處理。
正例(遵循規範):
使用外掛進行分頁查詢:
PageHelper.startPage(pageNum, pageSize);
List<Object> list = sqlSession.selectList("statementName");
反例(違反規範):
int start = 0; // 開始的記錄索引
int size = 10; // 需要獲取的記錄數量
List<Object> list = sqlSession.queryForList("statementName", start, size);
在這個反例中,使用了不推薦的 queryForList(String statementName, int start, int size) 方法進行分頁查詢,可能會導致效能問題和資源浪費。
遵循這條規範,可以提高應用的效能和資源使用效率,同時也是向現代化MyBatis使用方式邁進的一步。
4.執行緒池不允許使用Executors去建立,而是透過ThreadPoolExecutor的方式,這樣的處理方式讓寫的人員更加明確執行緒池的執行規則,規避資源耗盡的風險
深因:
明確執行緒池引數:透過直接使用 ThreadPoolExecutor 構造方法建立執行緒池,可以讓開發者明確指定執行緒池的核心引數,如核心執行緒數、最大執行緒數、存活時間、工作佇列等,這有助於開發者深入理解執行緒池的工作原理和效能特性。
避免資源耗盡:使用 Executors 類的靜態方法建立執行緒池(如 Executors.newCachedThreadPool() 和 Executors.newFixedThreadPool())時,可能會由於不恰當的配置導致資源耗盡。例如,newCachedThreadPool 預設允許建立的執行緒數量幾乎是無限的,這可能會導致大量執行緒同時執行,從而耗盡系統資源。
增強可維護性:明確執行緒池的配置引數,有助於後期維護和調優,因為這些引數直接影響到執行緒池的效能和系統資源的使用。
正例(遵循規範):
int corePoolSize = 10;
int maximumPoolSize = 100;
long keepAliveTime = 1L;
TimeUnit unit = TimeUnit.MINUTES;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue
);
在這個例子中,透過 ThreadPoolExecutor 直接構造執行緒池,所有重要的引數都被明確指定,更加透明和可控。
反例(違反規範):
ExecutorService executor = Executors.newFixedThreadPool(100);
// 或者
ExecutorService executor = Executors.newCachedThreadPool();
這兩個反例雖然可以快速方便地建立執行緒池,但隱藏了執行緒池的具體實現細節和引數配置,可能會因為不合理的預設配置導致效能問題或資源耗盡。
遵循這條規範,可以提升執行緒池的使用效率和安全性,減少因執行緒池不當使用導致的系統資源問題。
5.多執行緒並行處理定時任務時,Timer執行多個TimeTask時,只要其中之一沒有捕獲丟擲的異常,其它任務便會自動終止執行,使用ScheduledExecutorService則沒有這個問題
深因:
增強的健壯性:使用 ScheduledExecutorService 相比於 Timer,其能夠確保即使某個定時任務因異常終止,其他任務仍然可以繼續執行。這是因為 ScheduledExecutorService 內部對任務的排程是相互獨立的。
更靈活的錯誤處理:ScheduledExecutorService 允許開發者對每個任務的執行進行更細粒度的控制,包括異常處理。這樣,開發者可以針對特定的異常情況實施相應的處理策略,而不是讓一個未捕獲的異常影響到整個定時任務的執行。
更豐富的功能:ScheduledExecutorService 提供了比 Timer 更為豐富和靈活的排程功能,包括但不限於支援多執行緒並行執行任務、支援任務的週期性執行以及延遲執行等。
正例(遵循規範):
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
executor.scheduleAtFixedRate(() -> {
// 安全執行的任務1
}, 0, 10, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(() -> {
// 安全執行的任務2
// 即使這裡發生異常,不會影響任務1的執行
}, 0, 10, TimeUnit.SECONDS);
在這個例子中,使用 ScheduledExecutorService 建立了一個包含5個執行緒的執行緒池,並安排了兩個任務定期執行。每個任務的執行是獨立的,一個任務的失敗不會影響到另一個。
反例(違反規範):
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 任務1
}
}, 0, 10000);
timer.schedule(new TimerTask() {
@Override
public void run() {
// 任務2,如果這裡丟擲未捕獲的異常,其他任務也會停止執行
}
}, 0, 10000);
在這個反例中,使用 Timer 安排了兩個定時任務。如果任務2丟擲了未捕獲的異常,將會導致整個 Timer 的執行執行緒終止,從而導致任務1也會停止執行。
遵循這條規範,可以提高多執行緒並行處理定時任務的健壯性和可靠性,避免單個任務失敗導致整個定時任務系統的崩潰。
6.必須回收自定義的ThreadLocal變數,尤其線上程池場景下,執行緒經常會被複用,如果不清理自定義的 ThreadLocal變數,可能會影響後續業務邏輯和造成記憶體洩露等問題
深因:
防止記憶體洩露:ThreadLocal 變數儲存在每個 Thread 的一個 ThreadLocalMap 中,如果不手動清理,即使外部引用被回收,ThreadLocal 變數仍然可能長時間存活在 Thread 中,導致記憶體洩露,尤其是在使用執行緒池時,執行緒是被複用的,這種情況更為嚴重。
保證資料隔離性:線上程池場景下,執行緒被複用,如果不清理 ThreadLocal,可能會導致一些敏感資料被後續執行的任務訪問到,這違反了資料隔離的原則,可能影響業務邏輯的正確性。
提高系統穩定性:及時清理 ThreadLocal 變數,可以避免不必要的資源佔用和潛在的記憶體洩露問題,從而提高系統的穩定性和效能。
正例(遵循規範):
public class ExampleThreadLocal {
private static final ThreadLocal<Object> myThreadLocal = new ThreadLocal<>();
public void doSomething() {
try {
myThreadLocal.set(new Object()); // 使用 ThreadLocal
// 業務邏輯
} finally {
myThreadLocal.remove(); // 在 finally 塊中清理 ThreadLocal
}
}
}
在這個例子中,myThreadLocal 在使用完畢後,透過 finally 塊確保了其被清理,這樣即使在使用執行緒池的情況下,也不會有記憶體洩露或資料汙染的風險。
反例(違反規範):
public class ExampleThreadLocal {
private static final ThreadLocal<Object> myThreadLocal = new ThreadLocal<>();
public void doSomething() {
myThreadLocal.set(new Object()); // 使用 ThreadLocal 但沒有清理
// 業務邏輯,缺少清理操作
}
}
在這個反例中,myThreadLocal 被設定了值,但在方法結束時沒有被清理。這在單次使用 Thread 的場景中可能看起來沒什麼問題,但如果線上程池場景下,這個 Thread 可能被重複利用,會導致記憶體洩露和資料汙染。
遵循這條規範,可以有效避免使用 ThreadLocal 變數時的記憶體洩露問題,並保持資料的隔離性,提升系統的穩定性和安全性。
四、總結
在遙遠的Java王國中,住著一群熱愛程式碼的居民。他們日以繼夜地編寫程式碼,希望能創造出令人驚歎的軟體奇蹟。然而,並非所有程式碼都能成為傳說中的英雄。為什麼呢?因為,在這片繁榮的土地上,有一個被忽視的古老法典——Java基本編碼規範。
有人可能會問:“為什麼我們需要編碼規範?難道讓程式碼能跑就不夠好嗎?”哦,親愛的朋友,這就好比問為什麼超級英雄要穿緊身衣一樣。答案很簡單——為了讓一切看起來更加整潔、有序,以及……好吧,主要是為了看起來酷炫。
編碼規範的重要性不僅僅在於它讓程式碼看起來像是由單一神秘程式設計大師在一夜之間完成的,而且它還幫助我們避免了許多潛在的災難。比如,一個沒有遵循規範的程式碼庫,就像是一個沒有交通規則的城市,處處是事故現場,每個人都自行其是,結果只能是混亂一片。
回想下我們之前由於編碼問題幾個教訓:有生產事故的,有遇到問題難以排查的等等。
命名規範:想象一下,如果你的同事把所有的變數都命名為 a1,a2,b1……這不是在寫程式碼,這簡直是在玩一場“猜猜我是誰”的遊戲!
縮排和格式化:沒有一致的縮排和格式化,閱讀程式碼就像是在解讀古埃及象形文字。每個人都覺得自己是對的,但最後只能靠猜。
避免使用ThreadLocal未清理:這就像是你的室友用過浴室後不打掃——一次兩次還好,時間長了,你會發現自己生活在一個生物危機現場。
因此,親愛的Java居民們,讓我們一起遵守這些神聖的編碼準則吧。就像穿上了超級英雄的緊身衣,讓我們的程式碼更加健壯、優雅,並且……當然,更加酷炫!
記住,好的編碼規範不僅能讓你的程式碼“活”得更久,還能讓後來者在閱讀你的程式碼時,不至於想要穿越時空來找你算賬。最後,讓我們共勉之——在程式碼的世界裡,每一個規範的遵守,都是向著成為程式設計界超級英雄邁出的一步。