以下規(guī)則并非指導(dǎo)或推薦的性質(zhì),而是必須遵守的規(guī)定。如果不遵守這些規(guī)定,Android通常不會(huì)接受投稿。已有的代碼未必全部遵守了這些規(guī)定,但是新的代碼全部都應(yīng)該遵守。
Java語言規(guī)范
我們遵循標(biāo)準(zhǔn)的Java編碼規(guī)范,并加入了新的規(guī)則:
不要忽略異常
有時(shí),完全忽略異常是非常誘人的,比如:
void?setServerPort(String value)?{
try?{
serverPort = Integer.parseInt(value);
}?catch?(NumberFormatException e) { }
}
絕對不要這么做。也許你會(huì)認(rèn)為:你的代碼永遠(yuǎn)不會(huì)碰到這種出錯(cuò)的情況,或者處理異常并不重要,可類似上述忽略異常的代碼將會(huì)在代碼中埋下一顆地雷,說不定哪天它就會(huì)炸到某個(gè)人了。你必須在代碼中以某種規(guī)矩來處理所有的異常。根據(jù)情況的不同,處理的方式也會(huì)不一樣。
無論何時(shí),空的catch語句都會(huì)讓人感到不寒而栗。雖然很多情況下確實(shí)是一切正常,但至少你不得不去憂慮它。在Java中你無法逃離這種恐懼感。 -James Gosling
可接受的替代方案包括(按照推薦順序):
· 向方法的調(diào)用者拋出異常。
void?setServerPort(String value)?throws?NumberFormatException?{
serverPort = Integer.parseInt(value);
}
· 根據(jù)抽象級別拋出新的異常。
void?setServerPort(String value)?throws?ConfigurationException?{
try?{
serverPort = Integer.parseInt(value);
}?catch?(NumberFormatException e) {
throw?new?ConfigurationException("Port "?+ value +?" is not valid.");
}
}
· 默默地處理錯(cuò)誤并在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
}
}
· 捕獲異常并拋出一個(gè)新的RuntimeException。這種做法比較危險(xiǎn):只有確信發(fā)生該錯(cuò)誤時(shí)最合適的做法就是崩潰,才會(huì)這么做。
/** 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);
}
}
請記住,最初的異常是傳遞給構(gòu)造方法的RuntimeException。如果代碼必須在Java 1.3版本下編譯,需要忽略該異常。
· 最后一招:如果確信忽略異常比較合適,那就忽略吧,但必須把理想的原因注釋出來:
/** 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.
}
}
不要捕獲頂級的Exception
有時(shí)在捕獲Exception時(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!
}
不要這么做。絕大部分情況下,捕獲頂級的Exception或Throwable都是不合適的,Throwable更不合適,因?yàn)樗€包含了Error異常。這種捕獲非常危險(xiǎn)。這意味著本來不必考慮的Exception(包括類似ClassCastException的RuntimeException)被卷入到應(yīng)用程序級的錯(cuò)誤處理中來。這會(huì)讓代碼運(yùn)行的錯(cuò)誤變得模糊不清。這意味著,假如別人在你調(diào)用的代碼中加入了新的異常,編譯器將無法幫助你識別出各種不同的錯(cuò)誤類型。絕大部分情況下,無論如何你都不應(yīng)該用同一種方式來處理各種不同類型的異常。
本規(guī)則也有極少數(shù)例外情況:期望捕獲所有類型錯(cuò)誤的特定的測試代碼和頂層代碼(為了阻止這些錯(cuò)誤在用戶界面上顯示出來,或者保持批量工作的運(yùn)行)。這種情況下可以捕獲頂級的Exception(或Throwable)并進(jìn)行相應(yīng)的錯(cuò)誤處理。在開始之前,你應(yīng)該非常仔細(xì)地考慮一下,并在注釋中解釋清楚為什么這么做是安全的。
比捕獲頂級Exception更好的方案:
· 分開捕獲每一種異常,在一條try語句后面跟隨多個(gè)catch 語句塊。這樣可能會(huì)有點(diǎn)別扭,但總比捕獲所有Exception要好些。請小心別在catch語句塊中重復(fù)執(zhí)行大量的代碼。
· 重新組織一下代碼,使用多個(gè)try塊,使錯(cuò)誤處理的粒度更細(xì)一些。把IO從解析內(nèi)容的代碼中分離出來,根據(jù)各自的情況進(jìn)行單獨(dú)的錯(cuò)誤處理。
· 再次拋出異常。很多時(shí)候在你這個(gè)級別根本就沒必要捕獲這個(gè)異常,只要讓方法拋出該異常即可。
請記?。寒惓J悄愕呐笥眩‘?dāng)編譯器指出你沒有捕獲某個(gè)異常時(shí),請不要皺眉頭。而應(yīng)該微笑:編譯器幫助你找到了代碼中的運(yùn)行時(shí)(runtime)問題。
不要使用Finalizer
Finalizer提供了一個(gè)機(jī)會(huì),可以讓對象被垃圾回收器回收時(shí)執(zhí)行一些代碼。
優(yōu)點(diǎn):便于執(zhí)行清理工作,特別是針對外部資源。
缺點(diǎn):調(diào)用finalizer的時(shí)機(jī)并不確定,甚至根本就不會(huì)調(diào)用。
結(jié)論:我們不要使用finalizers。大多數(shù)情況下,可以用優(yōu)秀的異常處理代碼來執(zhí)行那些要放入finalizer的工作。如果確實(shí)是需要使用finalizer,那就定義一個(gè)close()方法(或類似的方法),并且在文檔中準(zhǔn)確地記錄下需要調(diào)用該方法的時(shí)機(jī)。相關(guān)例程可以參見InputStream。這種情況下還是適合使用finalizer的,但不需要在finalizer中輸出日志信息,因?yàn)槿罩静荒芤驗(yàn)檫@個(gè)而被撐爆。
使用完全限定Import
當(dāng)需要使用foo包中的Bar類時(shí),存在兩種可能的import方式:
1.?? import foo.*;
優(yōu)點(diǎn):可能會(huì)減少import語句。
1.?? import foo.Bar;
優(yōu)點(diǎn):實(shí)際用到的類一清二楚。代碼的可讀性更好,便于維護(hù)。
結(jié)論:用后一種寫法來import所有的Android代碼。不過導(dǎo)入java標(biāo)準(zhǔn)庫(java.util.*、java.io.*等) 和單元測試代碼 (junit.framework.*)時(shí)可以例外。
Java類庫規(guī)范
使用Android Java類庫和工具存在一些慣例。有時(shí)這些慣例會(huì)作出重大變化,可之前的代碼也許會(huì)用到過時(shí)的模板或類庫。如果用到這部分過時(shí)的代碼,沿用已有的風(fēng)格就是了(參閱Consistency)。創(chuàng)建新的組件時(shí)就不要再使用過時(shí)的類庫了。
Java編程規(guī)范
使用Javadoc標(biāo)準(zhǔn)注釋
每個(gè)文件的開頭都應(yīng)該有一句版權(quán)說明。然后下面應(yīng)該是package包語句和import語句,每個(gè)語句塊之間用空行分隔。然后是類或接口的定義。在Javadoc注釋中,應(yīng)描述類或接口的用途。
/*
* Copyright (C) 2010 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è)類和自建的public方法必須包含Javadoc注釋,注釋至少要包含描述該類或方法用途的語句。并且該語句應(yīng)該用第三人稱的動(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)?{
...
}
如果所有的Javadoc都會(huì)寫成“sets Foo”,對于那些無關(guān)緊要的類似setFoo()的get和set語句是不必撰寫Javadoc的。如果方法執(zhí)行了比較復(fù)雜的操作(比如執(zhí)行強(qiáng)制約束或者產(chǎn)生很重要的副作用),那就必須進(jìn)行注釋。如果“Foo”屬性的意義不容易理解,也應(yīng)該進(jìn)行注釋。
無論是public的還是其它類型的,所有自建的方法都將受益于Javadoc。public的方法是API的組成部分,因此更需要Javadoc。
Android目前還沒有規(guī)定自己的Javadoc注釋撰寫規(guī)范,但是應(yīng)該遵守Sun Javadoc約定。
編寫簡短的方法
為了把規(guī)??刂圃诤侠矸秶鷥?nèi),方法應(yīng)該保持簡短和重點(diǎn)突出。不過,有時(shí)較長的方法也是合適的,所以對方法的代碼長度并沒有硬性的限制。如果方法代碼超過了40行,就該考慮是否可以在不損害程序結(jié)構(gòu)的前提下進(jìn)行分拆。
在標(biāo)準(zhǔn)的位置定義字段
字段應(yīng)該定義在文件開頭,或者緊挨著使用這些字段的方法之前。
限制變量的作用范圍
局部變量的作用范圍應(yīng)該是限制為最小的(Effective Java第29條)。使用局部變量,可以增加代碼的可讀性和可維護(hù)性,并且降低發(fā)生錯(cuò)誤的可能性。每個(gè)變量都應(yīng)該在最小范圍的代碼塊中進(jìn)行聲明,該代碼塊的大小只要能夠包含所有對該變量的使用即可。
應(yīng)該在第一次用到局部變量的地方對其進(jìn)行聲明。幾乎所有局部變量聲明都應(yīng)該進(jìn)行初始化。如果還缺少足夠的信息來正確地初始化變量,那就應(yīng)該推遲聲明,直至可以初始化為止。
本規(guī)則存在一個(gè)例外,就是涉及try-catch語句的情況。如果變量是用方法的返回值來初始化的,而該方法可能會(huì)拋出一個(gè)checked異常,那么必須在try塊中進(jìn)行變量聲明。如果需在try塊之外使用該變量,那它就必須在try塊之前就進(jìn)行聲明了,這時(shí)它是不可能進(jìn)行正確的初始化的。
// 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 塊封裝在一個(gè)方法內(nèi)即可:
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)進(jìn)行聲明,:
for?(int?i =?0; i n; i++) {
doSomething(i);
}
和
for?(Iterator i = c.iterator(); i.hasNext(); ) {
doSomethingElse(i.next());
}
對Import語句排序
import語句的次序應(yīng)該如下:
1.?? Android imports
2.?? 第三方庫(com、junit、net、org)
3.?? java和javax
為了精確匹配IDE的配置,import順序應(yīng)該是:
· 在每組內(nèi)部按字母排序,大寫字母排在小寫字母的前面。
· 每個(gè)大組之間應(yīng)該空一行(android、com、junit、net、org、java、javax)。
原先次序是不作為規(guī)范性要求的。這意味著要么允許IDE改變順序,要么使用IDE的開發(fā)者不得不禁用import自動(dòng)管理功能并且人工維護(hù)import。這看起來比較糟糕。每當(dāng)說起java規(guī)范,推薦的規(guī)范到處都是。符合我們要求的差不多就是“選擇一個(gè)次序并堅(jiān)持下去?!庇谑牵覀兙瓦x擇一個(gè)規(guī)范,更新規(guī)范手冊,并讓IDE去遵守它。我們期望:不必耗費(fèi)更多的精力,用IDE編碼的用戶就按照這種規(guī)則去import所有的package。
基于以下原因,選定了本項(xiàng)規(guī)則:
· 導(dǎo)入人員期望最先看到的放在最開始位置(android)
· 導(dǎo)入人員期望最后才看到的放在最后(java)
· 風(fēng)格讓人容易遵守
· IDE可以遵守
靜態(tài)import的使用和位置已經(jīng)成為略帶爭議的話題。有些人愿意讓靜態(tài)import和其它import混在一起,另一些人則期望讓它們位于其它import之上或者之下。另外,我們還未提到讓所有IDE都遵守同一個(gè)次序的方法。
因?yàn)榇蠖鄶?shù)人都認(rèn)為這部分內(nèi)容并不要緊,只要遵守你的決定并堅(jiān)持下去即可。
使用空格進(jìn)行縮進(jìn)
我們的代碼塊縮進(jìn)使用4個(gè)空格。我們從不使用制表符tab。如果存在疑惑,與前后的其它代碼保持一致即可。
我們用8個(gè)空格作為換行后的縮進(jìn),包括函數(shù)調(diào)用和賦值。例如這是正確的:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
而這是錯(cuò)誤的:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
遵守字段命名慣例
· 非public的、非static的字段名稱以m開頭。
· static字段名稱以s開頭。
· 其它字段以小寫字母開頭。
· public static final字段(常量)全部字母大寫并用下劃線分隔。
例如:
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)的大括號風(fēng)格
大括號不單獨(dú)占用一行;它們緊接著上一行書寫。就像這樣:
class?MyClass?{
int?func()?{
if?(something) {
// ...
}?else?if?(somethingElse) {
// ...
}?else?{
// ...
}
}
}
我們需要用大括號來包裹條件語句塊。不過也有例外,如果整個(gè)條件語句塊(條件和語句本身)都能容納在一行內(nèi),也可以(但不是必須)把它們放入同一行中。也就是說,這是合法的:
if?(condition) {
body();
}
這也是合法的:
if(condition) body();
但這是非法的:
if?(condition)
body();?// bad!
限制代碼行的長度
每行代碼的長度應(yīng)該不超過100個(gè)字符。
有關(guān)本規(guī)則的討論有很多,最后的結(jié)論還是最多不超過100個(gè)字符。
例外:如果注釋行包含了超過100個(gè)字符的命令示例或者URL文字,為了便于剪切和復(fù)制,其長度可以超過100個(gè)字符。
例外:import行可以超過限制,因?yàn)楹苌儆腥藭?huì)去閱讀它。這也簡化了編程工具的寫入操作。
使用標(biāo)準(zhǔn)的Java Annotation
Annotation應(yīng)該位于Java語言元素的其它修飾符之前。 簡單的marker annotation(@Override等)可以和語言元素放在同一行。 如果存在多個(gè)annotation,或者annotation是參數(shù)化的,則應(yīng)按字母順序各占一行來列出。
對于Java 內(nèi)建的三種annotation,Android標(biāo)準(zhǔn)的實(shí)現(xiàn)如下:
· @Deprecated:只要某個(gè)語言元素已不再建議使用了,就必須使用@Deprecated annotation。如果使用了@Deprecated annotation,則必須同時(shí)進(jìn)行@deprecated Javadoc標(biāo)記,并且給出一個(gè)替代的實(shí)現(xiàn)方式。此外請記住,被@Deprecated的方法仍然是能正常執(zhí)行的。
如果看到以前的代碼帶有@deprecated Javadoc標(biāo)記,也請加上@Deprecated annotation。
· @Override:只要某個(gè)方法覆蓋了已過時(shí)的或繼承自超類的方法,就必須使用@Override annotation。
例如,如果方法使用了@inheritdocs Javadoc標(biāo)記,且繼承自超類(而不是interface),則必須同時(shí)用@Override標(biāo)明覆蓋了父類方法。
· @SuppressWarnings:@SuppressWarnings annotation僅用于無法消除編譯警告的場合。 如果警告確實(shí)經(jīng)過測試“不可能消除”,則必須使用@SuppressWarnings annotation,以確保所有的警告都能真實(shí)反映代碼中的問題。
當(dāng)需要使用@SuppressWarnings annotation時(shí),必須在前面加上TODO注釋行,用于解釋“不可能消除”警告的條件。通常是標(biāo)明某個(gè)令人討厭的類用到了某個(gè)拙劣的接口。比如:
//?TODO:?The third-party class com.third.useful.Utility.rotate() needs generics
@SuppressWarnings("generic-cast")
List<String> blix = Utility.rotate(blax);
如果需要使用@SuppressWarnings annotation,應(yīng)該重新組織一下代碼,把需要應(yīng)用annotation的語言元素獨(dú)立出來。
簡稱等同于單詞
簡稱和縮寫都視為變量名、方法名和類名。以下名稱可讀性更強(qiáng):
好?? ??????????????????????? 差
XmlHttpRequest?? XMLHTTPRequest
getCustomerId? ? ? getCustomerID
class Html?? ? ? ? ? ? class HTML
String url? ? ? ? ? ? ?? String URL
long id? ? ? ? ? ? ? ? ?? long ID
如何對待簡稱,JDK和Android底層代碼存在很大的差異。因此,你幾乎不大可能與其它代碼取得一致。別無選擇,把簡稱當(dāng)作完整的單詞看待吧。
關(guān)于本條規(guī)則的進(jìn)一步解釋,請參閱Effective Java第38條和Java Puzzlers第68條。
使用TODO注釋
對那些臨時(shí)性的、短期的、夠棒但不完美的代碼,請使用TODO注釋。
TODO注釋應(yīng)該包含全部大寫的TODO,后跟一個(gè)冒號:
//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月會(huì)修正”),或是一個(gè)很明確的事件(“在所有代碼整合人員理解了V7協(xié)議之后刪除本段代碼”)。
慎用Log
記錄日志會(huì)對性能產(chǎn)生顯著的負(fù)面影響。如果日志內(nèi)容不夠簡煉的話,很快會(huì)喪失可用性。日志功能支持五種不同的級別。以下列出了各個(gè)級別及其使用場合和方式。
· ERROR: 該級別日志應(yīng)該在致命錯(cuò)誤發(fā)生時(shí)使用,也就是說,錯(cuò)誤的后果能被用戶看到,但是不明確刪除部分?jǐn)?shù)據(jù)、卸裝程序、清除數(shù)據(jù)區(qū)或重新刷機(jī)(或更糟糕)就無法恢復(fù)。該級別總是記錄日志。需要記錄ERROR級別日志的事件一般都應(yīng)該向統(tǒng)計(jì)信息收集(statistics-gathering )服務(wù)器報(bào)告。
· WARNING: 該級別日志應(yīng)該用于那些重大的、意外的事件,也就是說,錯(cuò)誤的后果能被用戶看到,但是不采取明確的動(dòng)作可能就無法無損恢復(fù),從等待或重啟應(yīng)用開始,直至重新下載新版程序或重啟設(shè)備。該級別總是記錄日志。需記錄WARNING級別日志的事件也可以考慮向統(tǒng)計(jì)信息收集服務(wù)器報(bào)告。
· INFORMATIVE: 該級別的日志應(yīng)該用于記錄大部分人都會(huì)感興趣的事件,也就是說,如果檢測到事件的影響面可能很廣,但不一定是錯(cuò)誤。應(yīng)該只有那些擁有本區(qū)域內(nèi)最高級別身份認(rèn)證的模塊才能記錄這些日志(為了避免級別不足的模塊重復(fù)記錄日志)。該級別總是記錄日志。
· DEBUG: 該級別的日志應(yīng)該用于進(jìn)一步記錄有關(guān)調(diào)查、調(diào)試意外現(xiàn)象的設(shè)備事件。應(yīng)該只記錄那些有關(guān)控件運(yùn)行所必需的信息。如果debug日志占用了太多的日志空間,那就應(yīng)該使用詳細(xì)級別日志(verbose)才更為合適。
即使是發(fā)行版本(release build),該級別也會(huì)被記錄,并且需用if (LOCAL_LOG)或if (LOCAL_LOGD)語句塊包裹,這里的LOCAL_LOG[D]在你的類或子控件中定義。這樣就能夠一次性關(guān)閉所有的調(diào)試日志。因此在if (LOCAL_LOG)語句塊中不允許存在邏輯判斷語句。所有日志所需的文字組織工作也應(yīng)在if (LOCAL_LOG)語句塊內(nèi)完成。如果對記錄日志的調(diào)用會(huì)導(dǎo)致在if (LOCAL_LOG)語句塊之外完成文字組織工作,那該調(diào)用就必須控制在一個(gè)方法內(nèi)完成。
還存在一些代碼仍然在使用if (localLOGV)。這也是可以接受的,雖然名稱不是標(biāo)準(zhǔn)的。
· VERBOSE: 該級別日志應(yīng)用于所有其余的事件。該級別僅會(huì)在調(diào)試版本(debug build)下記錄日志,并且需用if (LOCAL_LOGV)語句塊(或等效語句)包裹,這樣該部分代碼默認(rèn)就不會(huì)編譯進(jìn)發(fā)行版本中去了。所有構(gòu)建日志文字的代碼將會(huì)在發(fā)行版本中剝離出去,并且需包含在if (LOCAL_LOGV)語句塊中。
注意:
· 除了VERBOSE級別外,在同一個(gè)模塊中同一個(gè)錯(cuò)誤應(yīng)該盡可能只報(bào)告一次:在同一個(gè)模塊內(nèi)的一系列層層嵌套的函數(shù)調(diào)用中,只有最內(nèi)層的函數(shù)才返回錯(cuò)誤;并且只有能為解決問題提供明顯幫助的時(shí)候,同一模塊中的調(diào)用方才寫入一些日志。
· 除了VERBOSE級別外,在一系列嵌套的模塊中,當(dāng)較低級別的模塊對來自較高級別模塊的非法數(shù)據(jù)進(jìn)行檢測時(shí),應(yīng)該只把檢測情況記錄在DEBUG日志中,并且只記錄那些調(diào)用者無法獲取的信息。特別是不需要記錄已經(jīng)拋出異常的情況(異常中應(yīng)該包含了全部有價(jià)值的信息),也不必記錄那些只包含錯(cuò)誤代碼的信息。當(dāng)應(yīng)用程序與系統(tǒng)框架間進(jìn)行交互時(shí),這一點(diǎn)尤為重要。系統(tǒng)框架已能正確處理的第三方應(yīng)用程序,也不應(yīng)該記錄大于DEBUG級別的日志。僅當(dāng)一個(gè)模塊或應(yīng)用程序檢測到自身或來自更低級別模塊的錯(cuò)誤時(shí),才應(yīng)該記錄INFORMATIVE及以上級別的日志。
· 如果一個(gè)通常要記錄日志的事件可能會(huì)多次發(fā)生,則采取一些頻次限制措施或許是個(gè)好主意,以防日志被很多重復(fù)(或類似)的信息給撐爆了。
· 網(wǎng)絡(luò)連接的丟失可被視為常見現(xiàn)象,也是完全可以預(yù)見的,不應(yīng)該無緣無故就記錄進(jìn)日志。影響范圍限于應(yīng)用程序內(nèi)部的網(wǎng)絡(luò)中斷應(yīng)該記錄在DEBUG或VERBOSE級別的日志中(根據(jù)影響的嚴(yán)重程度及意外程度,再來確定是否在發(fā)行版本中也記錄日志)。
· 有權(quán)訪問的文件系統(tǒng)或第三方應(yīng)用程序發(fā)起的系統(tǒng)空間滿,應(yīng)該記錄大于INFORMATIVE級別的日志。
· 來自任何未授信源的非法數(shù)據(jù)(包括共享存儲上的任何文件,或來自任何網(wǎng)絡(luò)連接的數(shù)據(jù))可被視為可預(yù)見的,如果檢測到非法數(shù)據(jù)也不應(yīng)該記錄大于DEBUG級別的日志(即使記錄也應(yīng)盡可能少)。
· 請記住,對字符串使用+操作符時(shí),會(huì)在后臺以默認(rèn)大?。?6個(gè)字符)緩沖區(qū)創(chuàng)建一個(gè)StringBuilder對象,并且可能還會(huì)創(chuàng)建一些其它的臨時(shí)String對象。換句話說,顯式創(chuàng)建StringBuilders對象的代價(jià)并不會(huì)比用'+'操作符更高(事實(shí)上效率還將會(huì)提高很多)。還要記住,即使不會(huì)再去讀取這些日志,調(diào)用Log.v()的代碼也將編譯進(jìn)發(fā)行版中并獲得執(zhí)行,包括創(chuàng)建字符串的代碼。
· 所有要被人閱讀并存在于發(fā)行版本中的日志,都應(yīng)該簡潔明了、沒有秘密、容易理解。這里包括所有DEBUG以上級別的日志。
· 只要有可能,日志就應(yīng)該一句一行。行長最好不超過80或100個(gè)字符,盡可能避免超過130或160個(gè)字符(包括標(biāo)識符)的行。
· 報(bào)告成功的日志記錄絕不應(yīng)該出現(xiàn)在大于VERBOSE級別的日志中。
· 用于診斷難以重現(xiàn)事件的臨時(shí)日志應(yīng)該限于DEBUG或VERBOSE級別,并且應(yīng)該用if語句塊包裹,以便在編譯時(shí)能夠一次全部關(guān)閉。
· 小心日志會(huì)泄漏隱私。應(yīng)該避免將私人信息記入日志,受保護(hù)的內(nèi)容肯定也不允許記錄。這在編寫系統(tǒng)框架級代碼時(shí)尤為重要,因?yàn)楹茈y預(yù)知哪些是私人信息和受保護(hù)信息。
· 絕對不要使用System.out.println() (或本地代碼中的printf())。System.out 和 System.err會(huì)重定向到/dev/null,因此print語句不會(huì)產(chǎn)生任何可見的效果??墒?,這些調(diào)用中的所有字符串創(chuàng)建工作都仍然會(huì)執(zhí)行。
· 日志的黃金法則是:你的日志記錄不會(huì)導(dǎo)致其它日志的緩沖區(qū)溢出,正如其他人的日志也不會(huì)讓你的溢出一樣。
保持一致
我們的最終想法是:保持一致。如果你正在編寫代碼,請花幾分鐘瀏覽一下前后的其它代碼,以確定它們的風(fēng)格。如果它們在if語句前后使用了空格,那你也應(yīng)該遵循。如果它們的注釋是用星號組成的框框圍起來的,那也請你照辦。
保持風(fēng)格規(guī)范的重點(diǎn)是有一個(gè)公共的代碼詞匯表,這樣大家就可以把注意力集中于你要說什么,而不是你如何說。我們在這里列出了全部的風(fēng)格規(guī)范,于是大家也知道了這些詞匯。不過本地化的風(fēng)格也很重要。如果你要加入的代碼和已存在的代碼風(fēng)格迥異,那就會(huì)突然打破閱讀的節(jié)奏。請努力避免這種情況的發(fā)生。
Javatest風(fēng)格規(guī)范
遵守測試方法命名規(guī)范
命名測試方法時(shí),可以用下劃線來分隔測試的條件。這種風(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))
}