編碼規範 - 養成良好的Java編碼習慣

恆宇少年發表於2018-06-23

最近在整理公司編碼規範方面的內容,2017阿里巴巴釋出了編碼規範外掛,強烈建議大家安裝使用,好的編碼習慣是通往成功的階梯。

技術群二維碼在底部,歡迎進群學習!!!

簡書整套文件以及原始碼解析

專題 專題名稱 專題描述
001 Spring Boot 核心技術 講解SpringBoot一些企業級層面的核心元件
002 Spring Cloud 核心技術 對Spring Cloud核心技術全面講解
003 QueryDSL 核心技術 全面講解QueryDSL核心技術以及基於SpringBoot整合SpringDataJPA
004 SpringDataJPA 核心技術 全面講解SpringDataJPA核心技術

###文件目錄

  • 註釋規範
    • 類註釋
    • 方法註釋
    • 行級註釋
    • DTO/Param註釋
  • 編碼規範
    • 命名風格
    • 常量定義
    • 程式碼格式
    • OOP 規約
    • 集合處理
    • 控制語句
  • 異常日誌規範
    • 異常處理
    • 日誌規約

###一、註釋規範

類註釋

類、類屬性使用Javadoc規範,類上描述該類的主要作用,註釋儘可能詳細,推薦把使用該類地方使用@see註解進行標註,類屬性詳細描述該屬性的儲存內容。 類註釋示例

/**
 * 統一資源Aop切面定義
 * 根據自定義註解配置自動設定配置的資源型別到指定的欄位
 * @author:於起宇 <br/>
 * ===============================
 * Created with IDEA.
 * Date:2017/12/15
 * Time:14:05
 * ================================
 */
複製程式碼

類屬性註釋示例

/**
 * 資源處理業務邏輯
 */
Autowired
ResourcePushService resourcePushService;
複製程式碼

方法註釋

介面方法、實現類方法、抽象方法等,詳細描述該方法主要作用,儘可能的描述出方法的主要流程步驟,方法定義的每一個引數都需要有詳細的註釋描述,建議新增方法返回值描述。 方法註釋示例

    /**
     * 資源設定切面方法
     * 攔截配置了@ResourceMethod註解的class method
     * cglib僅支援class 方法切面,介面切面不支援
     * @param proceedingJoinPoint 切面方法例項
     * @param resourceMethod 方法註解例項
     * @return 原方法返回物件
     */
    @Around(value = "@annotation(resourceMethod)")
    public Object resourcePutAround(ProceedingJoinPoint proceedingJoinPoint, ResourceMethod resourceMethod)
        throws Throwable
    {
		//...
	}
複製程式碼

如果是業務邏輯方法註釋建議新增步驟,如下所示:

    /**
     * 建立帖子
     * - 轉換引數實體為
     * - 儲存帖子基本資訊
     * @param param 建立帖子請求引數實體
     * @return 帖子編號
     * @throws LogicException
     */
    public String create(CreateTopicParam param) throws LogicException {
		//...
	}
複製程式碼

在上面程式碼中-分隔符代表主要執行步驟,每一個步驟以-分隔符開始,如果方法記憶體在邏輯分支處理,請看下面行註釋

行註釋

行級註釋一般都是方法內使用到,分為單行註釋、多行註釋,單行註釋採用//設定,多行註釋採用/* */設定,如下所示: 單行註釋

// 執行方法,獲取返回值
複製程式碼

多行註釋

/*
 * 執行方法,獲取返回值
 * 獲取返回值進行後續邏輯處理
 */
複製程式碼

DTO/Param註釋

我們在實際開發過程中資料庫對應的實體是不允許直接拿出來新增一些附加欄位的,也就是禁止新增非該資料表對應實體內的欄位,這種情況我們需要定義DTO/Param

DTO註釋

DTO是資料返回實體定義,如果我們在查詢資料庫時需要關聯其他表的資料並且返回給前端,那麼我們可以建立XxxDTO,注意:DTO全部大寫,只需要繼承查詢邏輯的主表實體就可以完成附加欄位的新增,要為每一個附加欄位新增javadoc詳細註釋,如下所示:

/**
 * 帖子列表資料轉換實體
 * @author:於起宇 <br/>
 * ===============================
 * Created with IDEA.
 * Date:2018/1/8
 * Time:10:52
 * ================================
 */
