天天看點

Google Java 程式設計風格指南

這份文檔是Google Java程式設計風格規範的完整定義。當且僅當一個Java源檔案符合此文檔中的規則, 我們才認為它符合Google的Java程式設計風格。

與其它的程式設計風格指南一樣,這裡所讨論的不僅僅是編碼格式美不美觀的問題, 同時也讨論一些約定及編碼标準。然而,這份文檔主要側重于我們所普遍遵循的規則, 對于那些不是明确強制要求的,我們盡量避免提供意見。

<a href="https://dn-biezhi.qbox.me/2015/09/55862257.png"></a>

在本文檔中,除非另有說明:

術語class可表示一個普通類,枚舉類,接口或是annotation類型(<code>@interface</code>)

術語comment隻用來指代實作的注釋(implementation comments),我們不使用“documentation comments”一詞,而是用Javadoc。

其他的術語說明會偶爾在後面的文檔出現。

本文檔中的示例代碼并不作為規範。也就是說,雖然示例代碼是遵循Google程式設計風格,但并不意味着這是展現這些代碼的唯一方式。 示例中的格式選擇不應該被強制定為規則。

源檔案以其最頂層的類名來命名,大小寫敏感,檔案擴充名為<code>.java</code>。

源檔案編碼格式為UTF-8。

除了行結束符序列,ASCII水準空格字元(0×20,即空格)是源檔案中唯一允許出現的空白字元,這意味着:

所有其它字元串中的空白字元都要進行轉義。

制表符不用于縮進。

對于剩餘的非ASCII字元,是使用實際的Unicode字元(比如∞),還是使用等價的Unicode轉義符(比如\u221e),取決于哪個能讓代碼更易于閱讀和了解。

Tip: 在使用Unicode轉義符或是一些實際的Unicode字元時,建議做些注釋給出解釋,這有助于别人閱讀和了解。

例如:

Tip: 永遠不要由于害怕某些程式可能無法正确處理非ASCII字元而讓你的代碼可讀性變差。當程式無法正确處理非ASCII字元時,它自然無法正确運作, 你就會去fix這些問題的了。(言下之意就是大膽去用非ASCII字元,如果真的有需要的話)

一個源檔案包含(按順序地):

許可證或版權資訊(如有需要)

package語句

import語句

一個頂級類(隻有一個)

以上每個部分之間用一個空行隔開。

如果一個檔案包含許可證或版權資訊,那麼它應當被放在檔案最前面。

package語句不換行,列限制(4.4節)并不适用于package語句。(即package語句寫在一行裡)

即,不要出現類似這樣的import語句:<code>import java.util.*;</code>

import語句不換行,列限制(4.4節)并不适用于import語句。(每個import語句獨立成行)

import語句可分為以下幾組,按照這個順序,每組由一個空行分隔:

所有的靜态導入獨立成組

<code>com.google</code> imports(僅當這個源檔案是在<code>com.google</code>包下)

第三方的包。每個頂級包為一組,字典序。例如:android, com, junit, org, sun

<code>java</code> imports

<code>javax</code> imports

組内不空行,按字典序排列。

每個頂級類都在一個與它同名的源檔案中(當然,還包含<code>.java</code>字尾)。

例外:<code>package-info.java</code>,該檔案中可沒有<code>package-info</code>類。

類的成員順序對易學性有很大的影響,但這也不存在唯一的通用法則。不同的類對成員的排序可能是不同的。 最重要的一點,每個類應該以某種邏輯去排序它的成員,維護者應該要能解釋這種排序邏輯。比如, 新的方法不能總是習慣性地添加到類的結尾,因為這樣就是按時間順序而非某種邏輯來排序的。

當一個類有多個構造函數,或是多個同名方法,這些函數/方法應該按順序出現在一起,中間不要放進其它函數/方法。

術語說明:塊狀結構(block-like construct)指的是一個類,方法或構造函數的主體。需要注意的是,數組初始化中的初始值可被選擇性地視為塊狀結構(4.8.3.1節)。

