作為一個 Java 程序員,Javadoc 大家都應該寫過吧,是不是覺得寫的時候特簡單呢?相信看完本文后你會若有所思。另外,本文非常適合處女座程序員閱讀。
句號
為什么是句號而不是其他的標點符號呢?因為這涉及到一個 JDK 文檔生成的規則:
The first sentence of each doc comment should be a summary sentence, containing a concise but complete description of the API item. This means the first sentence of each member, class, interface or package description. (成員、類、接口或包注釋的第一個句子將作為該注釋項的總結,這個句子應該是言簡意賅的)
既然提到了句子,那就說明應該用標准的方式——句號來進行第一個句子的斷句識別。也許是為了簡潔,Javadoc 工具並沒有“多語言句號識別”這個特性,所以不管我們用什么語言撰寫文檔注釋,斷句符號都必須是英文狀態下的句號——.
比如在 NetBeans IDE 里(默認編輯器配置)可以很直觀的看到用於總結的第一個句子和后面描述的區別:
在生成 Javadoc 后,類概要頁面我們可以看到最終的效果:
在使用中文撰寫文檔注釋時,為了保持整體風格一致,在所有需要使用句號的地方有兩種策略可選擇:
- 都使用英文句號:這樣做可以讓生成的文檔句號統一,但缺點是看上去有點別扭
- 只有第一個句子使用英文句號,其余地方都是要中文句號:這樣做后生成的文檔看上去比較順眼,但別人可能會奇怪為什么第一個句號是 .
當然,最徹底的解決方案是不用中文寫文檔注釋,這樣就不存在要統一的問題了!
下面我們重點介紹各種你熟悉的或是不熟悉的 Javadoc 文檔標記(它們很有內涵)
@author
該標記使用頻率是所有文檔標記中最高的,我想這是因為:
- 做好事要留名
- 使用超簡單,就像在填表格(姓名: )一樣自然
來看看大牛怎么寫的:
(from Commons Lang 2.5 StringUtils)
總結下來有三種寫法:
- 純文本
- 帶郵箱鏈接
- 帶 HTTP 鏈接
(個人建議用 HTTP 鏈接:打碼時可以順便推廣一下自己的博客,哈哈)
另外,在 JDK 代碼中我們經常看到 @author unascribed,意思是:“該代碼第一原作者不是我,但我實在也不知道是誰,就記作無名氏吧”(這是多么嚴肅的一種版權意識啊)
@serialXXX
這個系列應該是最不常用的文檔標記了,它們到底是干嘛用的呢?請看這里。
(我一次也沒有使用過這些文檔標記,看了官方文檔后也還是沒有搞懂怎么用,求各位指教)
@value
這個文檔標記非常實用(不光好用),可以用於生成被標記的常量字段的值。
直接用於常量字段時:
也可以使用引用方式:
{@inheritDoc}
這個標簽體現了 Java 面向對象的精辟所在:不但可以類可以集成,連文檔都可以繼承(足見 Java 在經典面向對象概念上的完備與圓潤)。
比如有個計算面積的接口:
它的實現方法標注了 {@inheritDoc}(處女座閱讀提示:無 .):
最后生成的文檔:
- 基類的文檔注釋被繼承到了子類
- 子類可以再加入自己的注釋(特殊化擴展)
- @return @param @throws 也會被繼承
其實在不寫 {@inheritDoc} 的情況下也存在文檔注釋的繼承,具體規則請看這里。
{@link} {@linkplain}
這兩個鏈接標記大家用/見的應該比較多,但它們有什么區別、在什么場景下該怎么使用很少有人能夠區分開(我猜你要用的時候一般也都是用 link 吧)。
看看官網的標准解釋:
link 和 linkplain 的實參都是 package.class#member label 。唯一的不同就是因為字體不同,如果 label 是個純文本,那就使用 linkplain 吧。(根據這點,我嚴重懷疑 Javadoc 文檔標記的設計者是處女座,~ ~)
pre
沒錯,這就是那個 HTML 標簽,用於顯示“原始樣子”的。這個標簽在寫 Javadoc 的時候非常有用,用或者不使用在打碼的時候看上去差別不大:
但最終生成 apidocs 之后差別一目了然(處女座閱讀提示:在源碼文檔注釋中特別需要注意 pre 后 { 的位置,緊跟 *,無空格):
@since
這個從字面的意思上很好理解,所以使用的比較多(如同 @author、@version 一樣)。但問題是大家寫的時候表達的意思五花八門,常見的有:
- 想表達日期/時間
@since 2014-01-01
@since 2014-01-01 14:00:00 - 想表達可運行的 JDK 版本
@since JDK1.5 - 想表達加入這個元素的版本
@since 1.0.0
根據官方文檔解釋,@since 表達的是被標記元素是哪個發布版本引入的(3)。比如別人在我們的文檔注釋中看到
那他可以(應該)認為這個類是在該程序對外發布 1.0.0 版本時已經引入的。如果他要做二次開發,那他就可以很清晰的向后兼容了(我們在用 JDK 的時候就是這個場景)。
@version
提到了 @since 就自然會聯想到 @version,因為它們的實參都是版本相關的。@version 要表達的是被標記元素自己的版本(注意對比 @since),也就是說這個版本只要代碼改過就應該發生變化,而 @since 是不會變的。
官方文檔也解釋了怎么用好這個文檔標記:通過 SCCS 字符 "%I%, %G%",例如 1.39, 02/28/97(文件版本號, 日期)生成。但實際上很少有項目這么做(至少目前 Oracle JDK 沒這么做,甚至都沒有使用 @version,或者是使用了但最后由於特殊原因總體移除了),大家一般都是 @version 1.0.0 然后就再也不修改了,不管被標記的元素改了多少次(這樣的做法還不如不寫)。
當然,通過版本控制系統 hook 來做是比較經典的做法,不過這樣總感覺沒有把這個標記的能力完全發揮出來。在我們的項目里是這樣使用的:@version 1.2.3.4, Jun 9, 2014
重點是版本號部分,在這個例子中從左到右(1.2.3.4)分別表示:
- 兼容性位 1,表示兼容性,如果 +1 了說明這個修改是不兼容的
- 特性位 2,表示已引入了兩個特性,每次 +1 說明引入一個新特性
- 缺陷修復位 3,表示已經修復了 3 個缺陷,每次 +1 說明修復了一個缺陷
- 重構位 4,表示已經進行了 4 次重構,每次 +1 說明重構了一次
前面 3 位表達的意義和 Semantic Versioning 建議的一致,重構我覺得非常重要,所以也加了進來。
@exception @throws
這兩兄弟的情況比 @link @linkplain 更糾結(人家 link 兄弟最起碼可以區分出來使用場景)。按照官方文檔解釋:它們完全是同義詞,沒有任何區分。那當年 Sun 在 JDK1.2 的時候為什么要加入 @throws 呢——答案是起名失誤了,詞性沒弄匹配:@throws Exception 比 @exception Exception 更符合語法,代入感更好!(細節:In javadoc, what is the difference between the tags @throws and @exception?)
標記總表
來張 Javadoc 文檔標記總表:
Tag | Introduced in JDK/SDK |
---|---|
@author |
1.0 |
{@code} |
1.5 |
{@docRoot} |
1.3 |
@deprecated |
1.0 |
@exception |
1.0 |
{@inheritDoc} |
1.4 |
{@link} |
1.2 |
{@linkplain} |
1.4 |
{@literal} |
1.5 |
@param |
1.0 |
@return |
1.0 |
@see |
1.0 |
@serial |
1.2 |
@serialData |
1.2 |
@serialField |
1.2 |
@since |
1.1 |
@throws |
1.2 |
{@value} |
1.4 |
@version |
1.0 |
這個表是 JDK7 技術手冊里的,從中我們可以看出,自 JDK1.5 以后就沒有加過新的文檔標記了,目測有兩個原因:
- Oracle:“這些已經足夠開發人員使用了,沒必要加新的了”
- *Sun:“看吧,Oracle 嚴重缺乏折騰精神,當初不應該賣給它的”
文檔標記介紹完了,下面我們來聊聊 Javadoc 相關的其他侃點。
getter/setter/isTrue
對於 POJO 來說,這幾個方法的注釋格式非常固定,一般我們都是用 IDE 自動生成:這樣的話別人一看到這樣固定格式的注釋(或者索性不要添加任何注釋)就知道這部分相對於其他部分並不重要,而一旦有的 getter/setter/isTrue 注釋不是這樣約定的,那就說明了實現上面不只是簡單的 get/set/is,還加入了額外的邏輯處理。
對齊
Javadoc 文檔注釋也有對齊(不是前面 pre 例子那種),這里說的對齊主要指的是以源碼視圖看到的,最典型的場景就是在給方法添加文檔注釋的時候,我們經常看到兩種風格:
第一眼看上去是不是風格 2 要順眼得多?但最好還是使用風格 1,因為:
- 這和編輯器配置的字體有關,如果不是(適合的)等寬字體,那會非常的參差不齊
- 浪費空間,特別是當注釋內容多了需要換行的時候會很別扭
- 最后生成的 apidocs 效果是一模一樣的(無對齊)
(一些 IDE 默認格式化文檔注釋的時候也是使用風格 1 進行格式化的,強烈建議使用風格 1)
包注釋
和前面幾點打碼風格相關的細節比起來,包注釋是具有一定的實用性的。雖然大家可能用得很少,但看得應該比較多,就是這部分:
這里我們使用了兩種方式來生成包文檔:
- package.html:這是 JDK1.5 以前的方式,現在已經不推薦使用
- package-info.java:目前推薦方式,因為這樣可以使用注解
在包上面使用注解?這個用法和在其他地方使用注解一樣,只是被標注的元素變成了包,在運行時可以獲取到包的注解,然后做你想做的事情吧!
中文
一開始我們提到了句號的問題(那的確是一個問題),最后我們來看看中文在寫文檔注釋的時候也非常值得注意的一點(其實不只是 Javadoc 文檔注釋,該建議也適用於其他一些情況):在中文和英文、數字中間插入一個空格(本文就是這樣排版的)。
比如說:
- 我覺得Java非常cool,特別是JDK8中的lambda,真希望9能帶來更多實用特性
- 我覺得 Java 非常 cool,特別是 JDK8 中的 lambda,真希望 9 能帶來更多實用特性
后者看上去就比前者更舒服一些,這樣的排版方式適合純文本編輯器,如果使用的是 Office 之類的工具就不需要手動空格了,因為它們默認已經處理的很好了。
總結
本文介紹了一些 Javadoc 文檔注釋相關的細節,從這冰山一角相信你對 Java 也有了另一番體驗(Java 的進化、工業化)。
總結一下本文內容:
- 對於文檔標記,大家可以盡量嘗試使用:把自己的思想通過適合的方式表達給他人是一種好習慣
- 對於風格相關,大家也可以適當嘗試(處女座/強迫症就算了):某大廠在某次改句號問題后出現過生產故障
不過,大家也千萬不要太較真,畢竟對於一個好的程序來說,代碼應該就是它的文檔(之一)。
參考
- http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html
- http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html