Android開發規範

偶然發現的好文章,陋習還是很多,切行切惜!!!參考鏈接http://blog.csdn.net/huangyabin001/article/details/30095775

1 介紹

1.1 目的

  • 減少維護花費。
  • 提高可讀性。
  • 加快工作交接。
  • 減少名字增生。
  • 降低缺陷引入的機會。

1.2 術語和定義

強制:編程時必須遵守的規定,含有強制字樣或字體用加粗式樣標註。

推薦:編程時推薦遵守的規定,字體用普通式樣標註。

2 文件組織

避免超過 2000 行的源文件。

2.1 Java 包和源文件

每個 Java 源文件都包含一個單一的公共類或接口。若私有類和接口與一個公共類相關聯,可以將它們和公共類放入同一個源文件。公共類必須是這個文件中的第一個類或接口

Java 源文件還遵循以下規則:

  • 開頭註釋
  • 包和引入語句
  • 類和接口聲明

2.1.1 開頭註釋

所有的源文件都應該在開頭有一個的註釋,其中列出版本信息、日期和版權聲明。

<span class="coMULTI">/*
 * Copyright (c) 2014 SIMCOM, Inc.
 * All Rights Reserved.
 * SIMCOM Proprietary and Confidential.
 */</span>

2.1.2 包和引入語句

在多數 Java 源文件中,第一個非註釋行是包語句,在它之後可以跟引入語句。

<span class="kw1">package</span> <span class="co2">com.<a target=_blank href="http://lib.csdn.net/base/15" target="_blank" style="text-decoration: none; color: rgb(12, 137, 207);">android</a>.sim</span><span class="sy0">;</span>
 
<span class="kw1">import</span> <span class="co2">java.io.IOException</span><span class="sy0">;</span>

在導入包時當完全限制代碼所使用的類的名字,儘量少用通配符的方式,但導入一些通用包,或用到一個包下大部分類時,則可是使用通配符方式。同一包中的類在導入時應聲明在一起,無效的未使用到的引用要即時刪除。

2.1.3 類和接口聲明

下表(強制)描述了類和接口聲明的各個部分以及它們出現的先後次序。

類/接口聲明的各部分 說明
類/接口文檔註釋(/**……*/) 類和接口應該有標準 Javadoc 註釋。
類或接口的聲明 類名和接口的第一個字符大寫,每個單詞的首字母大寫,中間可以有下劃線。
類/接口實現的長度限制 限制一個匿名內部類的長度不要超過 80 行。
類的(靜態)變量 首先是類的公共變量,隨後是保護變量,再後是包一級別的變量(沒有訪問修飾符,access modifier),最後是私有變量。靜態 final 變量應全部大寫,中間可以有下劃線。
實例變量  
構造器 構造器的代碼長度(不計空行),不應超過 200 行。
方法 方法名應爲動詞或動賓短語,首字母小寫,其後每個單詞首字母大寫,方法的參數不要超過 8個,參數名字必須和變量的命名規範一致,public 的方法之前應該有 Javadoc 註釋,方法之後的大括號位於行尾。方法應該保持簡短和重點突出,對方法的代碼長度並沒有硬性的限制。如果方法代碼超過了 40 行,就該考慮是否可以在不損害程序結構的前提下進行分拆。

3 縮進排版

4 個空格作爲縮進排版的一個單位,不使用製表符 tab。

8 個空格作爲換行後的縮進,包括函數調用和賦值。

<a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+instrument" style="text-decoration: none; color: rgb(12, 137, 207);">Instrument</a> i <span class="sy0">=</span>
        someLongexpression_r<span class="br0">(</span>that, NotFit, on, one, line<span class="br0">)</span><span class="sy0">;</span>    <span class="co1">// 推薦</span>
 
<a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+instrument" style="text-decoration: none; color: rgb(12, 137, 207);">Instrument</a> i <span class="sy0">=</span>
    someLongexpression_r<span class="br0">(</span>that, NotFit, on, one, line<span class="br0">)</span><span class="sy0">;</span>        <span class="co1">// 避免</span>

3.1 行長度

儘量避免一行的長度超過 100 個字符。

例外:如果註釋行包含了超過 100 個字符的命令示例或者 url 文字,爲了便於剪切和複製,其長度可以超過 100 個字符。

例外:import 行可以超過限制,因爲很少有人會去閱讀它。這也簡化了編程工具的寫入操作。

3.2 括號

大括號不單獨佔用一行,應緊接着上一行書寫。

<span class="kw1">class</span> MyClass <span class="br0">{</span>
    <span class="kw4">int</span> func<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
        <span class="kw1">if</span> <span class="br0">(</span>something<span class="br0">)</span> <span class="br0">{</span>
            <span class="co1">// ...</span>
        <span class="br0">}</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">(</span>somethingElse<span class="br0">)</span> <span class="br0">{</span>
            <span class="co1">// ...</span>
        <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
            <span class="co1">// ...</span>
        <span class="br0">}</span>
    <span class="br0">}</span>
<span class="br0">}</span>

我們需要用大括號來包裹條件語句塊。不過也有例外,如果整個條件語句塊(條件和語句本身)都能容納在一行內,也可以(但不是必須)把它們放入同一行中。也就是說,這是合法的:

<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    body<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>                    	  <span class="co1">// 推薦</span>
<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> body<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>    <span class="co1">// 避免</span>
<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span>
    body<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>          	  <span class="co1">// 錯誤</span>

3.3 換行

當一個表達式無法容納在一行內時,可以依據如下一般規則斷開:

  • 在一個逗號後面斷開。
  • 在一個操作符前面斷開。
  • 寧可選擇較高級別的斷開,而非較低級別的斷開。
  • 新的一行應該與上一行同一級別表達式的開頭處對齊。
  • 如果以上規則導致你的代碼混亂或者使你的代碼都堆擠在右邊,那就代之以縮進 8個空格。

以下是斷開方法調用的一些例子:

someMethod<span class="br0">(</span>longExpression1, longExpression2, longExpression3,
        longExpression4, longExpression5<span class="br0">)</span><span class="sy0">;</span>
 