@Data
public class TopicListDTO
        extends CommunicationTopicInfoEntity {

    /**
     * 使用者暱稱
     */
    private String userNickName;
    /**
     * 所屬機構名稱
     */
    private String orgName;
    //...
}
複製程式碼
Param註釋

對於介面、後臺來說接受請求時一般都是帶著一些引數,目前我們系統是前臺完全分離,所以後臺其實變相的也是介面,在上面DTO也有說到資料實體不允許新增附加引數,我們的引數也不可能都是資料實體內的欄位,這時需要建立對應的引數實體XxxParam,引數實體內的所有欄位都需要新增javadoc註釋,如下所示:

/**
 * 查詢帖子列表
 * - 用於查詢自己、他人、關鍵字、首頁帖子請求引數
 *
 * @author:於起宇 <br/>
 * ===============================
 * Created with IDEA.
 * Date:2018/1/8
 * Time:10:31
 * ================================
 */
@Data
public class SelectTopicParam extends PagerParam {
    /**
     * 使用者編號
     */
    private String userId;
    /**
     * 查詢關鍵字
     */
    @Length(max = 30)
    private String keyWord;
}
複製程式碼

二、編碼規範

命名風格

  1. 程式碼中命名不能以下劃線或美元符號開始,也不能以下劃線或美元符合結束。

錯誤示例:

_name / name_ / $name / name$ / __name / name__
複製程式碼
  1. 命名嚴禁出現中文拼音與英文混合方式出現,不允許直接使用中文方式命名

錯誤示例

WenZhang[文章] / WenZhangInfo[文章資訊] / getTopicLieBiao()[獲取文章列表] / int 數量 = 0
複製程式碼
  1. 類名使用UpperCamelCase風格,DTOVO除外

錯誤示例

QRCode / UserInfoDto / XMLService
複製程式碼

正確示例

QrCode / UserInfoDTO / XmlService
複製程式碼
  1. 方法名、引數名、成員變數、區域性變數統一使用lowerCamelCase風格,必須使用駝峰命名方式。

錯誤示例

localvalue / GetUserInfo() / userid
複製程式碼

正確示例

localValue / getUserInfo() / userId
複製程式碼
  1. 常量命名全部大寫,單詞間下劃線隔開,完整的表達含義,名字可以過長。

錯誤示例

MAX_COUNT / NAME
複製程式碼

正確示例

MAX_STOCK_COUNT / DEFAULT_ORG_NAME
複製程式碼
  1. 抽象類命名使用Abstract或者Base開頭,異常類命名使用Exception結尾,測試類命名時以將要測試的類全名 + Test

正確示例

AbstractCodeMessageService / BaseCodeMessageService / LogicException / UserControllerTest
複製程式碼
  1. 包名統一使用小寫,每個分隔符必須為自然語義的英文單詞,另外包名統一使用單數含義,如果需要複數含義,則可以在類名上體現。

正確示例

com.sanmi.framework.core / com.sanmi.framework.orm
複製程式碼
  1. 杜絕不規範的縮寫,避免詞義表達不清楚。

錯誤示例

AbstractClass = > AbsClass
condition => condi
複製程式碼
  1. 介面中的方法和屬性不要新增任何修飾符(public也不要新增),為了保持程式碼的簡潔性,加上有效的javadoc註釋。

錯誤示例

public abstract void commit();
複製程式碼

正確示例

void commin(); / String COMPANY_NAME = "sanmi";
複製程式碼
  1. 對於ServiceDAO類命名時實現類字尾要以Impl結尾,Mapper介面不存在實現類,無需處理。

正確示例

UserInfoService / UserInfoServiceImpl
複製程式碼
  1. 列舉類名統一帶上Enum字尾,列舉成員名稱需要全大寫,單詞間使用下劃線隔開。

正確示例

列舉名稱 => UserStatusEnum
成員名稱 => ENABLE / DISABLED / DELETE
複製程式碼
  1. 各層方法命名規約 Service/DAO/Mapper層方法命名規約如下:
    • 獲取單個物件的方法用get作為字首
    • 獲取多個物件的方法用list作為字首
    • 獲取統計值的方法用count作為字首
    • 插入方法用save / insert作為字首
    • 刪除方法用delete / remove作為字首
    • 修改方法用update作為字首

常量定義

  1. 不允許任何魔法值(未經過預先定義的常量)直接出現在程式碼中

