程式碼規範是程式設計師的基本功,也是團隊協作的基石。本章涵蓋命名、組織、註釋、聲明等核心主題。
GoToFail 漏洞的教訓#
2014 年,蘋果公司的 SSL/TLS 實作中出現了一個嚴重的安全漏洞,根本原因竟是一個簡單的編碼風格問題:
// 有漏洞的程式碼
if ((error = doSomething()) != 0)
goto fail;
goto fail; // 這行總是執行!
if ((error = doMore()) != 0)
goto fail;第二個
goto fail不在 if 條件內,導致後續驗證被跳過,任何憑證都會被接受。
如果遵循「總是使用大括號」的規範,問題一目了然:
if ((error = doSomething()) != 0) {
goto fail;
goto fail; // 顯然多餘
}教訓:編碼規範不是繁文縟節,而是防止低級錯誤的防線。
命名原則#
命名的重要性#
「計算機科學只有兩件難事:快取失效和命名。」—— Phil Karlton
好的命名能讓程式碼自我解釋,減少註釋需求。
命名方法#
| 方法 | 格式 | 適用場景 |
|---|---|---|
| 大駝峰 | InputStream | Java 類別、介面 |
| 小駝峰 | firstName | Java 方法、變數 |
| 蛇形 | max_value | C 語言、常數 |
| 串式 | background-color | CSS 屬性 |
Java 命名規範#
// 類別:大駝峰
public class UserService { }
// 方法:小駝峰,動詞開頭
public void calculateTotal() { }
// 變數:小駝峰,名詞
String firstName;
int itemCount;
// 常數:全大寫蛇形
static final int MAX_RETRY_COUNT = 3;命名三原則#
準確 - 名副其實,見名知意
// 差 int d; // elapsed time in days // 好 int elapsedTimeInDays;
2. **規範** - 遵循語言慣例
```java
// 差:混用命名風格
String user_name; // 蛇形
String firstName; // 駝峰
// 好:統一使用駝峰
String userName;
String firstName;可讀 - 寧長勿短
// 差 List<String> lst; // 好 List<String> userNames;
> [!TIP]
>
> 取名要做到「信、達、雅」—— 準確、直觀、優美。
## 程式碼組織
### 分塊思維
大腦按「塊」處理資訊。好的程式碼組織讓每個邏輯塊清晰可辨:
```java
// 差:擠在一起
if(firstName!=null&&lastName!=null)
// 好:清晰分塊
if ((firstName != null) && (lastName != null))空白的運用#
| 元素 | 用途 | 示例 |
|---|---|---|
| 空格 | 同行內區隔 | a + b |
| 縮排 | 層級關係 | 4 空格或 2 空格 |
| 空行 | 區塊分隔 | 方法之間 |
一行一行為#
// 差:兩個行為在一行
if (variable != null) variable.doSomething();
// 好:分開兩行
if (variable != null) {
variable.doSomething();
}換行原則#
- 每行不超過 80-120 字元
- 在逗號後換行
- 在運算符前換行
- 新行與上一行同級表達式對齊
String result = calculateSomething(longParameterOne,
longParameterTwo, longParameterThree);
String value = longStringOne + longStringTwo
+ longStringThree;註釋的藝術#
註釋的三個麻煩#
- 難以維護 - 註釋不會編譯,容易過時
- 成為藉口 - 過度依賴註釋,忽略程式碼本身的可讀性
- 容易濫用 - 被用來注釋掉程式碼或寫俏皮話
最好的程式碼不需要註釋。如果需要註釋,說明程式碼還不夠清晰。
三種註釋風格#
1. 版權聲明
/*
* Copyright (c) 2024, Company Name. All rights reserved.
*/2. API 文件(Javadoc)
/**
* 計算兩數之和。
*
* @param a 第一個加數
* @param b 第二個加數
* @return 兩數之和
* @throws IllegalArgumentException 如果參數為負數
*/
public int add(int a, int b) { }3. 實作說明
// 使用二分搜尋提高效能
int index = Collections.binarySearch(list, target);註釋三原則#
| 原則 | 說明 |
|---|---|
| 準確 | 錯誤的註釋比沒有更糟 |
| 必要 | 多餘的註釋浪費閱讀時間 |
| 清晰 | 混亂的註釋讓程式碼更亂 |
不要在程式碼裡保留 TODO、除錯語句或注釋掉的程式碼。
聲明規範#
八項紀律#
取好名字 - 遵循命名規範
一行一個聲明 - 便於維護
// 差 int width, height; // 好 int width; int height;
3. **需要時再聲明** - 局部變數靠近使用處
4. **類屬性集中聲明** - 便於查找
5. **聲明時初始化** - 防止遺漏
6. **左括號不單獨成行** - Java 慣例
```java
public void method() { // 好
// ...
}- 小括號緊靠標識符 -
method()而非method () - 搜尋最佳化的換行 - 常見搜尋模式放同一行
類的內部結構#
推薦順序:
- 類屬性
- 構造方法
- 工廠方法
- 公開方法
- 私有方法
Java 註解的使用#
@Override#
重寫的方法必須加上
@Override註解。
好處:
- 明確標示這是重寫方法
- 父類修改時編譯器會報錯
class Student extends Person {
@Override
public String getFirstName() {
return super.getFirstName();
}
}@Deprecated#
及時廢棄不合理的 API:
/**
* @deprecated 使用 {@link #newMethod()} 代替
*/
@Deprecated(since = "2.0", forRemoval = true)
public void oldMethod() { }@SuppressWarnings#
盡量不要使用
@SuppressWarnings。警告是有價值的資訊。
例外處理#
例外分類#
| 類型 | 示例 | 處理方式 |
|---|---|---|
| Error | OutOfMemoryError | 不需處理 |
| RuntimeException | NullPointerException | 需記錄在文件 |
| Checked Exception | IOException | 必須宣告或捕獲 |
例外處理原則#
不要用例外處理正常流程
// 差 try { checkUserName(name); } catch (InvalidNameException e) { // 處理無效名稱 } // 好 if (isValidUserName(name)) { // 處理有效名稱 } else { // 處理無效名稱 }
2. **選擇精確的例外類型** - 不要用 `Exception`
3. **提供清晰的例外描述**
```java
throw new IllegalArgumentException(
"User ID must be positive, but was: " + userId);檔案組織#
專案結構#
project/
├── README # 軟體說明
├── COPYRIGHT # 版權資訊
├── LICENSE # 授權條款
├── src/ # 原始碼
│ └── com/example/
├── test/ # 測試程式碼
│ └── com/example/
└── docs/ # 文件原始碼檔案結構#
- 版權聲明
- package 宣告
- import 語句
- 類別 Javadoc
- 類別宣告
- 類別實作
程式碼規範檢查清單
命名
- 類別使用大駝峰
- 方法和變數使用小駝峰
- 常數使用全大寫蛇形
- 名稱準確表達意圖
格式
- 統一縮排風格
- 每行不超過 120 字元
- if/for/while 總是使用大括號
- 適當使用空行分隔邏輯塊
註釋
- 公開 API 有 Javadoc
- 沒有過時的註釋
- 沒有注釋掉的程式碼
聲明
- 一行一個聲明
- 變數靠近使用處聲明
- 重寫方法有 @Override
例外
- 文件記錄所有可能的例外
- 不用例外處理正常流程
- 例外訊息清晰明確