var <span class="sy0">=</span> someMethod1<span class="br0">(</span>longExpression1,
                      someMethod2<span class="br0">(</span>longExpression2,
                                    longExpression3<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>

以下是兩個斷開算術表達式的例子。前者更好,因爲斷開處位於括號表達式的外邊,這是個較高級別的斷開。

long1 <span class="sy0">=</span> long2 <span class="sy0">*</span> <span class="br0">(</span>long3 <span class="sy0">+</span> long4 <span class="sy0">-</span> long5<span class="br0">)</span>
        <span class="sy0">+</span> <span class="nu0">4</span> <span class="sy0">*</span> longname6<span class="sy0">;</span>             <span class="co1">// 推薦</span>
 
long1 <span class="sy0">=</span> long2 <span class="sy0">*</span> <span class="br0">(</span>long3 <span class="sy0">+</span> long4
               <span class="sy0">-</span> long5<span class="br0">)</span> <span class="sy0">+</span> <span class="nu0">4</span> <span class="sy0">*</span> long6<span class="sy0">;</span> <span class="co1">// 避免</span>

以下是兩個縮進方法聲明的例子。前者是常規情形。後者若使用常規的縮進方式將會使第二行和第三行移得很靠右,所以代之以縮進 8 個空格。

<span class="co1">// 常規縮進</span>
someMethod<span class="br0">(</span><span class="kw4">int</span> anArg, <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+object" style="text-decoration: none; color: rgb(12, 137, 207);">Object</a> anotherArg, <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> yetAnotherArg,
            <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+object" style="text-decoration: none; color: rgb(12, 137, 207);">Object</a> andStillAnother<span class="br0">)</span> <span class="br0">{</span>
...
<span class="br0">}</span>
<span class="co1">// 爲避免太靠右,用8個空格縮進</span>
<span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">synchronized</span>	horkingLongMethodName<span class="br0">(</span><span class="kw4">int</span> anArg,
        <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+object" style="text-decoration: none; color: rgb(12, 137, 207);">Object</a> anotherArg, <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> yetAnotherArg,
        <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+object" style="text-decoration: none; color: rgb(12, 137, 207);">Object</a> andStillAnother<span class="br0">)</span> <span class="br0">{</span>
...
<span class="br0">}</span>

if 語句的換行通常使用 8 個空格的規則,因爲常規縮進(4 個空格)會使語句體看起來比較費勁。比如:

<span class="co1">// 請不要使用這種縮進</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="br0">(</span>condition1 <span class="sy0">&&</span> condition2<span class="br0">)</span>
    <span class="sy0">||</span> <span class="br0">(</span>condition3 <span class="sy0">&&</span> condition4<span class="br0">)</span>
    <span class="sy0">||!</span><span class="br0">(</span>condition5 <span class="sy0">&&</span> condition6<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>    <span class="co1">// 不好的縮進</span>
    doSomethingAboutIt<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>               <span class="co1">// 該行和if條件處於同一級</span>
<span class="br0">}</span>                                       <span class="co1">// 避免</span>
 
<span class="co1">// 使用這種縮進</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="br0">(</span>condition1 <span class="sy0">&&</span> condition2<span class="br0">)</span>
        <span class="sy0">||</span> <span class="br0">(</span>condition3 <span class="sy0">&&</span> condition4<span class="br0">)</span>
        <span class="sy0">||!</span><span class="br0">(</span>condition5 <span class="sy0">&&</span> condition6<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
    doSomethingAboutIt<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>                                       <span class="co1">// 推薦</span>
 
<span class="co1">// 或這種</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="br0">(</span>condition1 <span class="sy0">&&</span> condition2<span class="br0">)</span> <span class="sy0">||</span> <span class="br0">(</span>condition3 <span class="sy0">&&</span> condition4<span class="br0">)</span>
        <span class="sy0">||!</span><span class="br0">(</span>condition5 <span class="sy0">&&</span> condition6<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
    doSomethingAboutIt<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>                                       <span class="co1">// 推薦</span>

這裏有三種可行的方法用於處理三元運算表達式:

alpha <span class="sy0">=</span> <span class="br0">(</span>aLongBooleanExpression<span class="br0">)</span> <span class="sy0">?</span> beta <span class="sy0">:</span> gamma<span class="sy0">;</span>
alpha <span class="sy0">=</span> <span class="br0">(</span>aLongBooleanExpression<span class="br0">)</span> <span class="sy0">?</span> beta
                                 <span class="sy0">:</span> gamma<span class="sy0">;</span>
 
alpha <span class="sy0">=</span> <span class="br0">(</span>aLongBooleanExpression<span class="br0">)</span>
        <span class="sy0">?</span> beta
        <span class="sy0">:</span> gamma<span class="sy0">;</span>

4 註釋

Java 程序有兩類註釋:實現註釋(使用/*…*/和//界定的註釋)和文檔註釋(由/**…*/界定,可通過 javadoc 工具轉換成 html 文件)。

實現註釋用以註釋代碼或者實現細節。文檔註釋從實現自由的角度描述代碼的規範。它可以被那些手頭沒有源碼的開發人員讀懂。

註釋應被用來給出代碼的總括,並提供代碼自身沒有提供的附加信息。註釋應該僅包含與閱讀和理解程序有關的信息。例如,相應的包如何被建立或位於哪個目錄下之類的信息不應包括在註釋中。

在註釋裏,對設計決策中重要的或者不是顯而易見的地方進行說明是可以的,但應避免提供代碼中己清晰表達出來的重複信息。

註釋不應寫在用星號或其他字符畫出來的大框裏。註釋不應包括諸如製表符和回退符之類的特殊字符。

使用 Javadoc 標準註釋,每個文件的開頭都應該有一句版權說明。然後下面應該是package 包語句和 import 語句,每個語句塊之間用空行分隔。然後是類或接口的定義。在Javadoc 註釋中,應描述類或接口的用途。

<span class="coMULTI">/*
 * Copyright (c) 2014 SIMCOM, Inc.
 * All Rights Reserved.
 * SIMCOM Proprietary and Confidential.
 */</span>
 
<span class="kw1">package</span> <span class="co2">com.android.internal.foo</span><span class="sy0">;</span>
 
<span class="kw1">import</span> <span class="co2">android.os.Blah</span><span class="sy0">;</span>
<span class="kw1">import</span> <span class="co2">android.view.Yada</span><span class="sy0">;</span>
 
<span class="kw1">import</span> <span class="co2">java.sql.ResultSet</span><span class="sy0">;</span>
<span class="kw1">import</span> <span class="co2">java.sql.SQLException</span><span class="sy0">;</span>
 
<span class="co3">/**
 * 一句話功能描述
 * 功能詳細描述
 * @see 相關類/方法
 * @deprecated
 */</span>
 
<span class="kw1">public</span> <span class="kw1">class</span> Foo <span class="br0">{</span>
    ...
<span class="br0">}</span>

每個類和自建的 public 方法必須包含 Javadoc 註釋,註釋至少要包含描述該類或方法用途的語句。並且該語句應該用第三人稱的動詞形式來開頭

<span class="co3">/** Returns the correctly rounded positive square root of a double
value. */</span>
<span class="kw1">static</span> <span class="kw4">double</span> sqrt<span class="br0">(</span><span class="kw4">double</span> a<span class="br0">)</span> <span class="br0">{</span>
    ...
<span class="br0">}</span>
 
or
 
<span class="co3">/**
 * Constructs a new String by converting the specified array of 
 * bytes using the platform's default character encoding.
 */</span>
<span class="kw1">public</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a><span class="br0">(</span><span class="kw4">byte</span><span class="br0">[</span><span class="br0">]</span> bytes<span class="br0">)</span> <span class="br0">{</span>
    ...
<span class="br0">}</span>

如果所有的 Javadoc 都會寫成“sets Foo”,對於那些無關緊要的類似 setFoo()的 get和 set 語句是不必撰寫 Javadoc 的。如果方法執行了比較複雜的操作(比如執行強制約束或者產生很重要的副作用),那就必須進行註釋。如果“Foo”屬性的意義不容易理解,也應該進行註釋。

無論是 public 的還是其它類型的,所有自建的方法都將受益於 Javadoc。public 的方法是 API 的組成部分,因此更需要 Javadoc。

4.1 實現註釋的格式

程序可以有 4 種實現註釋的風格:塊、單行、尾端和行末。

4.1.1 塊註釋

塊註釋通常用於提供對文件,方法,數據結構和算法的描述。塊註釋被置於每個文件的開始處以及每個方法之前。它們也可以用於其他地方,比如方法內部。在功能和方法內部的塊註釋應該和它們所描述的代碼具有一樣的縮進格式。

塊註釋之首應該有一個空行,用於塊註釋和代碼分割開來,比如:

<span class="coMULTI">/*
 * Here is a block comment.
 */</span>

塊註釋可以以/*-開頭,這樣indent(1)就可以將之識別爲一個代碼塊的開始,而不是重排它。

<span class="coMULTI">/*-
  * Here is a block comment with some very special
  * formatting that I want indent(1) to ignore.
  *
  *    one
  *        two
  *            three
  */</span>

4.1.2 單行註釋

短註釋可以顯示在一行內,並與其後的代碼具有一樣的縮進層級。如果一個註釋不能在一行內寫完,就該採用塊註釋(參見“塊註釋”)。單行註釋之前應該有一個空行。以下是一個Java代碼中單行註釋的例子:

        <span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
            <span class="coMULTI">/* Handle the condition. */</span>
            …
        <span class="br0">}</span>

4.1.3 尾端註釋

極短的註釋可以與它們所要描述的代碼位於同一行,但是應該有足夠的空白來分開代碼和註釋。若有多個短註釋出現於大量代碼中,它們應該具有相同的縮進。

以下是一個Java代碼中尾端註釋的例子:

        <span class="kw1">if</span> <span class="br0">(</span>a <span class="sy0">==</span> <span class="nu0">2</span><span class="br0">)</span> <span class="br0">{</span>
            <span class="kw1">return</span> <span class="kw2">true</span><span class="sy0">;</span> 			<span class="coMULTI">/* special case */</span>
        <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
            <span class="kw1">return</span> isPrime<span class="br0">(</span>a<span class="br0">)</span><span class="sy0">;</span>	<span class="coMULTI">/* works only for odd a */</span>
        <span class="br0">}</span>

4.1.4 行末註釋

註釋界定符“//”,可以註釋掉整行或者一行中的一部分。它一般不用於連續多行的註釋文本;然而,它可以用來註釋掉連續多行的代碼段。以下是所有三種風格的例子:

        <span class="kw1">if</span> <span class="br0">(</span>foo <span class="sy0">></span> <span class="nu0">1</span><span class="br0">)</span> <span class="br0">{</span>
            <span class="co1">// Do a double-flip.</span>
            ...
        <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
            <span class="kw1">return</span> <span class="kw2">false</span><span class="sy0">;</span>		<span class="co1">// Explain why here.</span>
        <span class="br0">}</span>
 
        <span class="co1">//if (bar > 1) {</span>
        <span class="co1">//</span>
        <span class="co1">//    // Do a triple-flip.</span>
        <span class="co1">//    ...</span>
        <span class="co1">//} else {</span>
        <span class="co1">//    return false;</span>
        <span class="co1">//}</span>

4.2 文檔註釋

文檔註釋描述 Java 的類、接口、構造器,方法,以及字段。每個文檔註釋都會被置於註釋定界符/**…*/之中,一個註釋對應一個類、接口或成員。

該註釋應位於聲明之前:

<span class="co3">/**
 * The Example class providers ...
 */</span>
<span class="kw1">public</span> <span class="kw1">class</span> Example <span class="br0">{</span>
    ...
<span class="br0">}</span>

注意頂層的類和接口是不縮進的,而其成員是縮進的。描述類和接口的文檔註釋的第一行(/**)不需縮進,隨後的文檔註釋每行都縮進 1 格(使星號縱向對齊)。成員,包括構造函數在內,其文檔註釋的第一行縮進 4 格,隨後每行都縮進 5 格。

若你想給出有關類、接口、變量或方法的信息,而這些信息又不適合寫在文檔中,則可使用實現塊註釋或緊跟在聲明後面的單行註釋。例如,有關一個類實現的細節,應放入緊跟在類聲明後面的實現塊註釋中,而不是放在文檔註釋中。

文檔註釋不能放在一個方法或構造器的定義塊中,因爲 Java 會將位於文檔註釋之後的第一個聲明與其相關聯。

4.2.1 類註釋

在類、接口定義之前當對其進行註釋,包括類、接口的目的、作用、功能、繼承於何種父類,實現的接口、實現的算法、使用方法、示例程序等。

<span class="co3">/**
 * 一句話功能描述
 * 功能詳細描述
 * @see 相關類/方法
 * @deprecated
 */</span>

4.2.2 方法註釋

據標準Javadoc規範對方法進行註釋,以明確該方法功能、作用、各參數含義以及返回值等。複雜的算法用/**/在方法內註解出。

  • 參數註釋時當註明其取值範圍等。
  • 返回值當註釋出失敗、錯誤、異常時的返回情況。
  • 異常當註釋出什麼情況、什麼時候、什麼條件下會引發什麼樣的異常。
<span class="co3">/**
 * 一句話方法描述
 * 方法詳細描述
 * @param 參數名 參數描述
 * @param 參數名2 參數描述
 * @return 返回值類型說明
 * @throws Exception 異常說明
 * @see 類/方法/成員
*/</span>

4.2.3 類成員變量和常量註釋

成員變量和常量需要使用javadoc形式的註釋,以說明當前變量或常量的含義。

<span class="co3">/**
 * 成員變量描述
 */</span>
<span class="kw1">private</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> test<span class="sy0">;</span>
 
<span class="co3">/** 成員變量描述 */</span>
<span class="kw1">private</span> <span class="kw4">int</span> hello<span class="sy0">;</span>

5 聲明

5.1 每行聲明變量的數量

推薦一行一個聲明,因爲這樣以利於寫註釋。

<span class="kw4">int</span> level<span class="sy0">;</span>	 <span class="co1">// indentation level</span>
<span class="kw4">int</span> size<span class="sy0">;</span>        <span class="co1">// size of table</span>

注意:上面的例子中,在類型和標識符之間放了一個空格,另一種被允許的替代方式是使用製表符。

<span class="kw4">int</span>		level<span class="sy0">;</span>		<span class="co1">// indentation level</span>
<span class="kw4">int</span>		size<span class="sy0">;</span>		<span class="co1">// size of table</span>
<span class="kw4">char</span>		username<span class="sy0">;</span>	<span class="co1">// username</span>

5.2 初始化

儘量在聲明局部變量的同時初始化。唯一不這麼做的理由是變量的初始值依賴於某些先前發生的計算。

5.3 佈局

只在代碼塊的開始處聲明變量(一個塊是指任何被包含在大括號”{“和”}“中間的代碼)。不要在首次用到該變量時才聲明之。這會把注意力不集中的程序員搞糊塗,同時會妨礙代碼在該作用域內的可移植性。

<span class="kw4">void</span> myMethod<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
<span class="kw4">int</span> int1 <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>		<span class="co1">// beginning of method block</span>
 
    <span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
        <span class="kw4">int</span> int2 <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>	<span class="co1">// beginning of “if” block</span>
        ...
    <span class="br0">}</span>
<span class="br0">}</span>

避免聲明的局部變量覆蓋上一級聲明的變量。例如,不要在內部代碼塊中聲明相同的變量名:

<span class="kw4">int</span> count<span class="sy0">;</span>
...
<span class="me1">myMethod</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
        <span class="kw4">int</span> count <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>     <span class="co1">// 避免</span>
        ...
   <span class="br0">}</span>
   ...
<span class="br0">}</span>