大括号與<code>if, else, for, do, while</code>語句一起使用,即使隻有一條語句(或是空),也應該把大括号寫上。

左大括号前不換行

左大括号後換行

右大括号前換行

如果右大括号是一個語句、函數體或類的終止,則右大括号後換行; 否則不換行。例如,如果右大括号後面是else或逗号,則不換行。

示例:

4.8.1節給出了enum類的一些例外。

一個空的塊狀結構裡什麼也不包含,大括号可以簡潔地寫成<code>{}</code>,不需要換行。例外:如果它是一個多塊語句的一部分(if/else 或 try/catch/finally) ,即使大括号内沒内容,右大括号也要換行。

<code>void doNothing() {}</code>

每當開始一個新的塊,縮進增加2個空格,當塊結束時,縮進傳回先前的縮進級别。縮進級别适用于代碼和注釋。(見4.1.2節中的代碼示例)

每個語句後要換行。

一個項目可以選擇一行80個字元或100個字元的列限制,除了下述例外,任何一行如果超過這個字元數限制,必須自動換行。

例外:

不可能滿足列限制的行(例如,Javadoc中的一個長URL,或是一個長的JSNI方法參考)。

<code>package</code>和<code>import</code>語句(見3.2節和3.3節)。

注釋中那些可能被剪切并粘貼到shell中的指令行。

術語說明:一般情況下,一行長代碼為了避免超出列限制(80或100個字元)而被分為多行,我們稱之為自動換行(line-wrapping)。

我們并沒有全面,确定性的準則來決定在每一種情況下如何自動換行。很多時候,對于同一段代碼會有好幾種有效的自動換行方式。

Tip: 提取方法或局部變量可以在不換行的情況下解決代碼過長的問題(是合理縮短命名長度吧)

自動換行的基本準則是:更傾向于在更高的文法級别處斷開。

