貢獻(xiàn)者的代碼風(fēng)格
下面的代碼樣式是嚴(yán)格的規(guī)則莹桅,而不是準(zhǔn)則或建議。不符合這些規(guī)則的Android應(yīng)用通常不會(huì)被接受。我們認(rèn)識(shí)到珊擂,并非所有現(xiàn)有代碼都遵循這些規(guī)則,但我們期望所有新代碼都符合费变。
Java語言規(guī)則
Android遵循標(biāo)準(zhǔn)的Java編碼規(guī)則以及下面描述的附加規(guī)則摧扇。
不要忽略異常
可能很容易編寫完全忽略異常的代碼,例如:
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
切勿如此挚歧。雖然你可能認(rèn)為你的代碼永遠(yuǎn)不會(huì)出現(xiàn)這個(gè)錯(cuò)誤或者處理這個(gè)錯(cuò)誤并不重要扛稽,但這樣忽略異常將埋下一顆地雷,其他人某一天可能會(huì)觸發(fā)它滑负。你必須以合理的方式在代碼中處理每一個(gè)異常在张,方式上具體情況具體處理。
你應(yīng)當(dāng)為你寫出一個(gè)空的catch語句塊時(shí)感到驚恐矮慕。即便有時(shí)就得這么處理帮匾,但也不應(yīng)心安理得。寫Java代碼時(shí)痴鳄,你不應(yīng)當(dāng)逃避驚恐感瘟斜。——James Gosling
可接受的替代方案(按優(yōu)先順序)是:
- 將異常拋出給方法的調(diào)用者。
void setServerPort(String value) throws NumberFormatException {
serverPort = Integer.parseInt(value);
}
- 拋出一個(gè)適合你的抽象層次的新異常痪寻。
void setServerPort(String value) throws ConfigurationException {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ConfigurationException("Port " + value + " is not valid.");
}
}
- 優(yōu)雅地處理錯(cuò)誤并在catch {}塊中替換一個(gè)合適的值螺句。
/** 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
}
}
- 捕獲異常并拋出一個(gè)新的
RuntimeException
。這是比較危險(xiǎn)的槽华,除非你確定導(dǎo)致這個(gè)錯(cuò)誤的事件屬于程序崩潰壹蔓。
/** 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);
}
}
> **注意** 將原異常作為參數(shù)創(chuàng)建RuntimeException。如果代碼必須在Java 1.3上編譯猫态,那么你不得不忽略作為原因的原始異常佣蓉。
- 作為最后的手段,如果確定忽略異常是恰當(dāng)?shù)那籽敲纯梢院雎运缕荆仨氉⑨屢粋€(gè)很好的理由為什么忽略:
/** 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.
}
}
不要捕捉基類異常
有時(shí)候很容易懶惰到這樣捕捉異常:
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!
}
切勿如此。在幾乎所有情況下义辕,捕獲基類異诚罕辏或Throwable(最好不是Throwable,因?yàn)樗ㄥe(cuò)誤異常)是不合適的灌砖。這非常危險(xiǎn)璧函,因?yàn)檫@意味著你會(huì)捕捉那些本應(yīng)在應(yīng)用級(jí)被處理的非預(yù)期的異常(包括像ClassCastException這樣的運(yùn)行時(shí)異常)傀蚌。它忽略了處理異常時(shí)的異常類型,也就是如果別人在你調(diào)用的代碼中加入了一種新的類型的異常蘸吓,編譯器就無法幫你區(qū)別處理這個(gè)新的類型的異常善炫,而在大多數(shù)情況下,不應(yīng)以同樣的方式處理不同類型的異常库继。
這條規(guī)則也有個(gè)罕見的例外箩艺,在你希望捕獲各種錯(cuò)誤(防止它們出現(xiàn)在UI中,或者保持后續(xù)的工作繼續(xù)運(yùn)行)的測試代碼和頂層代碼中宪萄,你可以捕捉并恰當(dāng)?shù)靥幚砘惍惓#ɑ騎hrowable)艺谆。慎重而為,并注解出為何此處這么做是安全的拜英。
捕獲泛型異常的替代方案:
- 每個(gè)try語句之后分類型捕獲每一個(gè)異常静汤,方法雖笨,但是卻要優(yōu)于捕獲基類異常居凶,避免在catch塊中代碼過度重復(fù)撒妈。
- 重構(gòu)代碼,拆分單個(gè)try塊的內(nèi)容排监,換成多個(gè)try塊來捕獲異常。通過拆分輸入輸出與解析來區(qū)別處理捕獲的異常杰捂。
- 重新拋出異常舆床。多數(shù)時(shí)候你并不需要捕獲這個(gè)級(jí)別的異常,就讓方法拋出來即可嫁佳。
記装ざ印:把異常當(dāng)朋友吧!當(dāng)編譯器提示你沒有捕獲異常時(shí)蒿往,不用皺眉盛垦。微笑:編譯器不過是輔助你捕獲代碼中的運(yùn)行錯(cuò)誤。
不要使用Finalizers
Finalizers可以讓你在對(duì)象被垃圾回收時(shí)執(zhí)行一段代碼瓤漏。雖然他非常便于進(jìn)行資源清理(尤其是外部資源)腾夯,但是無法保證什么時(shí)候會(huì)調(diào)用它(或者甚至馬上就會(huì)調(diào)用)。
Android并不使用Finalizers蔬充。大多數(shù)情況下蝶俱,你可在一個(gè)擁有較好的異常捕獲的Finalizers中執(zhí)行你想要的操作。如果你的確需要它饥漫,定義一個(gè)close()(或類似的)方法并注釋出這個(gè)方法需要在什么時(shí)候被調(diào)用(可參見InputStream)榨呆。這種情況下,并不要求但可以適當(dāng)?shù)卮蛴∫恍┖喍痰娜罩鞠⒂苟樱?dāng)然也不能日志洪泛积蜻。
完整的導(dǎo)入
當(dāng)你想要使用foo包的Bar類時(shí)闯割,以下是兩種可能的導(dǎo)入方式:
-
import foo.*;
潛在地減少import語句的數(shù)量。 -
import foo.Bar;
能確切地指出什么類被使用了并且對(duì)維護(hù)者來說可讀性更高竿拆。
Android代碼中全部使用import foo.Bar;
的導(dǎo)入方式宙拉。另一種方案在Java標(biāo)準(zhǔn)庫(java.util.*
、java.io.*
等)與單元測試代碼(junit.framework.*
)上存在顯式異常如输。
Java庫規(guī)則
在使用Android的Java庫和工具上存在一些約定鼓黔。在某些情況下,這些約定出現(xiàn)了重大的改變不见,較早的代碼可能仍然使用一些已棄用的模式或庫澳化。對(duì)于現(xiàn)成的代碼可以繼續(xù)現(xiàn)有的風(fēng)格,但對(duì)于新創(chuàng)建的組件時(shí)稳吮,就不要再使用已棄用的庫了缎谷。
Java風(fēng)格規(guī)則
使用標(biāo)準(zhǔn)的Javadoc注釋
每個(gè)文件應(yīng)在頂部有一個(gè)版權(quán)聲明,其后是package和import語句(每個(gè)塊由空行分隔)灶似,最后是類或接口聲明列林。在Javadoc注釋中,需描述類或接口的作用酪惭。
/*
* Copyright (C) 2015 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 {
...
}
你寫的每一個(gè)類和重要的公共方法必須包含一個(gè)Javadoc注釋希痴,至少需要一句話的描述。句式以動(dòng)詞開始春感。
例如:
/** 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) {
...
}
對(duì)于像setFoo()
這樣普通的get與set方法就不需要寫Javadoc了砌创,能寫的也不過是“獲取 Foo”。如果方法執(zhí)行了一些復(fù)雜的操作(如強(qiáng)制約束條件或有重要的函數(shù)副作用)鲫懒,那么就需要注釋嫩实。如果讀者不清楚“Foo”屬性具體內(nèi)容時(shí),也需要注釋窥岩。
你寫的每個(gè)方法甲献,無論是公共的還是其他的,都將從Javadoc中受益颂翼。公共方法是API的一部分晃洒,因此需要Javadoc。Android目前沒有強(qiáng)制執(zhí)行一個(gè)特定的風(fēng)格來編寫Javadoc注釋朦乏,但你可以參照這個(gè)說明:如何為Javadoc工具編寫文檔注釋锥累。
短小精煉的方法
盡可能地保持方法的短小精煉。但有時(shí)方法內(nèi)容長又是恰當(dāng)?shù)募圆⒉挥残韵拗品椒ǖ膬?nèi)容長度桶略。如果一個(gè)方法超過40行左右,可以考慮在不破壞程序結(jié)構(gòu)的前提下對(duì)其拆解。
固定位置中定義字段
在文件的頂部或在使用它們的方法之前定義字段际歼。
限制變量范圍
將局部變量的范圍保持為最小惶翻。通過這樣做,我們可以提高代碼的可讀性和可維護(hù)性鹅心,并減少出錯(cuò)的可能性吕粗。每個(gè)變量應(yīng)該在包含變量的所有使用的最內(nèi)部塊中聲明。
局部變量應(yīng)該在它們第一次使用的點(diǎn)被聲明旭愧。幾乎每個(gè)局部變量聲明都應(yīng)該包含一個(gè)初始化器颅筋。在你還沒有足夠的初始化條件時(shí)就不要聲明局部變量,推遲到條件充足時(shí)是更為明智的選擇输枯。
在try-catch語句塊中议泵,如果變量通過一個(gè)會(huì)拋出異常的方法的返回值來初始化,則必須在try塊中進(jìn)行桃熄。如果值必須在try塊之外使用先口,那么它就得在try塊之前聲明,此時(shí)還不能明確地初始化:
// 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));
然而瞳收,即使這種情況也可以通過在一個(gè)方法中封裝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));
若不是有逼不得已的原因碉京,循環(huán)變量應(yīng)該在for語句體內(nèi)聲明:
for (int i = 0; i < n; i++) {
doSomething(i);
}
以及
for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomethingElse(i.next());
}
排序?qū)胝Z句
import語句的順序:
- Android包
- 第三方包(
com
、junit
螟深、net
谐宙、org
) -
java
與javax
要完全匹配IDE設(shè)置,導(dǎo)入順序應(yīng)為:
- 每個(gè)分組中按字母順序排列界弧,大寫字母在小寫之前(如:Z在a之前)
- 在每個(gè)主要分組(
android
卧惜、com
、junit
夹纫、net
、org
设凹、java
舰讹、javax
)之間用空行分隔。
最初闪朱,對(duì)排序沒有樣式要求月匣,這意味著IDE可能會(huì)經(jīng)常改變順序,或者開發(fā)人員須禁用自動(dòng)導(dǎo)入來進(jìn)行手動(dòng)維護(hù)導(dǎo)入奋姿。這很糟糕锄开。當(dāng)Java風(fēng)格提出時(shí),首選樣式經(jīng)過一段多變而有混亂的過程之后歸結(jié)為簡單地“選擇一個(gè)兼容的排序方案”称诗。所以我們選擇了一種風(fēng)格萍悴,更新了風(fēng)格指南,并讓IDE遵守它。以后IDE將按照此方案自行維護(hù)包的導(dǎo)入癣诱,而我們則無需再做額外操作计维。
風(fēng)格是這樣選取的:
- 想最先看到的導(dǎo)入應(yīng)放置于頂部(
android
)。 - 最不想看到的導(dǎo)入應(yīng)放置于底部(
java
)撕予。 - 大家更易于遵循的風(fēng)格鲫惶。
- IDE可以遵循的風(fēng)格。
靜態(tài)導(dǎo)入的使用及位置仍存在一些爭議实抡。有些人傾向于靜態(tài)導(dǎo)入穿插在其他導(dǎo)入之間欠母,而有些人則更樂意靜態(tài)導(dǎo)入在所有導(dǎo)入頂部或底部。加之吆寨,我們也沒有確定如何使所有IDE使用相同的順序赏淌。由于許多人認(rèn)為這是個(gè)低優(yōu)先級(jí)的問題,所以保持兼容性的前提下自行判斷即可鸟废。
空格縮進(jìn)
使用4個(gè)空格而非制表符進(jìn)行塊縮進(jìn)猜敢。存在疑問時(shí),保持與周圍代碼一致即可盒延。
使用8個(gè)空格進(jìn)行自動(dòng)換行缩擂,包括函數(shù)調(diào)用及賦值運(yùn)算。正確示例:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
錯(cuò)誤示例:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
遵循字段命名規(guī)則
- 非公共添寺,非靜態(tài)字段名以m開頭胯盯。
- 靜態(tài)字段名稱以s開頭。
- 其他字段以小寫字母開頭计露。
- 公共靜態(tài)final字段(常量)為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;
}
使用標(biāo)準(zhǔn)括號(hào)風(fēng)格
左大括號(hào)不要獨(dú)立成行; 與其之前的代碼在同一行:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
我們需要在條件語句周圍添加括號(hào)。例外:如果整個(gè)條件(條件和主體)適合一行票罐,你可以(非必須)把它全部放在一行上叉趣。 例如,這是可行的:
if (condition) {
body();
}
這也是可行的:
if (condition) body();
但這是不好的:
if (condition)
body(); // bad!
列長限制
代碼中的每行文字長度最多為100個(gè)字符该押。雖然關(guān)于這個(gè)規(guī)則存在很多爭論疗杉,但最終決定仍舊是100個(gè)字符。以下例外:
- 如果注釋行包含示例命令或長度超過100個(gè)字符的URL文本蚕礼,則該行可能長于100個(gè)字符烟具,以便于剪切和粘貼。
- 導(dǎo)入行可以超過限制奠蹬,因?yàn)槿藗兒苌倏吹剿鼈儯ㄟ@也簡化了工具寫入)朝聋。
使用標(biāo)準(zhǔn)Java注解
注解應(yīng)位于同一語句元素的其他修飾符之前。簡單的標(biāo)記注解(如@Override
)可以與語句元素列在同一行囤躁。如果有多個(gè)注解或參數(shù)化注解冀痕,它們則應(yīng)按字母順序逐行列出荔睹。
Java中三種預(yù)定義注解的Android標(biāo)準(zhǔn)用法是:
-
@Deprecated
:不再鼓勵(lì)使用的過時(shí)的語句元素必須使用@Deprecated注解。如果使用@Deprecated注解金度,則還必須具有Javadoc的@deprecated標(biāo)記应媚,并且指出替代方案。此外猜极,請記住中姜,@Deprecated方法仍然應(yīng)該工作。 如果您看到舊代碼帶有@deprecated Javadoc標(biāo)記跟伏,請?zhí)砑覢Deprecated注釋丢胚。 -
@Override
:只要方法從超類覆蓋聲明或?qū)崿F(xiàn),就必須使用@Override注解受扳。例如携龟,如果使用Javadoc的@inheritdocs標(biāo)記,并從類(而不是接口)派生勘高,則還必須@Overrides注解該方法峡蟋,表明其覆蓋父類的方法。 -
@SuppressWarnings
:在無法消除警告的情況下才可以使用@SuppressWarnings注解华望。如果警告通過了“不可消除”測試蕊蝗,則必須使用@SuppressWarnings注解,以確保所有警告都反映代碼存在實(shí)際問題赖舟。
當(dāng)需要使用@SuppressWarnings注解時(shí)蓬戚,就必須以TODO注釋出為何出現(xiàn)“不可消除”的情形。通常會(huì)定義出是哪個(gè)類提供了一個(gè)糟糕的接口宾抓。例如:
// TODO: The third-party class com.third.useful.Utility.rotate() needs generics
@SuppressWarnings("generic-cast")
List<String> blix = Utility.rotate(blax);
當(dāng)必須使用@SuppressWarnings注解時(shí)子漩,應(yīng)該重構(gòu)代碼以分離出需要使用該注解的語句元素。
運(yùn)用首字母縮略詞
使用首字母縮略詞及簡寫來命名變量石洗、方法和類以提高可讀性:
好 | 壞 |
---|---|
XmlHttpRequest | XMLHTTPRequest |
getCustomerId | getCustomerID |
class Html | class HTML |
String url | String URL |
long id | long ID |
由于JDK和Android代碼庫在首字母縮略詞之間非常不一致幢泼,因此幾乎不可能與周圍的代碼一致。所以讲衫,始終將首字母縮寫作為詞缕棵。
使用TODO注釋
使用TODO來注釋臨時(shí)的、解決方案短暫的或者足夠好但并不完美的代碼焦人。TODO需全大寫后跟冒號(hào):
// TODO: Remove this code after the UrlTable2 has been checked in.
以及
// TODO: Change this to use a flag instead of a constant.
如果您的TODO的形式是“在未來的日期做某事”,請確保您包括一個(gè)非常具體的日期(“在2005年11月修復(fù)”)或一個(gè)非常具體的事件(“在所有產(chǎn)品版本提升到V7之后刪除此代碼”)重父。
謹(jǐn)慎打印
雖然日志是有必要的花椭,但是它對(duì)性能具有顯著的負(fù)面影響,并且隨著日志的長度變大房午,其有效性損失越多矿辽。日志打印器提供了五種不同級(jí)別的日志:
-
ERROR
:致命錯(cuò)誤出現(xiàn)時(shí)使用,即導(dǎo)致不可逆的用戶可見效果,像隱式地刪除數(shù)據(jù)袋倔、卸載應(yīng)用程序雕蔽、擦數(shù)數(shù)據(jù)分區(qū)或刷設(shè)備(或更糟)。這個(gè)級(jí)別始終會(huì)被打印出來宾娜。通過ERROR級(jí)別的日志來收集異常并發(fā)送給統(tǒng)計(jì)服務(wù)器是個(gè)不錯(cuò)的方案批狐。 -
WARNING
:嚴(yán)重的意外事件發(fā)生時(shí)使用,即導(dǎo)致可恢復(fù)的用戶可見效果前塔,像并不會(huì)導(dǎo)致數(shù)據(jù)丟失的失誤操作嚣艇、一直等待或重啟應(yīng)用、重新下載一個(gè)新版本或重啟設(shè)備华弓。這個(gè)級(jí)別始終會(huì)被打印出來食零。通過WARNING級(jí)別的日志來收集異常并發(fā)送給統(tǒng)計(jì)服務(wù)器也是可以考慮的方案。 -
INFORMATIVE
:大多數(shù)人所關(guān)注的重要事件發(fā)生時(shí)使用寂屏,即檢測到一些具備廣泛影響但并不是錯(cuò)誤的情況贰谣。其應(yīng)該由領(lǐng)域中最具權(quán)威性的模塊來打印(以避免非授權(quán)的組件來重復(fù)打印日志)迁霎。這個(gè)級(jí)別始終會(huì)被打印出來吱抚。 -
DEBUG
:用于進(jìn)一步打印設(shè)備上可能與調(diào)試意外行為有關(guān)的內(nèi)容。你應(yīng)該只打印你的組件上發(fā)生的那些你所需要收集的足夠信息欧引。如果調(diào)試日志占了你的日志主導(dǎo)频伤,那么你應(yīng)該使用詳細(xì)日志記錄。
這個(gè)級(jí)別始終會(huì)被打印出來芝此。即使是正式版本憋肖,需要添加條件判斷來禁用所有的調(diào)試日志(如:if (LOCAL_LOG)
、if (LOCAL_LOGD)
)婚苹。if的判斷條件最好不要是活動(dòng)邏輯岸更。打印所需的字符串的創(chuàng)建最好也包含在if語句里面。如果打印字符串在if語句外部構(gòu)建膊升,則不應(yīng)在方法調(diào)用中重新映射日志調(diào)用怎炊。
有些代碼堅(jiān)持使用非標(biāo)準(zhǔn)命名的方案if (localLOGV)
,其尚可接受廓译。 -
VERBOSE
:以外的所有打印都可以使用這個(gè)评肆。只在調(diào)試版本中打印此級(jí)別的日志,并需要if語句包裹非区,才能默認(rèn)被編譯出來瓜挽。if語句中的字符串構(gòu)建將在正式版中刪除。
注意:
- 在給定的模塊中征绸,除了在VERBOSE級(jí)別久橙,如果可以俄占,一個(gè)錯(cuò)誤只應(yīng)報(bào)告一次。在模塊內(nèi)的單個(gè)函數(shù)調(diào)用鏈中淆衷,只有最內(nèi)層函數(shù)應(yīng)該返回錯(cuò)誤缸榄,如果明顯有助于隔離問題,那么同一模塊中的調(diào)用者應(yīng)該只添加一些日志祝拯。
- 在一個(gè)模塊鏈當(dāng)中甚带,除VERBOSE級(jí)別,當(dāng)?shù)图?jí)模塊檢測到來自高級(jí)模塊的無效數(shù)據(jù)時(shí)鹿驼,低級(jí)模塊只應(yīng)將此情況記錄到DEBUG日志中欲低,并且僅當(dāng)日志提供的信息對(duì)調(diào)用者來說不可用。具體來說畜晰,不需要打印拋出異常的位置(異常應(yīng)包含所有相關(guān)信息)砾莱,或者日志的所有信息包含在錯(cuò)誤代碼中。這在框架和應(yīng)用程序之間的交互中尤其重要凄鼻,由框架處理的第三方應(yīng)用引起的情形腊瑟,所觸發(fā)的日志級(jí)別不應(yīng)高于DEBUG級(jí)別。只有當(dāng)模塊或應(yīng)用程序檢測到來自其自身級(jí)別或較低級(jí)別的錯(cuò)誤時(shí)块蚌,才能觸發(fā)INFORMATIVE或更高級(jí)別的日志打印闰非。
- 有些日志打印會(huì)多次用到相同(或非常相似)的數(shù)據(jù)時(shí),可以提取公共數(shù)據(jù)用于打印日志峭范,以便更好地節(jié)約資源财松。
- 網(wǎng)絡(luò)連接的丟失完全是常見而又預(yù)期內(nèi)的,不應(yīng)該打印日志纱控。網(wǎng)絡(luò)連接丟失的日志應(yīng)該為DEBUG或VERBOSE級(jí)別(在正式版中是否打印日志取決于后果與非預(yù)期的嚴(yán)重程度)辆毡。
- 在第三方應(yīng)用程序提供的文件系統(tǒng)中具備一個(gè)完整的文件系統(tǒng)的,不應(yīng)打印級(jí)別高于INFORMATIVE的日志甜害。
- 來自任何不受信任的源(包括共享存儲(chǔ)上的任何文件舶掖,或通過任何網(wǎng)絡(luò)獲取的數(shù)據(jù))的無效數(shù)據(jù)被視為預(yù)期的,并且當(dāng)檢測數(shù)據(jù)無效時(shí)尔店,不應(yīng)觸發(fā)高于DEBUG的級(jí)別的日志打诱H痢(甚至對(duì)高級(jí)別的日志打印也應(yīng)受限制)。
- 記住嚣州,對(duì)字符串使用+運(yùn)算符時(shí)鲫售,會(huì)隱式創(chuàng)建一個(gè)帶有緩沖區(qū)(16字符)及其他臨時(shí)String對(duì)象的
StringBuilder
。顯式地創(chuàng)建StringBuilder并不比直接使用“+”更耗費(fèi)資源(實(shí)際上更高效)该肴。記住情竹,即便日志并不會(huì)被用于閱讀,在正式版中Log.v()
方法照樣會(huì)被編譯執(zhí)行沙庐,包括打印的字符串鲤妥。 - 所有高于DEBUG級(jí)別的日志,用于給他人閱讀或在正式版也啟用拱雏,則必須簡潔且易于理解棉安。
- 必要的時(shí)候,如果可能铸抑,記錄應(yīng)該保持在一行贡耽。線長度最多可達(dá)80或100個(gè)字符是完全可以接受的,如果可能鹊汛,應(yīng)避免長度大于130或160個(gè)字符(包括標(biāo)簽的長度)蒲赂。
- 不應(yīng)使用高于VERBOSE的級(jí)別記錄報(bào)告成功的日志。
- 用于追蹤難以重現(xiàn)的問題的臨時(shí)日志應(yīng)當(dāng)使用DEBUG或者VERBOSE級(jí)別刁憋,并且包裹在if語句中滥嘴,以便完成問題追蹤后完全禁用它。
- 小心日志中的安全漏洞至耻。避免打印私人信息若皱。毫無疑問地,也應(yīng)避免打印保護(hù)的內(nèi)容尘颓。這在編寫框架代碼時(shí)尤其重要走触,因?yàn)槭孪炔蝗菀字朗裁词撬饺诵畔⒒蚴鼙Wo(hù)的內(nèi)容。
- 禁用
System.out.println()
(或native的printf()
)疤苹。System.out和System.err被重定向到/dev/null互广,所以你的打印語句將沒有可見的效果。然而卧土,對(duì)于這些打印所需要的字符串仍然被構(gòu)建了惫皱。 - 日志的黃金規(guī)則:你的日志不必要的情況下不會(huì)將其他日志推出緩沖區(qū),其他日志也不會(huì)對(duì)你的日志這么做夸溶。
風(fēng)格一致
我們一致認(rèn)為:保持代碼風(fēng)格一致逸吵。如果您正在編輯代碼,請花幾分鐘時(shí)間查看周圍的代碼并確定其樣式缝裁。如果代碼在if條件左右使用了空格扫皱,那么你也該這么做。如果代碼的注釋用星星框起來捷绑,那么你也保證你的注釋用星星框起來韩脑。
編碼風(fēng)格指導(dǎo)的要點(diǎn)是有一個(gè)通用的編碼詞匯,所以人們可以專注于你在說什么粹污,而不是你如何說段多。我們在這里提出全球化的風(fēng)格規(guī)則,所以人們知道編碼詞匯壮吩,但本土化的風(fēng)格也很重要进苍。如果你添加到一個(gè)文件的代碼看起來與現(xiàn)有的代碼有很大的不同加缘,當(dāng)讀者閱讀它時(shí),它會(huì)使讀者脫離他們的節(jié)奏觉啊。所有盡量避免這一點(diǎn)拣宏。
Javatests風(fēng)格規(guī)則
遵循測試方法命名規(guī)則,使用下劃線將要測試的內(nèi)容與要測試的特定案例分開杠人。這種風(fēng)格讓閱讀者更容易看懂正在被測試的案例勋乾。例如:
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))
}