錯誤示例

if("enable".equals(user.getStatus())) { 
   //.. 
}
複製程式碼
  1. 使用封裝型別代替基本資料型別

正確示例

Integer => int
Double => double
Long => long
...
複製程式碼
  1. 如果常量僅在一個範圍內變化,使用enum來代替定義

正確示例

@Getter
public enum  UserBzEnum
{
    /**
     * 普通使用者
     */
    APP_USER("0"),
    /**
     * 月嫂
     */
    NANNY("1"),
    /**
     * 醫生
     */
    DOCTOR("2")
    ;
    private String value;

    UserBzEnum(String value) {
        this.value = value;
    }
}
複製程式碼

程式碼格式

  1. 大括號的使用約定,如果大括號內為空,直接使用{}即可,不需要換行;如果非空程式碼,則需要:
    • 左大括號前不換行
    • 左大括號後換行
    • 右大括號換行
    • 右大括號後還有else等程式碼則不換行;表示終止的右大括號後必須換行。
  2. 左小括號和字元之間不出現空格;右小括號和字元之間也不出現空格。 錯誤示例
if ( a == b )
複製程式碼
  1. if / for / while / switch / do 等保留字與括號之間都必須加空格
  2. 任何二目、三目運算子的左右兩邊都需要加一個空格。

正確示例

 if (ValidateTools.isEmpty(uploadBackEntity) || ValidateTools.isEmpty(uploadBackEntity.getUploadUrl())) {
 //...
        }
複製程式碼
  1. 採用4個空格縮排,禁止使用Tab控制符。
  2. 行級註釋的//與註釋內容之間有且僅有一個空格。

正確示例

// 定義使用者名稱
String userName = user.getName();
複製程式碼
  1. 單行字元不超過120個,超過需要換行,換行原則如下:
    • 第二行相對於第一行縮排4哥空格,從第三行開始不再進行縮排
    • 運算子一起換行(如:+)
    • .符號一起換行
    • 方法呼叫中的多個引數需要換行時,在逗號後進行。

正確示例

StringBuffer buffer = new StringBuffer();
// 超過120個字元的情況下,換行縮排4個空格,``.``和方法名一塊換行
buffer.append("san").append("mi") ...
        .append("ke")
        .append("ji");
// 引數過多超120個字元時,在 , 後換行
method(args1, args2, args3, ...
args98, args99
)        
複製程式碼
  1. 方法引數在定義和傳入時,多個引數後面必須加空格。 正確示例
method(args1, args2);
複製程式碼
OOP 規約
  1. 避免通過一個類的物件引用訪問此類的靜態變數或者靜態方法,會造成編譯器解析成本,直接用類名訪問即可。【JVM堆、棧、靜態程式碼塊解析成本是不一樣的】
  2. 所有覆蓋方法,必須新增@Overrider註解
  3. 對外部正在呼叫或者二方庫依賴的介面,不允許修改方法簽名,以避免對介面呼叫方產生影響;如果介面已經過時,必須新增@Deprecated註解,並清晰的說明替代介面或者替代服務。
  4. 禁止使用過時類或方法。
  5. Objectequals方法容易丟擲空指標異常,應使用常量或確定值的物件來呼叫equals方法。

錯誤示例

object.equals("test");
複製程式碼

正確示例

"test".equals(object)
複製程式碼
  1. 所有相同型別的封裝類之間比較,必須使用equals方法。

說明int封裝類Integer-128 ~ 127範圍內的賦值會在IntegerCache.cache中產生,該區間的值可以直接使用==進行比對,但是該區間外的值都會以引用型別在內建立,物件之間是無法使用==來進行比對的! 7. 建構函式內禁止編寫任何業務邏輯,如果有業務邏輯請建立init方法使用。

集合處理
  1. 使用集合轉陣列的方法,必須使用集合的toArray(T[] array),傳入的是型別完全一樣的陣列,大小則是list.size()

正確示例

List<String< list = new ArrayList();
list.add("jinan");
list.add("sanmi");
// 生成list長度的陣列例項
String[] array = new String[list.size()];
// 執行集合轉換資料
array = list.toArray(array);
複製程式碼
  1. 在使用工具類Arrays.asList()方法時,不能使用修改集合相關的方法,add / remove / clean方法會丟擲異常。
  2. 禁止在foreach迴圈內進行元素的remove / add操作,remove元素請使用Iterator方式,如果併發操作,需要對Iterator物件加鎖。