如果在<code>非指派運算符</code>處斷開,那麼在該符号前斷開(比如+,它将位于下一行)。注意:這一點與Google其它語言的程式設計風格不同(如C++和JavaScript)。 這條規則也适用于以下“類運算符”符号:點分隔符(.),類型界限中的&amp;(<code>&lt;T extends Foo&amp; Bar&gt;</code>),catch塊中的管道符号(<code>catch (FooException | BarException e</code>)

如果在<code>指派運算符</code>處斷開,通常的做法是在該符号後斷開(比如=,它與前面的内容留在同一行)。這條規則也适用于<code>foreach</code>語句中的分号。

方法名或構造函數名與左括号留在同一行。

逗号(,)與其前面的内容留在同一行。

自動換行時,第一行後的每一行至少比第一行多縮進4個空格(注意:制表符不用于縮進。見2.3.1節)。

當存在連續自動換行時,縮進可能會多縮進不隻4個空格(文法元素存在多級時)。一般而言,兩個連續行使用相同的縮進當且僅當它們開始于同級文法元素。

第4.6.3水準對齊一節中指出,不鼓勵使用可變數目的空格來對齊前面行的符号。

以下情況需要使用一個空行:

類内連續的成員之間:字段,構造函數,方法,嵌套類,靜态初始化塊,執行個體初始化塊。

例外:兩個連續字段之間的空行是可選的,用于字段的空行主要用來對字段進行邏輯分組。

在函數體内,語句的邏輯分組間使用空行。

類内的第一個成員前或最後一個成員後的空行是可選的(既不鼓勵也不反對這樣做,視個人喜好而定)。

要滿足本文檔中其他節的空行要求(比如3.3節:import語句)

多個連續的空行是允許的,但沒有必要這樣做(我們也不鼓勵這樣做)。

除了語言需求和其它規則,并且除了文字,注釋和Javadoc用到單個空格,單個ASCII空格也出現在以下幾個地方:

分隔任何保留字與緊随其後的左括号(<code>(</code>)(如<code>if, for catch</code>等)。

分隔任何保留字與其前面的右大括号(<code>}</code>)(如<code>else, catch</code>)。

在任何左大括号前(<code>{</code>),兩個例外:

<code>@SomeAnnotation({a, b})</code>(不使用空格)。

<code>String[][] x = foo;</code>(大括号間沒有空格,見下面的Note)。

在任何二進制或三元運算符的兩側。這也适用于以下“類運算符”符号:

類型界限中的&amp;(<code>&amp;lt;T extends Foo &amp;amp; Bar&amp;gt;</code>)。

catch塊中的管道符号(<code>catch (FooException | BarException e</code>)。

<code>foreach</code>語句中的分号。

在<code>, : ;</code>及右括号(<code>)</code>)後

如果在一條語句後做注釋,則雙斜杠(//)兩邊都要空格。這裡可以允許多個空格,但沒有必要。

類型和變量之間:List list。

數組初始化中,大括号内的空格是可選的,即<code>new int[] {5, 6}</code>和<code>new int[] { 5, 6 }</code>都是可以的。

Note:這個規則并不要求或禁止一行的開關或結尾需要額外的空格,隻對内部空格做要求。

術語說明:水準對齊指的是通過增加可變數量的空格來使某一行的字元與上一行的相應字元對齊。

這是允許的(而且在不少地方可以看到這樣的代碼),但Google程式設計風格對此不做要求。即使對于已經使用水準對齊的代碼,我們也不需要去保持這種風格。

以下示例先展示未對齊的代碼,然後是對齊的代碼:

Tip:對齊可增加代碼可讀性,但它為日後的維護帶來問題。考慮未來某個時候,我們需要修改一堆對齊的代碼中的一行。 這可能導緻原本很漂亮的對齊代碼變得錯位。很可能它會提示你調整周圍代碼的空白來使這一堆代碼重新水準對齊(比如程式員想保持這種水準對齊的風格), 這就會讓你做許多的無用功,增加了reviewer的工作并且可能導緻更多的合并沖突。

除非作者和reviewer都認為去掉小括号也不會使代碼被誤解,或是去掉小括号能讓代碼更易于閱讀,否則我們不應該去掉小括号。 我們沒有理由假設讀者能記住整個Java運算符優先級表。

枚舉常量間用逗号隔開,換行可選。

沒有方法和文檔的枚舉類可寫成數組初始化的格式:

<code>private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }</code>

由于枚舉類也是一個類,是以所有适用于其它類的格式規則也适用于枚舉類。

不要使用組合聲明,比如<code>int a, b;</code>。

不要在一個代碼塊的開頭把局部變量一次性都聲明了(這是c語言的做法),而是在第一次需要使用它時才聲明。 局部變量在聲明時最好就進行初始化,或者聲明後盡快進行初始化。

數組初始化可以寫成塊狀結構,比如,下面的寫法都是OK的:

中括号是類型的一部分:<code>String[] args</code>, 而非<code>String args[]</code>。

術語說明:switch塊的大括号内是一個或多個語句組。每個語句組包含一個或多個switch标簽(<code>case FOO:</code>或<code>default:</code>),後面跟着一條或多條語句。

與其它塊狀結構一緻,switch塊中的内容縮進為2個空格。

每個switch标簽後新起一行,再縮進2個空格,寫下一條或多條語句。

在一個switch塊内,每個語句組要麼通過<code>break, continue, return</code>或抛出異常來終止,要麼通過一條注釋來說明程式将繼續執行到下一個語句組, 任何能表達這個意思的注釋都是OK的(典型的是用<code>// fall through</code>)。這個特殊的注釋并不需要在最後一個語句組(一般是<code>default</code>)中出現。示例:

每個switch語句都包含一個<code>default</code>語句組,即使它什麼代碼也不包含。

注解緊跟在文檔塊後面,應用于類、方法和構造函數,一個注解獨占一行。這些換行不屬于自動換行(第4.5節,自動換行),是以縮進級别不變。例如:

<code>@Override @Nullable public String getNameIfPresent() { ... }</code>

例外:單個的注解可以和簽名的第一行出現在同一行。例如:

<code>@Override public int hashCode() { ... }</code>

應用于字段的注解緊随文檔塊出現,應用于字段的多個注解允許與字段出現在同一行。例如:

<code>@Partial @Mock DataLoader loader;</code>

參數和局部變量注解沒有特定規則。

塊注釋與其周圍的代碼在同一縮進級别。它們可以是<code>/* ... */</code>風格,也可以是<code>// ...</code>風格。對于多行的<code>/* ... */</code>注釋,後續行必須從<code>*</code>開始, 并且與前一行的<code>*</code>對齊。以下示例注釋都是OK的。

注釋不要封閉在由星号或其它字元繪制的架構裡。

Tip:在寫多行注釋時,如果你希望在必要時能重新換行(即注釋像段落風格一樣),那麼使用<code>/* ... */</code>。

類和成員的modifiers如果存在,則按Java語言規範中推薦的順序出現。

辨別符隻能使用ASCII字母和數字,是以每個有效的辨別符名稱都能比對正規表達式<code>\w+</code>。

在Google其它程式設計語言風格中使用的特殊字首或字尾,如<code>name_</code>, <code>mName</code>, <code>s_name</code>和<code>kName</code>,在Java程式設計風格中都不再使用。

包名全部小寫,連續的單詞隻是簡單地連接配接起來,不使用下劃線。

類名都以<code>UpperCamelCase</code>風格編寫。

類名通常是名詞或名詞短語,接口名稱有時可能是形容詞或形容詞短語。現在還沒有特定的規則或行之有效的約定來命名注解類型。

測試類的命名以它要測試的類的名稱開始,以<code>Test</code>結束。例如,<code>HashTest</code>或<code>HashIntegrationTest</code>。

方法名都以<code>lowerCamelCase</code>風格編寫。

方法名通常是動詞或動詞短語。

下劃線可能出現在JUnit測試方法名稱中用以分隔名稱的邏輯元件。一個典型的模式是:<code>test&amp;lt;MethodUnderTest&amp;gt;_&amp;lt;state&amp;gt;</code>,例如<code>testPop_emptyStack</code>。 并不存在唯一正确的方式來命名測試方法。

常量名命名模式為<code>CONSTANT_CASE</code>,全部字母大寫,用下劃線分隔單詞。那,到底什麼算是一個常量?

每個常量都是一個靜态final字段,但不是所有靜态final字段都是常量。在決定一個字段是否是一個常量時, 考慮它是否真的感覺像是一個常量。例如,如果任何一個該執行個體的觀測狀态是可變的,則它幾乎肯定不會是一個常量。 隻是永遠不<code>打算</code>改變對象一般是不夠的,它要真的一直不變才能将它示為常量。

這些名字通常是名詞或名詞短語。

非常量字段名以<code>lowerCamelCase</code>風格編寫。

參數名以<code>lowerCamelCase</code>風格編寫。

參數應該避免用單個字元命名。

局部變量名以<code>lowerCamelCase</code>風格編寫,比起其它類型的名稱,局部變量名可以有更為寬松的縮寫。

雖然縮寫更寬松,但還是要避免用單字元進行命名,除了臨時變量和循環變量。

即使局部變量是final和不可改變的,也不應該把它示為常量,自然也不能用常量的規則去命名它。

類型變量可用以下兩種風格之一進行命名:

單個的大寫字母,後面可以跟一個數字(如:E, T, X, T2)。

以類命名方式(5.2.2節),後面加個大寫的T(如:RequestT, FooBarT)。

名字從<code>散文形式</code>(prose form)開始:

把短語轉換為純ASCII碼,并且移除任何單引号。例如:”Müller’s algorithm”将變成”Muellers algorithm”。

把這個結果切分成單詞,在空格或其它标點符号(通常是連字元)處分割開。

推薦:如果某個單詞已經有了常用的駝峰表示形式,按它的組成将它分割開(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一個真正的駝峰表示形式,是以該推薦對它并不适用。

現在将所有字母都小寫(包括縮寫),然後将單詞的第一個字母大寫:

每個單詞的第一個字母都大寫,來得到大駝峰式命名。

除了第一個單詞,每個單詞的第一個字母都大寫,來得到小駝峰式命名。

最後将所有的單詞連接配接起來得到一個辨別符。

加星号處表示可以,但不推薦。

Note:在英語中,某些帶有連字元的單詞形式不唯一。例如:”nonempty”和”non-empty”都是正确的,是以方法名<code>checkNonempty</code>和<code>checkNonEmpty</code>也都是正确的。

隻要是合法的,就把<code>@Override</code>注解給用上。

除了下面的例子,對捕獲的異常不做響應是極少正确的。(典型的響應方式是列印日志,或者如果它被認為是不可能的,則把它當作一個<code>AssertionError</code>重新抛出。)

如果它确實是不需要在catch塊中做任何響應,需要做注釋加以說明(如下面的例子)。

例外:在測試中,如果一個捕獲的異常被命名為<code>expected</code>,則它可以被不加注釋地忽略。下面是一種非常常見的情形,用以確定所測試的方法會抛出一個期望中的異常, 是以在這裡就沒有必要加注釋。

使用類名調用靜态的類成員,而不是具體某個對象或表達式。

極少會去重載<code>Object.finalize</code>。

Javadoc塊的基本格式如下所示:

或者是以下單行形式:

<code>/** An especially short bit of Javadoc. */</code>

基本格式總是OK的。當整個Javadoc塊能容納于一行時(且沒有Javadoc标記@XXX),可以使用單行形式。

空行(即,隻包含最左側星号的行)會出現在段落之間和Javadoc标記(@XXX)之前(如果有的話)。 除了第一個段落,每個段落第一個單詞前都有标簽<code>&amp;lt;p&amp;gt;</code>,并且它和第一個單詞間沒有空格。

标準的Javadoc标記按以下順序出現:<code>@param</code>, <code>@return</code>, <code>@throws</code>, <code>@deprecated</code>, 前面這4種标記如果出現,描述都不能為空。 當描述無法在一行中容納,連續行需要至少再縮進4個空格。

每個類或成員的Javadoc以一個簡短的摘要片段開始。這個片段是非常重要的,在某些情況下,它是唯一出現的文本,比如在類和方法索引中。

Tip:一個常見的錯誤是把簡單的Javadoc寫成<code>/** @return the customer ID */</code>,這是不正确的。它應該寫成<code>/** Returns the customer ID. */</code>。

至少在每個public類及它的每個public和protected成員處使用Javadoc,以下是一些例外:

對于簡單明顯的方法如<code>getFoo</code>,Javadoc是可選的(即,是可以不寫的)。這種情況下除了寫“Returns the foo”,确實也沒有什麼值得寫了。

單元測試類中的測試方法可能是不言自明的最常見例子了,我們通常可以從這些方法的描述性命名中知道它是幹什麼的,是以不需要額外的文檔說明。

Tip:如果有一些相關資訊是需要讀者了解的,那麼以上的例外不應作為忽視這些資訊的理由。例如,對于方法名<code>getCanonicalName</code>, 就不應該忽視文檔說明,因為讀者很可能不知道詞語<code>canonical name</code>指的是什麼。

如果一個方法重載了超類中的方法,那麼Javadoc并非必需的。

對于包外不可見的類和方法,如有需要,也是要使用Javadoc的。如果一個注釋是用來定義一個類,方法,字段的整體目的或行為, 那麼這個注釋應該寫成Javadoc,這樣更統一更友好。