5.4 類和接口的聲明

當編寫類和接口是,應該(強制)遵守以下格式規則:

  • 在方法名與其參數列表之前的左括號”(“間不要有空格。
  • 左大括號”{“位於聲明語句同行的末尾。
  • 右大括號”}“另起一行,與相應的聲明語句對齊。除非是一個空語句,”}“應緊跟在”{“之後。
  • 方法與方法之間以空行分隔。
<span class="kw1">class</span> Sample <span class="kw1">extends</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+object" style="text-decoration: none; color: rgb(12, 137, 207);">Object</a> <span class="br0">{</span>
    <span class="kw4">int</span> ivar1<span class="sy0">;</span>
    <span class="kw4">int</span> ivar2<span class="sy0">;</span>
 
    Sample<span class="br0">(</span><span class="kw4">int</span> i,	<span class="kw4">int</span> j<span class="br0">)</span> <span class="br0">{</span>
        ivar1 <span class="sy0">=</span> i<span class="sy0">;</span>
        ivar2 <span class="sy0">=</span> j<span class="sy0">;</span>
    <span class="br0">}</span>
 
    <span class="kw4">int</span> emptyMethod<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
    ...
<span class="br0">}</span>

6 語句

6.1 簡單語句

每行之多包含一條語句,例如:

    argv<span class="sy0">++;</span>     	<span class="co1">// 推薦</span>
    argc<span class="sy0">--;</span>     	<span class="co1">// 推薦</span>
    argv<span class="sy0">++;</span> argc<span class="sy0">--;</span> 	<span class="co1">// 避免</span>

6.2 複合語句

複合語句是包含在大括號中的語句序列,形如”{ 語句 }“。例如下面各段。

  • 被括其中的語句應該較之複合語句縮進一個層次。
  • 左大括號”{“應位於複合語句起始行的行尾,右大括號”}“應另起一行並與複合語句首行對齊。
  • 大括號可以被用於所有語句,包括單個語句,只要這些語句是諸如 if-else 或 for 控制結構的一部分。這樣便於添加語句而無需擔心由於忘了加括號而引入 bug。

6.3 返回語句

一個帶返回值的return語句不使用小括號”()“,除非它們以某種方式使返回值更爲顯見。例如:

<span class="kw1">return</span><span class="sy0">;</span>
 
<span class="kw1">return</span> myDisk.<span class="me1">size</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>                  <span class="co1">// 避免</span>
 
<span class="kw1">return</span> <span class="br0">(</span>size <span class="sy0">?</span> size <span class="sy0">:</span> defaultSize<span class="br0">)</span><span class="sy0">;</span>    <span class="co1">// 避免</span>

6.4 if, if-else, if else-if else 語句

if-else語句應該具有如下格式:

