本頁中的程式碼樣式是嚴格規則,用於將 Java 程式碼提供給 Android 開放原始碼計畫 (AOSP)。對 Android 平台的貢獻 違反這些規定通常無法接受。三 並非所有現有程式碼都遵循這些規則,但我們希望 以符合相關規定的新程式碼請參閱尊重程式設計 。
保持一致
其中最簡單的規則之一就是「持之以恆」。如要編輯程式碼,請
查看周圍程式碼,並確定樣式。如果這樣
程式碼也應該在 if
子句前後加上空格。如果程式碼
評論周圍有小星方塊
留下你的評論
周圍也有幾顆星星。
要遵循樣式準則的重點,在於要有共通性 不必費心編寫程式 你說什麼。這裡說明通用的樣式規則 雖然當地風格也很重要如果您新增的程式碼 與檔案周圍的現有程式碼有極大差異 讀者在閱讀時就會離開節奏請嘗試 避免這種情況發生
Java 語言規則
Android 遵循標準 Java 編碼慣例,另有其他規則 。
不要忽略例外狀況
您可能會很想撰寫忽略例外狀況的程式碼,例如:
void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { } }
請不要這麼做。雖然您可能以為程式碼不會發生這類情況 或根本沒有處理錯誤的狀況,而忽略這類型的 例外狀況會在您的程式碼中建立礦場,讓其他人 觸發一次您必須處理程式碼中的所有例外狀況, 原則上實際處理方式取決於個案情況。
「任何人都應該要有空白的捕獲子句,而且不應該好奇心。 感覺。有時確實是正確的 但至少得想著想在 Java 中,您無法 逃離令人毛骨悚然的感覺。」- 大明 哥斯林
可接受的替代方案如下:
- 將例外狀況向上擲回方法的呼叫端。
void setServerPort(String value) throws NumberFormatException { serverPort = Integer.parseInt(value); }
-
擲回適用於您抽象層級的新例外狀況。
void setServerPort(String value) throws ConfigurationException { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { throw new ConfigurationException("Port " + value + " is not valid."); } }
-
妥善處理錯誤,並在
catch {}
區塊。/** Set port. If value is not a valid number, 80 is substituted. */ void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { serverPort = 80; // default port for server } }
-
擷取例外狀況並擲回新的
RuntimeException
例項。 這很危險,因此除非你認為正是因為如此,否則這麼做 錯誤發生時,應當異常終止。/** Set port. If value is not a valid number, die. */ void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { throw new RuntimeException("port " + value " is invalid, ", e); } }
-
最後,如果您確信忽略例外情況是
可以不理會,但您也必須註明原因
。
/** If value is not a valid number, original port number is used. */ void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { // Method is documented to just ignore invalid user input. // serverPort will just be unchanged. } }
不要擷取一般例外狀況
擷取例外狀況時可能會忍受延遲 如下所示:
try { someComplicatedIOFunction(); // may throw IOException someComplicatedParsingFunction(); // may throw ParsingException someComplicatedSecurityFunction(); // may throw SecurityException // phew, made it all the way } catch (Exception e) { // I'll just catch all exceptions handleError(); // with one generic handler! }
請不要這麼做。大多數情況下,找出一般性
Exception
或 Throwable
(最好不是 Throwable
因為其中包含
Error
例外狀況)。這屬於危險性
從未預期的情況 (包括 ClassCastException
等執行階段例外狀況)
會在應用程式層級的錯誤處理中抓出。它會掩蓋故障
如何處理程式碼屬性,亦即有人加入新的
您呼叫的程式碼有例外狀況,編譯器不會指出
您必須以不同方式處理錯誤大多數的情況下
不得以相同的方式處理不同類型的例外狀況。
這項規則的罕見情形是測試程式碼和頂層程式碼,其中
您希望找出所有類型的錯誤 (防止顯示
或持續執行批次工作)。在這些情況下
一般 Exception
(或 Throwable
),並妥善處理錯誤。
不過,在這之前請三思,並撰寫評論
解釋為何應該如此安全
擷取一般例外狀況的替代方案:
-
從多重貓咪區塊中分別擷取每個例外狀況,例如:
try { ... } catch (ClassNotFoundException | NoSuchMethodException e) { ... }
- 重構程式碼,進行更精細的錯誤處理, 多次嘗試方塊。將 IO 從剖析中分割並處理錯誤 來單獨使用。
- 擲回例外狀況。許多時候,您甚至可以用 例外狀況,只要讓方法擲回即可。
請記住,你的朋友是例外情況!當編譯器表明 未接收到例外狀況,也不要用草球。微笑!編譯器剛剛成功建立 輕鬆找出程式碼中的執行階段問題
請勿使用最終化器
當物件 我們計算了垃圾收集機制雖然定案影片適合用來清理 (尤其是外部資源),無法保證 將呼叫最終器 (或者甚至系統完全呼叫)。
Android 不會使用最終化器。在大部分的情況下,您可以使用
但這是最佳的例外狀況處理方法如果絕對需要最終化器
定義 close()
方法 (或類似方式),並明確記錄該方法
方法需要呼叫 (詳情請參閱
InputStream)。在本例中
是可以提供,但並非必須列印簡短記錄訊息
。
完全符合資格的匯入項目
如果您要使用套件 foo
中的 Bar
類別,有兩個
可能的匯入方式
import foo.*;
可能會減少匯入陳述式的數量。
import foo.Bar;
清楚指明所使用的類別,程式碼則更加明確 以便維護人員讀取
使用 import foo.Bar;
匯入所有 Android 程式碼。一個
為 Java 標準程式庫 (java.util.*
、
java.io.*
等) 和單元測試程式碼 (junit.framework.*
)。
Java 程式庫規則
使用 Android 的 Java 程式庫和工具有一些慣例。於 有一些案例時,慣性的變化,在舊版程式碼中, 可能會使用已淘汰的模式或程式庫使用這類程式碼時 您可以繼續使用現有樣式建立新元件時 但一律不得使用已淘汰的程式庫
Java 樣式規則
使用 Javadoc 標準註解
每個檔案的上方都應該有版權聲明,緊接在 後面加上 套件和 import 陳述式 (每個區塊均以空白行分隔) 最後是類別或介面宣告在 Javadoc 註解中 說明類別或介面的作用
/* * Copyright yyyy The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.foo; import android.os.Blah; import android.view.Yada; import java.sql.ResultSet; import java.sql.SQLException; /** * Does X and Y and provides an abstraction for Z. */ public class Foo { ... }
您編寫的每個類別和非常見公開方法都必須包含 Javadoc 註解,至少包含一個句子來說明類別 或方法採用何種做法這個句子應以第三人稱 敘述性動詞
範例
/** Returns the correctly rounded positive square root of a double value. */ static double sqrt(double a) { ... }
或
/** * Constructs a new String by converting the specified array of * bytes using the platform's default character encoding. */ public String(byte[] bytes) { ... }
您不需要為簡易的 get 和設定如
setFoo()
如果您所有的 Javadoc 都顯示為「sets Foo」。如果
方法能夠執行更複雜的工作 (例如強制執行限制
或是有重要副作用),就必須加以記錄如果不是
明顯地產出「Foo」這個房源所以請務必記錄下來
無論您的編寫方式是公開或其他方式,都會受益於 Javadoc。 公開方法屬於 API 的一部分,因此需要使用 Javadoc。Android 版 在編寫 Javadoc 時不會強制執行特定樣式 新增註解,但請按照如何為 Javadoc 工具撰寫文件註解一文的說明。
寫出簡短方法
如果可以,請盡量使用精簡且聚焦的方法。我們瞭解 方法有時候可行,因此這個方法沒有硬性限制 長度。如果方法超過 40 行,請先思考能否 不受程式結構影響
定義標準位置中的欄位
定義欄位,可以是檔案頂端或緊接在 加以運用
限制變數範圍
請盡量減少本機變數的範圍。這個 程式碼的可讀性和可維護性,並減少 出錯的可能性請在最內宣告每個變數 包含該變數的所有使用區塊
在第一次使用時宣告本機變數。 幾乎每個本機變數宣告都應包含一個初始化器。 如果資訊不足,無法初始化變數 請謹慎地將聲明延後到執行為止。
例外狀況是 try-catch 陳述式。如果變數是以 擲回已檢查例外狀況的方法的回傳值,必須 會在 try 區塊中初始化如果這個值必須在 則在 try 區塊之前宣告此函式,而應在該區塊之前宣告 但無法合理初始化:
// Instantiate class cl, which represents some sort of Set Set s = null; try { s = (Set) cl.newInstance(); } catch(IllegalAccessException e) { throw new IllegalArgumentException(cl + " not accessible"); } catch(InstantiationException e) { throw new IllegalArgumentException(cl + " not instantiable"); } // Exercise the set s.addAll(Arrays.asList(args));
不過,透過封裝 try-catch 方法 封鎖在方法中
Set createSet(Class cl) { // Instantiate class cl, which represents some sort of Set try { return (Set) cl.newInstance(); } catch(IllegalAccessException e) { throw new IllegalArgumentException(cl + " not accessible"); } catch(InstantiationException e) { throw new IllegalArgumentException(cl + " not instantiable"); } } ... // Exercise the set Set s = createSet(cl); s.addAll(Arrays.asList(args));
除非在 陳述式中宣告迴圈變數,除非 理由如下:
for (int i = 0; i < n; i++) { doSomething(i); }
和
for (Iterator i = c.iterator(); i.hasNext(); ) { doSomethingElse(i.next()); }
訂單匯入陳述式
匯入陳述式的順序為:
- Android 匯入
-
從第三方匯入 (
com
、junit
、net
、org
) -
「
java
」和「javax
」
如要完全符合 IDE 設定,匯入作業必須:
- 每個群組內的字母順序,中間是大寫字母 大寫字母 (例如,在 a 前面)
-
每個主要群組之間以空白行分隔
(
android
、com
、junit
、net
、org
、java
、javax
)
最初,順序並沒有樣式規定,意味著 IDE 就是總是改變排序,IDE 開發人員 停用自動匯入管理功能,並手動維護 以及匯入的內容這種情形已判定為不良。當要求 Java 式時, 喜歡的風格五花八門,因此 Android 裝置必須 「挑選訂單,並確保一致性」我們選擇了風格 更新了樣式指南,並讓 IDE 遵循指示。我們預期 IDE 使用者負責編寫程式碼,且所有套件中的匯入內容都會符合以下情況 您無須額外進行工程作業。
我們選擇的風格如下:
-
使用者最想要優先查看的匯入項目通常位於頂端
(
android
)。 -
使用者偏好查看的匯入內容通常會排在底部
(
java
)。 - 人類可以輕鬆掌握風格。
- IDE 可以遵循樣式。
將靜態匯入項目排在所有其他匯入項目上方,順序與 也不必定期匯入
使用空格進行縮排
我們針對區塊使用四 (4) 空格縮排,而不應使用定位點。如有疑問 與周圍程式碼一致
我們針對行換行使用八 (8) 空格,包括函式呼叫 和作業
建議
Instrument i = someLongExpression(that, wouldNotFit, on, one, line);
不建議使用
Instrument i = someLongExpression(that, wouldNotFit, on, one, line);
遵循欄位命名慣例
-
非公開、非靜態欄位名稱的開頭是
m
。 -
靜態欄位名稱開頭為
s
。 - 其他欄位的開頭則是小寫英文字母。
-
靜態最終欄位 (常數,可大幅變動) 為
ALL_CAPS_WITH_UNDERSCORES
。
例如:
public class MyClass { public static final int SOME_CONSTANT = 42; public int publicField; private static MyClass sSingleton; int mPackagePrivate; private int mPrivate; protected int mProtected; }
使用標準大括號樣式
請將大括號放在程式碼前面,而不是單獨一行:
class MyClass { int func() { if (something) { // ... } else if (somethingElse) { // ... } else { // ... } } }
條件式陳述式前後必須使用大括號。例外狀況:如果 整個條件 (限制條件和主體) 合佔一行,您可以 可 (但不一定要) 在同一行。舉例來說 可接受:
if (condition) { body(); }
,這是可以接受的:
if (condition) body();
但這不在可接受的範圍內:
if (condition) body(); // bad!
限制行長度
程式碼中的每一行文字不得超過 100 個字元。 雖然許多討論已圍繞這項規則,但這項決定仍然存在 ,長度上限為 100 個字元, 例外狀況:
- 如果註解行包含範例指令或詳細網址 超過 100 個字元,該行可超出 100 個字元 剪下及貼上。
- 匯入線條可能會超過限制,因為很少見到 (這也能簡化工具的編寫方式)。
使用標準 Java 註解
註解應置於相同語言的其他修飾符之前
元素。簡易標記註解 (例如 @Override
) 可列於
語言元素的同一行如有多則註解
或參數化註解,
依字母順序排列
Java 中三個預先定義註解的 Android 標準做法 是:
-
使用
@Deprecated
註解 使用註解元素。如果您使用@Deprecated
註解,也必須有@deprecated
Javadoc 標記,然後命名替代實作。此外, 請注意,@Deprecated
方法仍應可以運作。 如果您發現有@deprecated
Javadoc 標記的舊程式碼,請將@Deprecated
註解。 -
請一律使用
@Override
註解 方法會覆寫宣告或實作 父類別例如,如果您使用@inheritdocs
Javadoc 標記,且 衍生自類別 (非介面),您也必須加上註解 這個方法會覆寫父項類別的方法 -
使用
@SuppressWarnings
註解 才可正常使用 取消警示如果警告通過 排除」測試,@SuppressWarnings
註解必須 確保所有警告反映出 再也不是件繁重乏味的工作需要
@SuppressWarnings
註解時 開頭為TODO
註解,說明「不可」 排除」值。通常用來找出有問題的類別 提供奇怪的介面例如:// TODO: The third-party class com.third.useful.Utility.rotate() needs generics @SuppressWarnings("generic-cast") List<String> blix = Utility.rotate(blax);
需要
@SuppressWarnings
註解時,請重構程式碼 這樣就能區隔出含有註解的軟體元素
將縮寫詞視為單字
在命名變數、方法 和類別讓名稱更清晰易讀:
良好 | 不佳 |
---|---|
XmlHttpRequest | XMLHTTPRequest |
getCustomerId | getCustomerID |
HTML 類別 | HTML 類別 |
字串網址 | 字串網址 |
長 ID | 長 ID |
JDK 和 Android 程式碼集都不一致 因此幾乎不可能與 周圍的程式碼因此,請一律將縮寫字視為字詞。
使用 TODO 註解
針對程式碼暫時、短期解決方案,使用 TODO
註解。
好夠了,但還是不夠完美這些註解應包含所有註解中TODO
字串
,後面接著冒號:
// TODO: Remove this code after the UrlTable2 has been checked in.
和
// TODO: Change this to use a flag instead of a constant.
如果 TODO
的格式為「日後要執行某項動作」沒問題
加入特定日期 (「修正期限為 2005 年 11 月」),或是
特定事件 (「在所有生產混音器的情況下移除此程式碼
瞭解通訊協定 V7」)。
節制記錄
雖然需要記錄功能,但會對 如未合理保持 terse。記錄功能提供五種不同的記錄等級:
-
ERROR
:使用發生嚴重錯誤的情況時, 可能會讓使用者看見的後果,而且無法復原 而不需要刪除部分資料、解除安裝應用程式 抹除資料分區,或重新刷新裝置 (甚至更糟)。 系統一律會記錄這個等級。導致部分記錄發生的問題ERROR
級適合回報給 收集統計資料的伺服器。 -
WARNING
:使用時機嚴重或非預期的情況 也就是說,使用者一看就知道結果了 藉由執行 從等待或重新啟動應用程式 以便重新下載新版應用程式或重新啟動 裝置。系統一律會記錄這個等級。合理化記錄問題WARNING
層級就算是回報給 收集統計資料的伺服器。 -
INFORMATIVE
:用來記下有意思的資訊 也就是說,如果偵測到 但這不一定是錯誤如此 只能以認為有利的模組記錄 是網域中最具公信力的資訊 (以避免重複 而非授權元件進行記錄) 事件。系統一律會記錄這個等級。 -
DEBUG
:用來進一步注意 可能相關的裝置進行調查及偵錯 行為只記錄收集所需的資料 瞭解元件運作情形如果偵錯 則請使用詳細記錄 。即使發布子版本也會記錄這個等級,此等級為必要項目 由
if (LOCAL_LOG)
或if LOCAL_LOGD)
區塊包圍,其中LOCAL_LOG[D]
的定義如下 以便在類別或子元件中預測 停用所有這類記錄功能因此,沒有任何有效邏輯 在if (LOCAL_LOG)
區塊中。各類字串的建構內容 此外,記錄檔中也需要放置在if (LOCAL_LOG)
區塊。不要重構記錄呼叫 並輸出至方法呼叫 建立字串的建築物if (LOCAL_LOG)
區塊。部分程式碼仍顯示
if (localLOGV)
。這個 也可以接受,但並非標準名稱。 -
VERBOSE
:用於其他用途。這個等級只有 記錄於偵錯版本上,且應該以if (LOCAL_LOGV)
封鎖 (或同等元素), 編碼器-解碼器架構所有字串架構都會去除 以及發布子版本if (LOCAL_LOGV)
區塊。
附註
-
在特定模組 (不含
VERBOSE
層級) 中, 可能只能回報一次。在單一網路鏈中 函式呼叫時,只有最內部的函式 傳回錯誤,且相同模組中的呼叫端只能新增 或是記錄一些記錄,如果這對於找出問題會很有幫助。 -
在
VERBOSE
層級的模組鏈結中, 較低層級模組會偵測來自較高層級模組的無效資料 較低層級的模組應該只將此情況記錄到DEBUG
記錄,且僅適用於記錄未提供 否則可為呼叫端提供完整存取權具體來說 記錄擲回例外狀況的情境 (例外狀況應 包含所有相關資訊),或只在 包含在錯誤代碼中尤其是 架構與應用程式互動中相當重要的一環 以及第三方應用程式造成的損害 由架構處理的事件,觸發記錄不應高於DEBUG
等級。只有在發生此情況時,才會觸發記錄程序INFORMATIVE
以上等級是指模組或應用程式偵測到 獨立層級或來自較低層級的錯誤 - 當一個常用於合理化記錄功能的條件時 建議您執行 頻率限制機制,以避免含有許多 重複相同 (或非常類似) 資訊的副本
-
網路連線中斷的情形相當常見,而且完全沒有網路連線。
請勿故意記錄。網路斷線
會影響應用程式內後果的連線
DEBUG
或VERBOSE
層級 (取決於 結果嚴重到足以讓使用者留存在版本上 建構)。 - 在可存取或應用程式的檔案系統中擁有完整的檔案系統 代表第三方應用程式不應在層級記錄 大於 INFORMATIVE 的
-
來自任何不受信任的來源 (包括
共用儲存空間或透過網路
安全連線) 視為預期而不應觸發
裝置偵測到低於
DEBUG
的程度時記錄 無效 (之後應盡可能減少記錄)。 -
用於
String
物件時,會以隱含形式使用+
運算子 建立採用預設值的StringBuilder
例項 緩衝區空間 (16 個半形字元),且可能採用其他暫時String
如需儲存大量結構化物件 建議使用 Cloud Bigtable所以明確建立StringBuilder
物件並不是 比依賴預設的+
運算子的費用高,且可大幅 )。請注意,如果程式碼Log.v()
會在發布子版本上編譯及執行。 包括建構字串,即使沒有讀取記錄。 -
可供他人讀取,且是為了
提供的發布子版本應該較為繁複
並以淺顯易懂的方式呈現這包括所有記錄功能
最高等級為
DEBUG
。 - 如果可以,請一行記錄。 一行長度上限為 80 或 100 個半形字元 可接受。避免超過 130 或 160 個字元的長度 (包括標記長度)。
-
如果記錄報告成功,請勿在更高等級使用
VERBOSE
。 -
如果您使用暫時性記錄功能來診斷難以解決的問題
重現,保留為
DEBUG
或VERBOSE
層級,並 如果封鎖時允許停用該功能 也就是編譯時間 - 留意記錄中的安全性外洩問題。避免記錄隱私 可能不準確或不適當請特別注意,請避免記錄受保護內容的相關資訊。 特別是當您 因為要事先瞭解到 不會用於私人資訊或受保護內容
-
一律不使用
System.out.println()
(或為以下用途使用printf()
) 原生程式碼)。System.out
和System.err
可享 重新導向至/dev/null
,因此您的輸出陳述式沒有任何 可見效果不過,發生了 這些呼叫仍會執行 - 記錄的黃金法則在於 其他記錄也可視需要將其他記錄推送到緩衝區中,就像其他記錄可能會一樣 而不是挑戰自我
Javatest 樣式規則
請遵循測試方法命名慣例,並使用底線分隔 具體測試是針對特定個案進行測試這個風格讓 可讓您更輕鬆地查看測試的案例例如:
testMethod_specificCase1 testMethod_specificCase2 void testIsDistinguishable_protanopia() { ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA) assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK)) assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y)) }