Android開發規範
1 介紹
1.1 目的
- 減少維護花費。
- 提高可讀性。
- 加快工作交接。
- 減少名字增生。
- 降低缺陷引入的機會。
1.2 術語和定義
強制:程式設計時必須遵守的規定,含有強制字樣或字型用加粗式樣標註。
推薦:程式設計時推薦遵守的規定,字型用普通式樣標註。
2 檔案組織
避免超過 2000 行的原始檔。
2.1 Java 包和原始檔
每個 Java 原始檔都包含一個單一的公共類或介面。若私有類和介面與一個公共類相關聯,可以將它們和公共類放入同一個原始檔。公共類必須是這個檔案中的第一個類或介面。
Java 原始檔還遵循以下規則:
- 開頭註釋
- 包和引入語句
- 類和介面宣告
2.1.1 開頭註釋
/*
* Copyright (c) 2014 SIMCOM, Inc.
* All Rights Reserved.
* SIMCOM Proprietary and Confidential.
*/
2.1.2 包和引入語句
在多數 Java 原始檔中,第一個非註釋行是包語句,在它之後可以跟引入語句。
package com.android.sim;
import java.io.IOException;
在匯入包時當完全限制程式碼所使用的類的名字,儘量少用萬用字元的方式,但匯入一些通用包,或用到一個包下大部分類時,則可是使用萬用字元方式。同一包中的類在匯入時應宣告在一起,無效的未使用到的引用要即時刪除。
2.1.3 類和介面宣告
下表(強制)描述了類和介面宣告的各個部分以及它們出現的先後次序。
類/介面宣告的各部分 | 說明 |
---|---|
類/介面文件註釋(/**……*/) | 類和介面應該有標準 Javadoc 註釋。 |
類或介面的宣告 | 類名和介面的第一個字元大寫,每個單詞的首字母大寫,中間可以有下劃線。 |
類/介面實現的長度限制 | 限制一個匿名內部類的長度不要超過 80 行。 |
類的(靜態)變數 | 首先是類的公共變數,隨後是保護變數,再後是包一級別的變數(沒有訪問修飾符,access modifier),最後是私有變數。靜態 final 變數應全部大寫,中間可以有下劃線。 |
例項變數 | |
構造器 | 構造器的程式碼長度(不計空行),不應超過 200 行。 |
方法 | 方法名應為動詞或動賓短語,首字母小寫,其後每個單詞首字母大寫,方法的引數不要超過 8個,引數名字必須和變數的命名規範一致,public 的方法之前應該有 Javadoc 註釋,方法之後的大括號位於行尾。方法應該保持簡短和重點突出,對方法的程式碼長度並沒有硬性的限制。如果方法程式碼超過了 40 行,就該考慮是否可以在不損害程式結構的前提下進行分拆。 |
3 縮排排版
4 個空格作為縮排排版的一個單位,不使用製表符 tab。
8 個空格作為換行後的縮排,包括函式呼叫和賦值。
Instrument i =
someLongexpression_r(that, NotFit, on, one, line); // 推薦
Instrument i =
someLongexpression_r(that, NotFit, on, one, line); // 避免
3.1 行長度
儘量避免一行的長度超過 100 個字元。
例外:如果註釋行包含了超過 100 個字元的命令示例或者 url 文字,為了便於剪下和複製,其長度可以超過 100 個字元。
例外:import 行可以超過限制,因為很少有人會去閱讀它。這也簡化了程式設計工具的寫入操作。
3.2 括號
大括號不單獨佔用一行,應緊接著上一行書寫。
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
我們需要用大括號來包裹條件語句塊。不過也有例外,如果整個條件語句塊(條件和語句本身)都能容納在一行內,也可以(但不是必須)把它們放入同一行中。也就是說,這是合法的:
if (condition) {
body();
} // 推薦
if (condition) body(); // 避免
if (condition)
body(); // 錯誤
3.3 換行
當一個表示式無法容納在一行內時,可以依據如下一般規則斷開:
- 在一個逗號後面斷開。
- 在一個操作符前面斷開。
- 寧可選擇較高級別的斷開,而非較低級別的斷開。
- 新的一行應該與上一行同一級別表示式的開頭處對齊。
- 如果以上規則導致你的程式碼混亂或者使你的程式碼都堆擠在右邊,那就代之以縮排 8個空格。
以下是斷開方法呼叫的一些例子:
someMethod(longExpression1, longExpression2, longExpression3,
longExpression4, longExpression5);
var = someMethod1(longExpression1,
someMethod2(longExpression2,
longExpression3));
以下是兩個斷開算術表示式的例子。前者更好,因為斷開處位於括號表示式的外邊,這是個較高級別的斷開。
long1 = long2 * (long3 + long4 - long5)
+ 4 * longname6; // 推薦
long1 = long2 * (long3 + long4
- long5) + 4 * long6; // 避免
以下是兩個縮排方法宣告的例子。前者是常規情形。後者若使用常規的縮排方式將會使第二行和第三行移得很靠右,所以代之以縮排 8 個空格。
// 常規縮排
someMethod(int anArg, Object anotherArg, String yetAnotherArg,
Object andStillAnother) {
...
}
// 為避免太靠右,用8個空格縮排
private static synchronized horkingLongMethodName(int anArg,
Object anotherArg, String yetAnotherArg,
Object andStillAnother) {
...
}
if 語句的換行通常使用 8 個空格的規則,因為常規縮排(4 個空格)會使語句體看起來比較費勁。比如:
// 請不要使用這種縮排
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) { // 不好的縮排
doSomethingAboutIt(); // 該行和if條件處於同一級
} // 避免
// 使用這種縮排
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
} // 推薦
// 或這種
if ((condition1 && condition2) || (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
} // 推薦
這裡有三種可行的方法用於處理三元運算表示式:
alpha = (aLongBooleanExpression) ? beta : gamma;
alpha = (aLongBooleanExpression) ? beta
: gamma;
alpha = (aLongBooleanExpression)
? beta
: gamma;
4 註釋
Java 程式有兩類註釋:實現註釋(使用/*…*/和//界定的註釋)和文件註釋(由/**…*/界定,可通過 javadoc 工具轉換成 html 檔案)。
實現註釋用以註釋程式碼或者實現細節。文件註釋從實現自由的角度描述程式碼的規範。它可以被那些手頭沒有原始碼的開發人員讀懂。
註釋應被用來給出程式碼的總括,並提供程式碼自身沒有提供的附加資訊。註釋應該僅包含與閱讀和理解程式有關的資訊。例如,相應的包如何被建立或位於哪個目錄下之類的資訊不應包括在註釋中。
在註釋裡,對設計決策中重要的或者不是顯而易見的地方進行說明是可以的,但應避擴音供程式碼中己清晰表達出來的重複資訊。
註釋不應寫在用星號或其他字元畫出來的大框裡。註釋不應包括諸如製表符和回退符之類的特殊字元。
使用 Javadoc 標準註釋,每個檔案的開頭都應該有一句版權說明。然後下面應該是package 包語句和 import 語句,每個語句塊之間用空行分隔。然後是類或介面的定義。在Javadoc 註釋中,應描述類或介面的用途。
/*
* Copyright (c) 2014 SIMCOM, Inc.
* All Rights Reserved.
* SIMCOM Proprietary and Confidential.
*/
package com.android.internal.foo;
import android.os.Blah;
import android.view.Yada;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 一句話功能描述
* 功能詳細描述
* @see 相關類/方法
* @deprecated
*/
public class Foo {
...
}
每個類和自建的 public 方法必須包含 Javadoc 註釋,註釋至少要包含描述該類或方法用途的語句。並且該語句應該用第三人稱的動詞形式來開頭。
/** Returns the correctly rounded positive square root of a double
value. */
static double sqrt(double a) {
...
}
or
/**
* Constructs a new String by converting the specified array of
* bytes using the platform's default character encoding.
*/
public String(byte[] bytes) {
...
}
如果所有的 Javadoc 都會寫成“sets Foo”,對於那些無關緊要的類似 setFoo()的 get和 set 語句是不必撰寫 Javadoc 的。如果方法執行了比較複雜的操作(比如執行強制約束或者產生很重要的副作用),那就必須進行註釋。如果“Foo”屬性的意義不容易理解,也應該進行註釋。
無論是 public 的還是其它型別的,所有自建的方法都將受益於 Javadoc。public 的方法是 API 的組成部分,因此更需要 Javadoc。
4.1 實現註釋的格式
程式可以有 4 種實現註釋的風格:塊、單行、尾端和行末。
4.1.1 塊註釋
塊註釋通常用於提供對檔案,方法,資料結構和演算法的描述。塊註釋被置於每個檔案的開始處以及每個方法之前。它們也可以用於其他地方,比如方法內部。在功能和方法內部的塊註釋應該和它們所描述的程式碼具有一樣的縮排格式。
塊註釋之首應該有一個空行,用於塊註釋和程式碼分割開來,比如:
/*
* Here is a block comment.
*/
塊註釋可以以/*-開頭,這樣indent(1)就可以將之識別為一個程式碼塊的開始,而不是重排它。
/*-
* Here is a block comment with some very special
* formatting that I want indent(1) to ignore.
*
* one
* two
* three
*/
4.1.2 單行註釋
短註釋可以顯示在一行內,並與其後的程式碼具有一樣的縮排層級。如果一個註釋不能在一行內寫完,就該採用塊註釋(參見“塊註釋”)。單行註釋之前應該有一個空行。以下是一個Java程式碼中單行註釋的例子:
if (condition) {
/* Handle the condition. */
…
}
4.1.3 尾端註釋
極短的註釋可以與它們所要描述的程式碼位於同一行,但是應該有足夠的空白來分開程式碼和註釋。若有多個短註釋出現於大量程式碼中,它們應該具有相同的縮排。
以下是一個Java程式碼中尾端註釋的例子:
if (a == 2) {
return true; /* special case */
} else {
return isPrime(a); /* works only for odd a */
}
4.1.4 行末註釋
註釋界定符“//”,可以註釋掉整行或者一行中的一部分。它一般不用於連續多行的註釋文字;然而,它可以用來註釋掉連續多行的程式碼段。以下是所有三種風格的例子:
if (foo > 1) {
// Do a double-flip.
...
} else {
return false; // Explain why here.
}
//if (bar > 1) {
//
// // Do a triple-flip.
// ...
//} else {
// return false;
//}
4.2 文件註釋
文件註釋描述 Java 的類、介面、構造器,方法,以及欄位。每個文件註釋都會被置於註釋定界符/**…*/之中,一個註釋對應一個類、介面或成員。
該註釋應位於宣告之前:
/**
* The Example class providers ...
*/
public class Example {
...
}
注意頂層的類和介面是不縮排的,而其成員是縮排的。描述類和介面的文件註釋的第一行(/**)不需縮排,隨後的文件註釋每行都縮排 1 格(使星號縱向對齊)。成員,包括建構函式在內,其文件註釋的第一行縮排 4 格,隨後每行都縮排 5 格。
若你想給出有關類、介面、變數或方法的資訊,而這些資訊又不適合寫在文件中,則可使用實現塊註釋或緊跟在聲明後面的單行註釋。例如,有關一個類實現的細節,應放入緊跟在類聲明後面的實現塊註釋中,而不是放在文件註釋中。
文件註釋不能放在一個方法或構造器的定義塊中,因為 Java 會將位於文件註釋之後的第一個宣告與其相關聯。
4.2.1 類註釋
在類、介面定義之前當對其進行註釋,包括類、介面的目的、作用、功能、繼承於何種父類,實現的介面、實現的演算法、使用方法、示例程式等。
/**
* 一句話功能描述
* 功能詳細描述
* @see 相關類/方法
* @deprecated
*/
4.2.2 方法註釋
據標準Javadoc規範對方法進行註釋,以明確該方法功能、作用、各引數含義以及返回值等。複雜的演算法用/**/在方法內註解出。
- 引數註釋時當註明其取值範圍等。
- 返回值當註釋出失敗、錯誤、異常時的返回情況。
- 異常當註釋出什麼情況、什麼時候、什麼條件下會引發什麼樣的異常。
/**
* 一句話方法描述
* 方法詳細描述
* @param 引數名 引數描述
* @param 引數名2 引數描述
* @return 返回值型別說明
* @throws Exception 異常說明
* @see 類/方法/成員
*/
4.2.3 類成員變數和常量註釋
成員變數和常量需要使用javadoc形式的註釋,以說明當前變數或常量的含義。
/**
* 成員變數描述
*/
private String test;
/** 成員變數描述 */
private int hello;
5 宣告
5.1 每行宣告變數的數量
推薦一行一個宣告,因為這樣以利於寫註釋。
int level; // indentation level
int size; // size of table
注意:上面的例子中,在型別和識別符號之間放了一個空格,另一種被允許的替代方式是使用製表符。
int level; // indentation level
int size; // size of table
char username; // username
5.2 初始化
儘量在宣告區域性變數的同時初始化。唯一不這麼做的理由是變數的初始值依賴於某些先前發生的計算。
5.3 佈局
只在程式碼塊的開始處宣告變數(一個塊是指任何被包含在大括號”{“和”}“中間的程式碼)。不要在首次用到該變數時才宣告之。這會把注意力不集中的程式設計師搞糊塗,同時會妨礙程式碼在該作用域內的可移植性。
void myMethod() {
int int1 = 0; // beginning of method block
if (condition) {
int int2 = 0; // beginning of “if” block
...
}
}
避免宣告的區域性變數覆蓋上一級宣告的變數。例如,不要在內部程式碼塊中宣告相同的變數名:
int count;
...
myMethod() {
if (condition) {
int count = 0; // 避免
...
}
...
}
5.4 類和介面的宣告
當編寫類和介面是,應該(強制)遵守以下格式規則:
- 在方法名與其引數列表之前的左括號”(“間不要有空格。
- 左大括號”{“位於宣告語句同行的末尾。
- 右大括號”}“另起一行,與相應的宣告語句對齊。除非是一個空語句,”}“應緊跟在”{“之後。
- 方法與方法之間以空行分隔。
class Sample extends Object {
int ivar1;
int ivar2;
Sample(int i, int j) {
ivar1 = i;
ivar2 = j;
}
int emptyMethod() {}
...
}
6 語句
6.1 簡單語句
每行之多包含一條語句,例如:
argv++; // 推薦
argc--; // 推薦
argv++; argc--; // 避免
6.2 複合語句
複合語句是包含在大括號中的語句序列,形如”{ 語句 }“。例如下面各段。
- 被括其中的語句應該較之複合語句縮排一個層次。
- 左大括號”{“應位於複合語句起始行的行尾,右大括號”}“應另起一行並與複合語句首行對齊。
- 大括號可以被用於所有語句,包括單個語句,只要這些語句是諸如 if-else 或 for 控制結構的一部分。這樣便於新增語句而無需擔心由於忘了加括號而引入 bug。
6.3 返回語句
一個帶返回值的return語句不使用小括號”()“,除非它們以某種方式使返回值更為顯見。例如:
return;
return myDisk.size(); // 避免
return (size ? size : defaultSize); // 避免
6.4 if, if-else, if else-if else 語句
if-else語句應該具有如下格式:
if (condition) {
statements;
}
if (condition) {
statements;
} else {
statements;
}
if (condition) {
statements;
} else if (condition) {
statements;
} else{
statements;
}
注意:if語句總是用”{“和”}“括起來,避免使用如下容易引起錯誤的格式:
if (condition) // 避免
statement;
6.5 for 語句
一個for語句應該具有如下格式:
for (initialization; condition; update) {
statements;
}
當在for語句的初始化或更新子句中使用逗號時,避免因使用三個以上變數,而導致複雜度提高。若需要,可以在for迴圈之前(為初始化子句)或for迴圈末尾(為更新子句)使用單獨的語句。
6.6 while 語句
一個while語句應該具有如下格式:
while (condition) {
statements;
}
6.7 do-while 語句
do {
statements;
} while (condition);
6.8 switch 語句
一個switch語句應該具有如下格式:
switch (condition) {
case ABC:
statements;
/* falls through */
case DEF:
statements;
break;
case XYZ:
statements;
break;
default:
statements;
break;
}
每當一個case順著往下執行時(因為沒有break語句),通常應在break語句的位置添加註釋。上面的示例程式碼中就包含註釋/* falls through */。
6.9 try-catch 語句
一個try-catch語句應該具有如下格式:
try {
statements;
} catch (ExceptionClass e) {
statements;
}
try {
statements;
} catch (ExceptionClass e) {
statements;
} finally {
statements;
}
7 空白
7.1 空行
空行將邏輯相關的程式碼段分隔開,以提高可讀性。下列情況應該總是使用空行:
- 一個原始檔的兩個片段(section)之間。
- 類宣告和介面宣告之間。
- 兩個方法之間。
- 方法內的區域性變數和方法的第一條語句之間。
- 塊註釋或單行註釋之前。
- 一個方法內的兩個邏輯段之間,用以提高可讀性。
7.2 空格
下列情況應該使用空格:
- 一個緊跟著括號的關鍵字應該被空格分開。例如:
while (true) {
...
}
- 空白應該位於引數列表中逗號的後面。
- 所有的二元運算子,除了”.”,都應該使用空格將之與運算元分開。一元操作符和運算元之間不應該加空格,比如:負號(”-”)、自增(”++”)和自減(”–”)。例如:
a += c + d;
a = (a + b) / (c * d);
while (d++ = s++) {
n++;
}
- for語句中的表示式應該被空格分開。
for (expr1; expr2; expr3)
- 強制轉型後應該跟一個空格。
myMethod((byte) aNum, (Object) x);
myMethod((int) (cp + 5), ((int) (i + 3)) + 1);
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 開頭。
其它欄位以小寫字母開頭。
public class MyClass {
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
8.5 常量命名
類常量的宣告,應該全部大寫,單詞間用下劃線隔開。
static final int MIN_WIDTH = 4;
static final int MAX_WIDTH = 999;
static final int GET_THE_CPU = 1;
8.6 異常命名
自定義異常的命名必須以Exception為結尾,已明確標示為一個異常。
異常例項一般使用e、ex等,在多個異常時使用該異常名或簡寫加E,Ex等組成,如:SQLEx,ActionEx。
8.7 Layout 命名
命名必須以全部單詞小寫,單詞間以下劃線分割,並且儘可能的使用名詞或名片語,即使用 模組名_功能名稱 來命名。
addressbook_list.xml // 推薦
list_addressbook.xml // 避免
8.8 資源 ID 命名
layout中所使用的id命名必須以全部單詞小寫,單詞間以下劃線分割,並且儘可能的使用名詞或名片語,並且要求能夠通過id直接理解當前元件要實現的功能。
EditText名 @+id/book_name_edit // 推薦
EditText名 @+id/textbookname // 避免
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 設定。
Button btn = new Button(context);
LayoutParams lp = new
LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
lp.setMargins(0, 0, 0, 0);
btn.setTextSize(12);
btn.setPadding(0, 0, 0, 0);
這個時候,一般在設定 margin 和 padding 時,應該對要設定的 dip 值轉換為 px 單位,而字型的大小設定中,系統預設給出了 sp 的單位,所以可以不用進行轉換。轉換的方法參考下面的程式碼:
/**
* 把dip單位轉成px單位
* @param context context物件
* @param dip dip數值
* @return dip對應的px值
*/
public static int formatDipToPx(Context context, int dip) {
DisplayMetrics dm = new DisplayMetrics();
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
int dip = (int) Math.ceil(dip * dm.density);
return dip;
}
9.2 引用類變數和類方法
避免用一個物件訪問一個類的靜態變數和方法。應該用類名替代。
classMethod(); // 推薦
AClass.classMethod(); // 推薦
anObject.classMethod(); // 避免
9.3 常量
位於 for 迴圈中作為計數器值的數字常量,除了-1,0 和 1 之外,不應被直接寫入程式碼。
9.4 變數賦值
避免在一個語句中給多個變數賦相同的值,它很難讀懂。
9.5 信令類
如果類只是用來作為資訊傳遞的中間變數,則應該宣告為信令類,即所有的全域性變數都是 final 型別,在初始化時賦值。
private final String name;
public Foo(String str) {
name = str;
}
public Foo(String str ) {
this.str = str; // 避免在建構函式中出現this引用
}
9.6 不要忽略異常
有時,完全忽略異常是非常誘人的。
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { } // 錯誤
}
絕對不要這麼做。也許你會認為:你的程式碼永遠不會碰到這種出錯的情況,或者處理異常並不重要,可類似上述忽略異常的程式碼將會在程式碼中埋下一顆地雷,說不定哪天它就會炸到某個人了。你必須在程式碼中以某種規矩來處理所有的異常。根據情況的不同,處理的方式也會不一樣。可接受的替代方案包括(按照推薦順序):向方法的呼叫者丟擲異常。
void setServerPort(String value) throws NumberFormatException {
serverPort = Integer.parseInt(value);
}
根據抽象級別丟擲新的異常。
默默地處理錯誤並在 catch {} 語句塊中替換為合適的值。
/** 設定埠。假如值不是數字則用80代替 */
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
serverPort = 80; // 服務預設埠
}
}
捕獲異常並丟擲一個新的 RuntimeException。這種做法比較危險:只有確信發生該錯誤時最合適的做法就是崩潰,才會這麼做。
/** 設定埠,假如值不是數字則程式終止。 */
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new RuntimeException("port " + value " is invalid, ", e);
}
}
請記住,最初的異常是傳遞給構造方法的 RuntimeException。如果程式碼必須在 Java 1.3 版本下編譯,需要忽略該異常。最後一招:如果確信忽略異常比較合適,那就忽略吧,但必須把理想的原因註釋出來。
/** 假如值不是數字則使用原來的埠號。 */
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
// 方法記錄:無視無效的使用者輸入。
// 服務埠不會被改變。
}
}
9.7 不要捕獲頂級的 Exception
有時在捕獲 Exception 時偷懶也是很吸引人的,類似如下的處理方式:
try {
someComplicatedIOFunction(); // 可能丟擲IOException
someComplicatedParsingFunction