<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>
 
<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>
 
<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">else</span><span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>

注意:if語句總是用”{“和”}“括起來,避免使用如下容易引起錯誤的格式:

<span class="kw1">if</span> <span class="br0">(</span>condition<span class="br0">)</span>  <span class="co1">// 避免</span>
    statement<span class="sy0">;</span>

6.5 for 語句

一個for語句應該具有如下格式:

<span class="kw1">for</span> <span class="br0">(</span>initialization<span class="sy0">;</span> condition<span class="sy0">;</span> update<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>

當在for語句的初始化或更新子句中使用逗號時,避免因使用三個以上變量,而導致複雜度提高。若需要,可以在for循環之前(爲初始化子句)或for循環末尾(爲更新子句)使用單獨的語句。

6.6 while 語句

一個while語句應該具有如下格式:

<span class="kw1">while</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>

6.7 do-while 語句

<span class="kw1">do</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">while</span> <span class="br0">(</span>condition<span class="br0">)</span><span class="sy0">;</span>

6.8 switch 語句

一個switch語句應該具有如下格式:

<span class="kw1">switch</span> <span class="br0">(</span>condition<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">case</span> ABC<span class="sy0">:</span>
        statements<span class="sy0">;</span>
        <span class="coMULTI">/* falls through */</span>
    <span class="kw1">case</span> DEF<span class="sy0">:</span>
        statements<span class="sy0">;</span>
        <span class="kw1">break</span><span class="sy0">;</span>
 
    <span class="kw1">case</span> XYZ<span class="sy0">:</span>
        statements<span class="sy0">;</span>
        <span class="kw1">break</span><span class="sy0">;</span>
 
    <span class="kw1">default</span><span class="sy0">:</span>
        statements<span class="sy0">;</span>
        <span class="kw1">break</span><span class="sy0">;</span>
<span class="br0">}</span>

每當一個case順着往下執行時(因爲沒有break語句),通常應在break語句的位置添加註釋。上面的示例代碼中就包含註釋/* falls through */。

6.9 try-catch 語句

一個try-catch語句應該具有如下格式:

<span class="kw1">try</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span>ExceptionClass	e<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>
 
<span class="kw1">try</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span>ExceptionClass	e<span class="br0">)</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">finally</span> <span class="br0">{</span>
    statements<span class="sy0">;</span>
<span class="br0">}</span>

7 空白

7.1 空行

空行將邏輯相關的代碼段分隔開,以提高可讀性。下列情況應該總是使用空行:

  • 一個源文件的兩個片段(section)之間。
  • 類聲明和接口聲明之間。
  • 兩個方法之間。
  • 方法內的局部變量和方法的第一條語句之間。
  • 塊註釋或單行註釋之前。
  • 一個方法內的兩個邏輯段之間,用以提高可讀性。

7.2 空格

下列情況應該使用空格:

  • 一個緊跟着括號的關鍵字應該被空格分開。例如:
<span class="kw1">while</span> <span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span> <span class="br0">{</span>
    ...
<span class="br0">}</span>
  • 空白應該位於參數列表中逗號的後面。
  • 所有的二元運算符,除了”.”,都應該使用空格將之與操作數分開。一元操作符和操作數之間不應該加空格,比如:負號(”-”)、自增(”++”)和自減(”–”)。例如:
a <span class="sy0">+=</span> c <span class="sy0">+</span> d<span class="sy0">;</span>
a <span class="sy0">=</span> <span class="br0">(</span>a <span class="sy0">+</span> b<span class="br0">)</span> <span class="sy0">/</span> <span class="br0">(</span>c <span class="sy0">*</span> d<span class="br0">)</span><span class="sy0">;</span>
 
<span class="kw1">while</span> <span class="br0">(</span>d<span class="sy0">++</span> <span class="sy0">=</span> s<span class="sy0">++</span><span class="br0">)</span> <span class="br0">{</span>
    n<span class="sy0">++;</span>
<span class="br0">}</span>
  • for語句中的表達式應該被空格分開。
<span class="kw1">for</span> <span class="br0">(</span>expr1<span class="sy0">;</span> expr2<span class="sy0">;</span> expr3<span class="br0">)</span>
  • 強制轉型後應該跟一個空格。