正確示例

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
	String item = iterator.next();
	if (condition) {
		iterator.remove();
	}
}
複製程式碼
控制語句
  1. 在一個switch內,每個case要麼使用break / return來終止,要麼註釋說明程式將繼續執行到具體的哪個case為止;在一個switch內必須包含default程式碼塊在所有case之後,就算業務邏輯為空也要存在!
  2. if / else / for / while / do語句中必須使用大括號,即便是隻有一行程式碼,也需要新增大括號。

錯誤示例

if (condition) statements;
複製程式碼

正確示例

if (condition) {
	statements;
}
複製程式碼
  1. 在表達異常分支時,儘可能的少用if / else if巢狀方式,可以修改成:
if (condition) {
	//...
	return object;
}
// 繼續處理 else / else if 業務邏輯程式碼
複製程式碼

如果不得不使用超過三層的if / else if邏輯判斷,可以將程式碼改成衛語句策略設計模式狀態設計模式,下面是衛語句示例:

public void today() {
	if (isBuy()) {
		System.out.println("do something.");
		return;
	}
	if (isFree()) {
		System.out.println("do something.");
		return;
	}
	System.out.println("coding..");
}
複製程式碼

在我們系統設計時,一般都會使用throw new XxxException();return;代替,所有邏輯異常判斷都採用自定義業務邏輯異常進行處理。

三、異常日誌規範

異常規約
  1. Java 類庫中定義的可以預判斷來規避RuntimeException,不應該採用try {} catch(Exception e){}來處理。

錯誤示例

// 直接使用不確定物件
object.setXxx(value);
複製程式碼

正確示例

// 判斷非空後使用不確定物件
if (object != null) {
	//...
}
複製程式碼
  1. 捕獲異常目的是為了處理它,不要捕獲了卻什麼都不處理而拋棄,如果不想處理它,請將該異常拋給它的呼叫者,最外層的業務使用者必須處理異常,將異常資訊轉換成使用者可以理解的提示資訊。
  2. try程式碼塊放到了事務程式碼中,catch異常後,如果需要回滾事務,一定要注意rollback事務。
  3. finally程式碼塊必須對資源物件、劉物件進行關閉操作,即使有異常也要做try-catch操作。
  4. 不能在finally程式碼塊中使用return
  5. 業務邏輯異常請交付給框架處理,我們將業務邏輯驗證使用業務邏輯異常處理的機制進行拋給框架處理。

正確示例

if (condition) {
	throw new LogicException(ErrorCodeEnum.USER_NOT_FOUND);
}
複製程式碼
日誌規約
  1. 程式碼中不可直接使用日誌系統(Log4j、Logback)中的API,而依賴使用日誌框架SLF4j中的API。使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一。 正確示例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 獲取日誌物件
private static final Logger logger = LoggerFactory.getLogger(Xxx.class);
複製程式碼

日誌物件定義一般都會在專案框架搭建初期定義,直接使用即可,如果需要自定義,則按照上面的方式進行宣告日誌物件。 2. 對trace / debug / info級別的日誌輸出,必須使用佔位符的方式,如果不使用佔位符而是直接拼接,可能會導致變數為null導致系統異常,還一點日誌等級不匹配時雖然不會列印,但是會執行字串的拼接,浪費伺服器系統資源。

正確示例

logger.debug("執行查詢使用者:{},基本資訊。",user.getId());
複製程式碼
  1. 針對方法的主要引數需要列印對應的值,方便後期日誌除錯專案。

正確示例

public void today(String userId) {
	logger.debug("查詢使用者:{},今日任務完成進度",userId);
}
複製程式碼
  1. 方法內分支邏輯需要列印對應的日誌資訊,方便後期日誌除錯專案。

正確示例

if (condition1) {
	logger.debug("do something.");
} else if (condition2) {
	logger.debug("do other thing.");
}
複製程式碼
  1. 系統異常包括兩大類:案發現場異常堆疊資訊.

正確示例

logger.error(引數或物件.toString() + "_" + e.getMessage(), e);
複製程式碼

寫在最後

強烈建議IDEA開發工具安裝使用阿里巴巴國際編碼規約外掛,為良好的編碼習慣打下基礎。 歡迎加入QQ技術交流群,共同進步。

QQ技術交流群

相關文章