myMethod<span class="br0">(</span><span class="br0">(</span><span class="kw4">byte</span><span class="br0">)</span> aNum, <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+object" style="text-decoration: none; color: rgb(12, 137, 207);">Object</a><span class="br0">)</span> x<span class="br0">)</span><span class="sy0">;</span>
myMethod<span class="br0">(</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">(</span>cp <span class="sy0">+</span> <span class="nu0">5</span><span class="br0">)</span>, <span class="br0">(</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">(</span>i <span class="sy0">+</span> <span class="nu0">3</span><span class="br0">)</span><span class="br0">)</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">)</span><span class="sy0">;</span>

8 命名規範

命名規範使程序更易讀,從而更易於理解。它們也可以提供一些有關標識符功能的信息,以助於理解代碼。

8.1 包命名

包名由全部小寫字母組成,包名的前綴以com開頭,包名後續部分的格式爲:

[域名反轉].[項目名].[模塊名].[子模塊名]…

例如:com.android.sim.message.sms

8.2 類和接口命名

類名是個一名詞,採用大小寫混合的方式,每個單詞的首字母大寫。儘量使你的類名簡潔而富於描述。使用完整單詞,或約定成俗並且使用廣泛的縮寫詞,如url,html,接口和類名規則一至但要使用I前綴。

繼承自系統組件類的命名,後綴必須明確表示出系統組件的類別,Activity類後綴使用Activity,Service類後綴使用Service,BroadcaseReceiver類後綴使用Receiver,ContentProvider使用Provider。

8.3 方法命名

方法名是一個動詞或者動名詞結構,採用大小寫混合的方式,第一個單詞的首字母小寫,其後單詞的首字母大寫,即駝峯命名規則。

以它做什麼來命名,而不是以它怎樣做命名。如doUpdate(),isNumber()。

8.4 變量命名

第一個單詞的首字母小寫,其後單詞的首字母大寫。變量名不應以下劃線或美元符號開頭,儘管這在語法上是允許的。變量名的選用應該易於記憶,即,能夠指出其用途。儘量避免單個字符的變量名,除非是一次性的臨時變量。臨時變量通常被取名爲 i,j,k,m 和 n,它們一般用於整型;c,d,e,它們一般用於字符型。

其中系統控件中在後綴中體現控件類型,如下所示:

組件名稱 簡寫 組件名稱 簡寫
Button Btn RadioButton Rbtn
ImageButton Ibtn TextView Tv
ImageView Iv ListView Lv
ProgressBar Pbar EditText Et
ScrollView Sv CheckBox Cb
RelativeLayout Rly LinearLayout Lly
TableLayout Tly LinearLayout Aly
FrameLayout Fly    

非 public 的、非 static 的字段名稱以 m 開頭。

static 字段名稱以 s 開頭。

其它字段以小寫字母開頭。

<span class="kw1">public</span>	<span class="kw1">class</span> MyClass <span class="br0">{</span>
    <span class="kw1">public</span> <span class="kw4">int</span> publicField<span class="sy0">;</span>
    <span class="kw1">private</span> <span class="kw1">static</span> MyClass sSingleton<span class="sy0">;</span>
    <span class="kw4">int</span> mPackagePrivate<span class="sy0">;</span>
    <span class="kw1">private</span> <span class="kw4">int</span> mPrivate<span class="sy0">;</span>
    <span class="kw1">protected</span> <span class="kw4">int</span> mProtected<span class="sy0">;</span>
<span class="br0">}</span>

8.5 常量命名

類常量的聲明,應該全部大寫,單詞間用下劃線隔開。

<span class="kw1">static</span> <span class="kw1">final</span> <span class="kw4">int</span> MIN_WIDTH <span class="sy0">=</span> <span class="nu0">4</span><span class="sy0">;</span>
<span class="kw1">static</span> <span class="kw1">final</span> <span class="kw4">int</span> MAX_WIDTH <span class="sy0">=</span> <span class="nu0">999</span><span class="sy0">;</span>
<span class="kw1">static</span> <span class="kw1">final</span> <span class="kw4">int</span> GET_THE_CPU <span class="sy0">=</span> <span class="nu0">1</span><span class="sy0">;</span>

8.6 異常命名

自定義異常的命名必須以Exception爲結尾,已明確標示爲一個異常。

異常實例一般使用e、ex等,在多個異常時使用該異常名或簡寫加E,Ex等組成,如:SQLEx,ActionEx。

8.7 Layout 命名

命名必須以全部單詞小寫,單詞間以下劃線分割,並且儘可能的使用名詞或名詞組,即使用 模塊名_功能名稱 來命名。

addressbook_list.<span class="me1">xml</span>	<span class="co1">// 推薦</span>
list_addressbook.<span class="me1">xml</span>	<span class="co1">// 避免</span>

8.8 資源 ID 命名

layout中所使用的id命名必須以全部單詞小寫,單詞間以下劃線分割,並且儘可能的使用名詞或名詞組,並且要求能夠通過id直接理解當前組件要實現的功能。

EditText名 @<span class="sy0">+</span>id<span class="sy0">/</span>book_name_edit	<span class="co1">// 推薦</span>
EditText名 @<span class="sy0">+</span>id<span class="sy0">/</span>textbookname	<span class="co1">// 避免</span>

8.9 Activity 中 View 命名

採用大小寫混合模式,第一個單詞首字母小寫,其餘單詞首字母大寫最後一個單詞爲該View 類型的縮寫,格式如下:

邏輯名+View 類型縮寫(View 縮寫參照 8.4 組件名稱縮寫表)。

Button homeBtn

8.10 strings.xml 中 ID 命名

命名必須以全部單詞小寫,單詞間以下劃線分割,並且儘可能的使用名詞或名詞組,格式如下:

  • activity名稱_功能模塊名稱_邏輯名稱 或
  • activity名稱_邏輯名稱 或
  • common_邏輯名稱

邏輯名稱多個單詞用下劃線連接,同時使用activity名稱註釋。

main_menu_about
main_title
common_exit
common_app_name

8.11 資源命名

layout中使用的所有資源(如drawable,style等)命名必須以全部單詞小寫,單詞間以下劃線分割,並且儘可能的使用名詞或名詞組,即使用模塊名_用途來命名。如果爲公共資源,如分割線等,則直接用用途來命名。如:menu_icon_navigate.png

9 編程規範

9.1 單位規範

在使用單位時,如果沒有特殊情況,一律使用 sp 作爲文字大小的單位,將 dip 作爲其他元素的單位。因爲這兩個單位是與設備分辨率無關的,能夠解決在不同分辨率的設備上顯示效果不同的問題。另外,在編碼中定義控件的 margin 或 padding 屬性時,SDK 裏面並沒有提供 dip 單位的 api 設置接口,而是提供了默認的 px 設置。

<a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+button" style="text-decoration: none; color: rgb(12, 137, 207);">Button</a> btn <span class="sy0">=</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+button" style="text-decoration: none; color: rgb(12, 137, 207);">Button</a><span class="br0">(</span>context<span class="br0">)</span><span class="sy0">;</span>
LayoutParams lp <span class="sy0">=</span> <span class="kw1">new</span>
        LayoutParams<span class="br0">(</span>LayoutParams.<span class="me1">FILL_PARENT</span>,LayoutParams.<span class="me1">FILL_PARENT</span><span class="br0">)</span><span class="sy0">;</span>
lp.<span class="me1">setMargins</span><span class="br0">(</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy0">;</span> 
btn.<span class="me1">setTextSize</span><span class="br0">(</span><span class="nu0">12</span><span class="br0">)</span><span class="sy0">;</span> 
btn.<span class="me1">setPadding</span><span class="br0">(</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy0">;</span>

這個時候,一般在設置 margin 和 padding 時,應該對要設置的 dip 值轉換爲 px 單位,而字體的大小設置中,系統默認給出了 sp 的單位,所以可以不用進行轉換。轉換的方法參考下面的代碼:

<span class="co3">/**
 * 把dip單位轉成px單位
 * @param context context對象
 * @param dip dip數值
 * @return dip對應的px值
 */</span>
<span class="kw1">public</span> <span class="kw1">static</span> <span class="kw4">int</span> formatDipToPx<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+context" style="text-decoration: none; color: rgb(12, 137, 207);">Context</a> context, <span class="kw4">int</span> dip<span class="br0">)</span> <span class="br0">{</span>
    DisplayMetrics dm <span class="sy0">=</span> <span class="kw1">new</span> DisplayMetrics<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">(</span><span class="br0">(</span>Activity<span class="br0">)</span>context<span class="br0">)</span>.<span class="me1">getWindowManager</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">getDefaultDisplay</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">getMetrics</span><span class="br0">(</span>dm<span class="br0">)</span><span class="sy0">;</span>
    <span class="kw4">int</span> dip <span class="sy0">=</span> <span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+math" style="text-decoration: none; color: rgb(12, 137, 207);">Math</a>.<span class="me1">ceil</span><span class="br0">(</span>dip <span class="sy0">*</span> dm.<span class="me1">density</span><span class="br0">)</span><span class="sy0">;</span>
    <span class="kw1">return</span> dip<span class="sy0">;</span>
<span class="br0">}</span>

9.2 引用類變量和類方法

避免用一個對象訪問一個類的靜態變量和方法。應該用類名替代。

classMethod<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>               <span class="co1">// 推薦</span>
AClass.<span class="me1">classMethod</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>        <span class="co1">// 推薦</span>
anObject.<span class="me1">classMethod</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>      <span class="co1">// 避免</span>

9.3 常量

位於 for 循環中作爲計數器值的數字常量,除了-1,0 和 1 之外,不應被直接寫入代碼。

9.4 變量賦值

避免在一個語句中給多個變量賦相同的值,它很難讀懂。

9.5 信令類

如果類只是用來作爲信息傳遞的中間變量,則應該聲明爲信令類,即所有的全局變量都是 final 類型,在初始化時賦值。

<span class="kw1">private</span> <span class="kw1">final</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> name<span class="sy0">;</span>
<span class="kw1">public</span> Foo<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> str<span class="br0">)</span> <span class="br0">{</span>
    name <span class="sy0">=</span> str<span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">public</span> Foo<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> str <span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">this</span>.<span class="me1">str</span> <span class="sy0">=</span> str<span class="sy0">;</span>                   <span class="co1">// 避免在構造函數中出現this引用</span>
<span class="br0">}</span>

9.6 不要忽略異常

有時,完全忽略異常是非常誘人的。

<span class="kw4">void</span> setServerPort<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> value<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">try</span> <span class="br0">{</span>
        serverPort <span class="sy0">=</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer" style="text-decoration: none; color: rgb(12, 137, 207);">Integer</a>.<span class="me1">parseInt</span><span class="br0">(</span>value<span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+numberformatexception" style="text-decoration: none; color: rgb(12, 137, 207);">NumberFormatException</a> e<span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>    <span class="co1">// 錯誤</span>
<span class="br0">}</span>

絕對不要這麼做。也許你會認爲:你的代碼永遠不會碰到這種出錯的情況,或者處理異常並不重要,可類似上述忽略異常的代碼將會在代碼中埋下一顆地雷,說不定哪天它就會炸到某個人了。你必須在代碼中以某種規矩來處理所有的異常。根據情況的不同,處理的方式也會不一樣。可接受的替代方案包括(按照推薦順序):向方法的調用者拋出異常。

<span class="kw4">void</span> setServerPort<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> value<span class="br0">)</span> <span class="kw1">throws</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+numberformatexception" style="text-decoration: none; color: rgb(12, 137, 207);">NumberFormatException</a> <span class="br0">{</span>
    serverPort <span class="sy0">=</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer" style="text-decoration: none; color: rgb(12, 137, 207);">Integer</a>.<span class="me1">parseInt</span><span class="br0">(</span>value<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>

根據抽象級別拋出新的異常。

<span class="kw4">void</span> setServerPort<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> value<span class="br0">)</span> <span class="kw1">throws</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+configurationexception" style="text-decoration: none; color: rgb(12, 137, 207);">ConfigurationException</a> <span class="br0">{</span>
    <span class="kw1">try</span> <span class="br0">{</span>
        serverPort <span class="sy0">=</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer" style="text-decoration: none; color: rgb(12, 137, 207);">Integer</a>.<span class="me1">parseInt</span><span class="br0">(</span>value<span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+numberformatexception" style="text-decoration: none; color: rgb(12, 137, 207);">NumberFormatException</a> e<span class="br0">)</span> <span class="br0">{</span>
        <span class="kw1">throw</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+configurationexception" style="text-decoration: none; color: rgb(12, 137, 207);">ConfigurationException</a><span class="br0">(</span><span class="st0">"Port "</span> <span class="sy0">+</span> value <span class="sy0">+</span> <span class="st0">" is not valid."</span><span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span>
<span class="br0">}</span>

默默地處理錯誤並在 catch {} 語句塊中替換爲合適的值。

<span class="co3">/**	設置端口。假如值不是數字則用80代替 */</span>
<span class="kw4">void</span> setServerPort<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> value<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">try</span> <span class="br0">{</span>
        serverPort <span class="sy0">=</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer" style="text-decoration: none; color: rgb(12, 137, 207);">Integer</a>.<span class="me1">parseInt</span><span class="br0">(</span>value<span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span>	<span class="kw1">catch</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+numberformatexception" style="text-decoration: none; color: rgb(12, 137, 207);">NumberFormatException</a> e<span class="br0">)</span> <span class="br0">{</span>
        serverPort <span class="sy0">=</span> <span class="nu0">80</span><span class="sy0">;</span> 	<span class="co1">// 服務默認端口</span>
    <span class="br0">}</span>
<span class="br0">}</span>

捕獲異常並拋出一個新的 RuntimeException。這種做法比較危險:只有確信發生該錯誤時最合適的做法就是崩潰,纔會這麼做。

<span class="co3">/**	設置端口,假如值不是數字則程序終止。 */</span>
<span class="kw4">void</span> setServerPort<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> value<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">try</span> <span class="br0">{</span>
        serverPort <span class="sy0">=</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer" style="text-decoration: none; color: rgb(12, 137, 207);">Integer</a>.<span class="me1">parseInt</span><span class="br0">(</span>value<span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+numberformatexception" style="text-decoration: none; color: rgb(12, 137, 207);">NumberFormatException</a> e<span class="br0">)</span> <span class="br0">{</span>
        <span class="kw1">throw</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+runtimeexception" style="text-decoration: none; color: rgb(12, 137, 207);">RuntimeException</a><span class="br0">(</span><span class="st0">"port "</span> <span class="sy0">+</span> value <span class="st0">" is invalid, "</span>, e<span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span>
<span class="br0">}</span>

請記住,最初的異常是傳遞給構造方法的 RuntimeException。如果代碼必須在 Java 1.3 版本下編譯,需要忽略該異常。最後一招:如果確信忽略異常比較合適,那就忽略吧,但必須把理想的原因註釋出來。

<span class="co3">/**	假如值不是數字則使用原來的端口號。 */</span>
<span class="kw4">void</span> setServerPort<span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string" style="text-decoration: none; color: rgb(12, 137, 207);">String</a> value<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">try</span> <span class="br0">{</span>
        serverPort <span class="sy0">=</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer" style="text-decoration: none; color: rgb(12, 137, 207);">Integer</a>.<span class="me1">parseInt</span><span class="br0">(</span>value<span class="br0">)</span><span class="sy0">;</span>
    <span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+numberformatexception" style="text-decoration: none; color: rgb(12, 137, 207);">NumberFormatException</a> e<span class="br0">)</span> <span class="br0">{</span>
        <span class="co1">// 方法記錄:無視無效的用戶輸入。</span>
        <span class="co1">// 服務端口不會被改變。</span>
    <span class="br0">}</span>
<span class="br0">}</span>

9.7 不要捕獲頂級的 Exception

有時在捕獲 Exception 時偷懶也是很吸引人的,類似如下的處理方式:

<span class="kw1">try</span> <span class="br0">{</span>
    someComplicatedIOFunction<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>       	<span class="co1">// 可能拋出IOException</span>
    someComplicatedParsingFunction<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>  	<span class="co1">// 可能拋出ParsingException</span>
    someComplicatedSecurityFunction<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span> 	<span class="co1">// 可能拋出SecurityException</span>
    <span class="co1">// 其他可以拋出Exception的代碼</span>
<span class="br0">}</span> <span class="kw1">catch</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+exception" style="text-decoration: none; color: rgb(12, 137, 207);">Exception</a> e<span class="br0">)</span> <span class="br0">{</span>                	<span class="co1">// 一次性捕獲所有exceptions</span>
    handleError<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>                     	<span class="co1">// 只有一個通用的處理方法!</span>
<span class="br0">}</span>

不要這麼做。絕大部分情況下,捕獲頂級的 Exception 或 Throwable 都是不合適的,Throwable 更不合適,因爲它還包含了 Error 異常。這種捕獲非常危險。這意味着本來不必考慮的 Exception(包括類似 ClassCastException 的 RuntimeException)被捲入到應用程序級的錯誤處理中來。這會讓代碼運行的錯誤變得模糊不清。這意味着,假如別人在你調用的代碼中加入了新的異常,編譯器將無法幫助你識別出各種不同的錯誤類型。絕大部分情況下,無論如何你都不應該用同一種方式來處理各種不同類型的異常。

本規則也有極少數例外情況:期望捕獲所有類型錯誤的特定的測試代碼和頂層代碼(爲了阻止這些錯誤在用戶界面上顯示出來,或者保持批量工作的運行)。這種情況下可以捕獲頂級的 Exception(或 Throwable)並進行相應的錯誤處理。在開始之前,你應該非常仔細地考慮一下,並在註釋中解釋清楚爲什麼這麼做是安全的。

比捕獲頂級 Exception 更好的方案:

  • 分開捕獲每一種異常,在一條 try 語句後面跟隨多個 catch 語句塊。這樣可能會有點彆扭,但總比捕獲所有 Exception 要好些。請小心別在 catch 語句塊中重複執行大量的代碼。
  • 重新組織一下代碼,使用多個 try 塊,使錯誤處理的粒度更細一些。把 IO 從解析內容的代碼中分離出來,根據各自的情況進行單獨的錯誤處理。
  • 再次拋出異常。很多時候在你這個級別根本就沒必要捕獲這個異常,只要讓方法拋出該異常即可。

請記住:異常是你的朋友!當編譯器指出你沒有捕獲某個異常時,請不要皺眉頭。而應該微笑:編譯器幫助你找到了代碼中的運行時(runtime)問題。

9.8 不要使用 Finalizer

Finalizer 提供了一個機會,可以讓對象被垃圾回收器回收時執行一些代碼。

優點:便於執行清理工作,特別是針對外部資源。

缺點:調用 finalizer 的時機並不確定,甚至根本就不會調用。

結論:我們不要使用 finalizers。大多數情況下,可以用優秀的異常處理代碼來執行那些要放入 finalizer 的工作。如果確實是需要使用 finalizer,那就定義一個 close() 方法(或類似的方法),並且在文檔中準確地記錄下需要調用該方法的時機。相關例程可以參見InputStream。這種情況下還是適合使用 finalizer 的,但不需要在 finalizer 中輸出日誌信息,因爲日誌不能因爲這個而被撐爆。

9.9 使用完全限定 Import

當需要使用 foo 包中的 Bar 類時,存在兩種可能的 import 方式:

1. import foo.*;

優點:可能會減少 import 語句。

2. import foo.Bar;

優點:實際用到的類一清二楚。代碼的可讀性更好,便於維護。

結論: 用後一種寫法來 import 所有的 Android 代碼。不過導入 java 標準庫 (java.util.*、java.io.*等) 和單元測試代碼 (junit.framework.*) 時可以例外。

9.10 對 Import 語句排序

import 語句的次序應該如下:

1. Android imports

2. 第三方庫(com、junit、net、org)

3. java 和 javax

爲了精確匹配 IDE 的配置,import 順序應該是:

1. 在每組內部按字母排序,大寫字母排在小寫字母的前面。

2. 每個大組之間應該空一行(android、com、junit、net、org、java、javax)。

3. 原先次序是不作爲規範性要求的。這意味着要麼允許 IDE 改變順序,要麼使用 IDE的開發者不得不禁用 import 自動管理功能並且人工維護 import。這看起來比較糟糕。每當說起 java 規範,推薦的規範到處都是。符合我們要求的差不多就是“選擇一個次序並堅持下去。”於是,我們就選擇一個規範,更新規範手冊,並讓 IDE去遵守它。我們期望:不必耗費更多的精力,用 IDE 編碼的用戶就按照這種規則去 import 所有的 package。

基於以下原因,選定了本項規則:

  • 導入人員期望最先看到的放在最開始位置(android)。
  • 導入人員期望最後纔看到的放在最後(java)。
  • 風格讓人容易遵守。
  • IDE 可以遵守。

靜態 import 的使用和位置已經成爲略帶爭議的話題。有些人願意讓靜態 import 和其它 import 混在一起,另一些人則期望讓它們位於其它 import 之上或者之下。另外,我們還未提到讓所有 IDE 都遵守同一個次序的方法。

因爲大多數人都認爲這部分內容並不要緊,只要遵守你的決定並堅持下去即可。

9.11 限制變量的作用範圍

局部變量的作用範圍應該是限制爲最小的(Effective Java 第 29 條)。使用局部變量,可以增加代碼的可讀性和可維護性,並且降低發生錯誤的可能性。每個變量都應該在最小範圍的代碼塊中進行聲明,該代碼塊的大小只要能夠包含所有對該變量的使用即可。

應該在第一次用到局部變量的地方對其進行聲明。幾乎所有局部變量聲明都應該進行初始化。如果還缺少足夠的信息來正確地初始化變量,那就應該推遲聲明,直至可以初始化爲止。

本規則存在一個例外,就是涉及 try-catch 語句的情況。如果變量是用方法的返回值來初始化的,而該方法可能會拋出一個 checked 異常,那麼必須在 try 塊中進行變量聲明。如果需在 try 塊之外使用該變量,那它就必須在 try 塊之前就進行聲明瞭,這時它是不可能進行正確的初始化的。

<span class="co1">//	實例化類cl,表示有序集合</span>
<a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+set" style="text-decoration: none; color: rgb(12, 137, 207);">Set</a> s <span class="sy0">=</span> <span class="kw2">null</span><span class="sy0">;</span>
<span class="kw1">try</span> <span class="br0">{</span>
    s <span class="sy0">=</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+set" style="text-decoration: none; color: rgb(12, 137, 207);">Set</a><span class="br0">)</span> cl.<span class="me1">newInstance</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">catch</span><span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalaccessexception" style="text-decoration: none; color: rgb(12, 137, 207);">IllegalAccessException</a> e<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">throw</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalargumentexception" style="text-decoration: none; color: rgb(12, 137, 207);">IllegalArgumentException</a><span class="br0">(</span>cl <span class="sy0">+</span> <span class="st0">" not accessible"</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">catch</span><span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+instantiationexception" style="text-decoration: none; color: rgb(12, 137, 207);">InstantiationException</a> e<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">throw</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalargumentexception" style="text-decoration: none; color: rgb(12, 137, 207);">IllegalArgumentException</a><span class="br0">(</span>cl <span class="sy0">+</span> <span class="st0">" not instantiable"</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
 
<span class="co1">//	集合練習</span>
s.<span class="me1">addAll</span><span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+arrays" style="text-decoration: none; color: rgb(12, 137, 207);">Arrays</a>.<span class="me1">asList</span><span class="br0">(</span>args<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>

但即便是這種情況也是可以避免的,把try-catch 塊封裝在一個方法內即可。

<a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+set" style="text-decoration: none; color: rgb(12, 137, 207);">Set</a> createSet<span class="br0">(</span><span class="kw1">Class</span> cl<span class="br0">)</span> <span class="br0">{</span>
<span class="co1">//	實例化類cl,表示有序集合</span>
<span class="kw1">try</span> <span class="br0">{</span>
    <span class="kw1">return</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+set" style="text-decoration: none; color: rgb(12, 137, 207);">Set</a><span class="br0">)</span> cl.<span class="me1">newInstance</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span> <span class="kw1">catch</span><span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalaccessexception" style="text-decoration: none; color: rgb(12, 137, 207);">IllegalAccessException</a> e<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">throw</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalargumentexception" style="text-decoration: none; color: rgb(12, 137, 207);">IllegalArgumentException</a><span class="br0">(</span>cl <span class="sy0">+</span> <span class="st0">" not accessible"</span><span class="br0">)</span><span class="sy0">;</span> 
<span class="br0">}</span> <span class="kw1">catch</span><span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+instantiationexception" style="text-decoration: none; color: rgb(12, 137, 207);">InstantiationException</a> e<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">throw</span> <span class="kw1">new</span> <a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalargumentexception" style="text-decoration: none; color: rgb(12, 137, 207);">IllegalArgumentException</a><span class="br0">(</span>cl <span class="sy0">+</span> <span class="st0">" not instantiable"</span><span class="br0">)</span><span class="sy0">;</span> 
    <span class="br0">}</span>
<span class="br0">}</span>
...
 
<span class="co1">//	集合練習</span>
<a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+set" style="text-decoration: none; color: rgb(12, 137, 207);">Set</a> s <span class="sy0">=</span> createSet<span class="br0">(</span>cl<span class="br0">)</span><span class="sy0">;</span>
s.<span class="me1">addAll</span><span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+arrays" style="text-decoration: none; color: rgb(12, 137, 207);">Arrays</a>.<span class="me1">asList</span><span class="br0">(</span>args<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>

除非理由十分充分,否則循環變量都應該在for語句內進行聲明。

<span class="kw1">for</span> <span class="br0">(</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i n<span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">)</span> <span class="br0">{</span>
    doSomething<span class="br0">(</span>i<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">for</span> <span class="br0">(</span><a target=_blank target="_blank" href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+iterator" style="text-decoration: none; color: rgb(12, 137, 207);">Iterator</a> i <span class="sy0">=</span> c.<span class="me1">iterator</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span> i.<span class="me1">hasNext</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span> <span class="br0">)</span> <span class="br0">{</span>
    doSomethingElse<span class="br0">(</span>i.<span class="me1">next</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>

9.12 使用標準的 Java Annotation

Annotation 應該位於 Java 語言元素的其它修飾符之前。 簡單的 marker annotation(@Override 等)可以和語言元素放在同一行。 如果存在多個 annotation,或者annotation 是參數化的,則應按字母順序各佔一行來列出。

對於 Java 內建的三種 annotation,Android 標準的實現如下:

  • @Deprecated:只要某個語言元素已不再建議使用了,就必須使用@Deprecated annotation。如果使用了@Deprecated annotation,則必須同時進行@deprecated Javadoc 標記,並且給出一個替代的實現方式。此外請記住,被@Deprecated 的方法仍然是能正常執行的。如果看到以前的代碼帶有@deprecated Javadoc 標記,也請加上@Deprecated annotation。
  • @Override:只要某個方法覆蓋了已過時的或繼承自超類的方法,就必須使用@Override annotation。例如,如果方法使用了@inheritdocs Javadoc 標記,且繼承自超類(而不是 interface),則必須同時用@Override 標明覆蓋了父類方法。
  • @SuppressWarnings:@SuppressWarnings annotation 僅用於無法消除編譯警告的場合。如果警告確實經過測試“不可能消除”,則必須使用@SuppressWarnings annotation,以確保所有的警告都能真實反映代碼中的問題。

當需要使用@SuppressWarnings annotation時,必須在前面加上TODO註釋行,用於解釋“不可能消除”警告的條件。通常是標明某個令人討厭的類用到了某個拙劣的接口。

<span class="co1">// TODO:	第三方類 com.third.useful.Utility.rotate()	必須採用泛型</span>
@SuppressWarnings<span class="br0">(</span><span class="st0">"generic-cast"</span><span class="br0">)</span>
List<span class="sy0"><</span>String<span class="sy0">></span> blix <span class="sy0">=</span> Utility.<span class="me1">rotate</span><span class="br0">(</span>blax<span class="br0">)</span><span class="sy0">;</span>

如果需要使用@SuppressWarnings annotation,應該重新組織一下代碼,把需要應用 annotation 的語言元素獨立出來。

9.13 簡稱等同於單詞

簡稱和縮寫都視爲變量名、方法名和類名。以下名稱可讀性更強:

XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
class Html class HTML
String url String URL
long id long ID

如何對待簡稱,JDK 和 Android 底層代碼存在很大的差異。因此,你幾乎不大可能與其它代碼取得一致。別無選擇,把簡稱當作完整的單詞看待吧。

關於本條規則的進一步解釋,請參閱 Effective Java 第 38 條和 Java Puzzlers 第 68條。

9.14 使用 TODO 註釋

對那些臨時性的、短期的、夠棒但不完美的代碼,請使用 TODO 註釋

TODO 註釋應該包含全部大寫的 TODO,後跟一個冒號:

<span class="co1">// TODO: Remove this code after the UrlTable2 has been checked in. </span>
<span class="co1">// TODO: Change this to use a flag instead of a constant.</span>

如果 TODO 註釋是“將來要做某事”的格式,則請確保包含一個很明確的日期(“在2013 年 11 月會修正”),或是一個很明確的事件(“在所有代碼整合人員理解了 V7 協議之後刪除本段代碼”)。

9.15 慎用 Log

記錄日誌會對性能產生顯著的負面影響。如果日誌內容不夠簡煉的話,很快會喪失可用性。日誌功能支持五種不同的級別。以下列出了各個級別及其使用場合和方式。

  • ERROR: 該級別日誌應該在致命錯誤發生時使用,也就是說,錯誤的後果能被用戶看到,但是不明確刪除部分數據、卸裝程序、清除數據區或重新刷機(或更糟糕)就無法恢復。該級別總是記錄日誌。需要記錄 ERROR 級別日誌的事件一般都應該向統計信息收集(statistics-gathering )服務器報告。
  • WARNING:該級別日誌應該用於那些重大的、意外的事件,也就是說,錯誤的後果能被用戶看到,但是不採取明確的動作可能就無法無損恢復,從等待或重啓應用開始,直至重新下載新版程序或重啓設備。該級別總是記錄日誌。需記錄WARNING 級別日誌的事件也可以考慮向統計信息收集服務器報告。
  • INFORMATIVE:該級別的日誌應該用於記錄大部分人都會感興趣的事件,也就是說,如果檢測到事件的影響面可能很廣,但不一定是錯誤。應該只有那些擁有本區域內最高級別身份認證的模塊才能記錄這些日誌(爲了避免級別不足的模塊重複記錄日誌)。該級別總是記錄日誌。
  • DEBUG:該級別的日誌應該用於進一步記錄有關調查、調試意外現象的設備事件。應該只記錄那些有關控件運行所必需的信息。如果 debug 日誌佔用了太多的日誌空間,那就應該使用詳細級別日誌(verbose)才更爲合適。即使是發行版本(release build),該級別也會被記錄,並且需用 if (LOCAL_LOG) 或 if (LOCAL_LOGD) 語句塊包裹,這裏的LOCAL_LOG[D] 在你的類或子控件中定義。這樣就能夠一次性關閉所有的調試日誌。因此在 if (LOCAL_LOG) 語句塊中不允許存在邏輯判斷語句。所有日誌所需的文字組織工作也應在 if (LOCAL_LOG) 語句塊內完成。如果對記錄日誌的調用會導致在 if (LOCAL_LOG) 語句塊之外完成文字組織工作,那該調用就必須控制在一個方法內完成。還存在一些代碼仍然在使用 if (localLOGV)。這也是可以接受的,雖然名稱不是標準的。
  • VERBOSE:該級別日誌應用於所有其餘的事件。該級別僅會在調試版本(debug build)下記錄日誌,並且需用 if (LOCAL_LOGV) 語句塊(或等效語句)包裹,這樣該部分代碼默認就不會編譯進發行版本中去了。所有構建日誌文字的代碼將會在發行版本中剝離出去,並且需包含在 if (LOCAL_LOGV) 語句塊中。

注意:

  • 除了 VERBOSE 級別外,在同一個模塊中同一個錯誤應該儘可能只報告一次:在同一個模塊內的一系列層層嵌套的函數調用中,只有最內層的函數才返回錯誤;並且只有能爲解決問題提供明顯幫助的時候,同一模塊中的調用方纔寫入一些日誌。
  • 除了 VERBOSE 級別外,在一系列嵌套的模塊中,當較低級別的模塊對來自較高級別模塊的非法數據進行檢測時,應該只把檢測情況記錄在 DEBUG 日誌中,並且只記錄那些調用者無法獲取的信息。特別是不需要記錄已經拋出異常的情況(異常中應該包含了全部有價值的信息),也不必記錄那些只包含錯誤代碼的信息。當應用程序與系統框架間進行交互時,這一點尤爲重要。系統框架已能正確處理的第三方應用程序,也不應該記錄大於 DEBUG 級別的日誌。僅當一個模塊或應用程序檢測到自身或來自更低級別模塊的錯誤時,才應該記錄 INFORMATIVE 及以上級別的日誌。
  • 如果一個通常要記錄日誌的事件可能會多次發生,則採取一些頻次限制措施或許是個好主意,以防日誌被很多重複(或類似)的信息給撐爆了。
  • 網絡連接的丟失可被視爲常見現象,也是完全可以預見的,不應該無緣無故就記錄進日誌。影響範圍限於應用程序內部的網絡中斷應該記錄在 DEBUG 或 VERBOSE級別的日誌中(根據影響的嚴重程度及意外程度,再來確定是否在發行版本中也記錄日誌)。
  • 有權訪問的文件系統或第三方應用程序發起的系統空間滿,應該記錄大於INFORMATIVE 級別的日誌。
  • 來自任何未授信源的非法數據(包括共享存儲上的任何文件,或來自任何網絡連接的數據)可被視爲可預見的,如果檢測到非法數據也不應該記錄大於 DEBUG 級別的日誌(即使記錄也應儘可能少)。
  • 請記住,對字符串使用+操作符時,會在後臺以默認大小(16 個字符)緩衝區創建一個 StringBuilder 對象,並且可能還會創建一些其它的臨時 String 對象。換句話說,顯式創建 StringBuilders 對象的代價並不會比用'+'操作符更高(事實上效率還將會提高很多)。還要記住,即使不會再去讀取這些日誌,調用 Log.v() 的代碼也將編譯進發行版中並獲得執行,包括創建字符串的代碼。
  • 所有要被人閱讀並存在於發行版本中的日誌,都應該簡潔明瞭、沒有祕密、容易理解。這裏包括所有 DEBUG 以上級別的日誌。
  • 只要有可能,日誌就應該一句一行。行長最好不超過 80 或 100 個字符,儘可能避免超過 130 或 160 個字符(包括標識符)的行。
  • 報告成功的日誌記錄絕不應該出現在大於 VERBOSE 級別的日誌中。
  • 用於診斷難以重現事件的臨時日誌應該限於 DEBUG 或 VERBOSE 級別,並且應該用 if 語句塊包裹,以便在編譯時能夠一次全部關閉。
  • 小心日誌會泄漏隱私。應該避免將私人信息記入日誌,受保護的內容肯定也不允許記錄。這在編寫系統框架級代碼時尤爲重要,因爲很難預知哪些是私人信息和受保護信息。
  • 絕對不要使用 System.out.println()(或本地代碼中的 printf())。System.out 和System.err 會重定向到/dev/null,因此 print 語句不會產生任何可見的效果。可是,這些調用中的所有字符串創建工作都仍然會執行。

日誌的黃金法則是:你的日誌記錄不會導致其它日誌的緩衝區溢出,正如其他人的日誌也不會讓你的溢出一樣。

10 在Eclipse中使用模板

10.1 導入android編碼規範模板

在eclipse的preferences中,選擇java → code style → formatter中選擇Import,選擇工程根目錄下的development/ide/eclipse/目錄下的android-formatting.xml。

在eclipse的preferences中,選擇java → code style → Organize Imports中選擇Import,選擇工程根目錄下的development/ide/eclipse/目錄下的android.importorder。

說明:導入這兩個文件,是爲了與源碼中的Android程序保持一致的編碼規範。android-formatting.xml用來配置eclipse編譯器的代碼風格;android.importorder用來配置eclipse的import的順序和結構。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章