Android基礎(chǔ)進(jìn)階之EffectiveJava翻譯系列(第七章:通用原則)

本章主要討論語言的具體內(nèi)容蠕搜。它討論了局部變量的處理、控制結(jié)構(gòu)轨蛤、庫的使用虫埂、各種數(shù)據(jù)類型的使用,以及使用反射和本地方法缝呕。最后斧散,討論了優(yōu)化和命名約定

Item 45:最小化局部變量作用域

作用域:一個花括號{}包裹起來的區(qū)域

此條例同Item13相似:最小化類和成員變量的訪問權(quán)限
Java允許你在任何地方聲明變量,但是最重要的是在首次使用的地方聲明變量,并初始化
循環(huán)提供了一種實(shí)現(xiàn)此種方式的機(jī)制,而且for循環(huán)比while循環(huán)好,如

for (Element e : c) {
    doSomething(e);
}

//before JDK1.5
for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomething((Element) i.next());
}

為什么for比while好呢?

//bad
Iterator<Element> i = c.iterator();
while (i.hasNext()) {
    doSomething(i.next());
}
...
Iterator<Element> i2 = c2.iterator();
while (i.hasNext()) { // BUG! 應(yīng)該是i2
    doSomethingElse(i2.next());
}

當(dāng)我們寫一個差不多的代碼,從一個地方copy過來的時(shí)候,很有可能忘記修改某個變量值(如i2),它不會在編譯期報(bào)錯,我們很可能長時(shí)間遺留這個bug
使用for循環(huán)可以避免這個bug

for (Iterator<Element> i = c.iterator(); i.hasNext(); ) {
    doSomething(i.next());
}
 ...
// Compile-time error - cannot find symbol i
for (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) {
    doSomething(i2.next());
}

這就是最小化作用域的好處

Item 46: Prefer for-each loops to traditional for loops

對于不需要下標(biāo)來做特殊操作的遍歷,推薦使用增強(qiáng)for循環(huán)
你能發(fā)現(xiàn)下面的bug嗎?

enum Suit { CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
 NINE, TEN, JACK, QUEEN, KING }
...
Collection<Suit> suits = Arrays.asList(Suit.values());
Collection<Rank> ranks = Arrays.asList(Rank.values());
List<Card> deck = new ArrayList<Card>();
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )
    for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
        deck.add(new Card(i.next(), j.next()));

...
...
...
...
...
...
發(fā)現(xiàn)不了也不要難過,很多有經(jīng)驗(yàn)的程序員也會犯這個錯誤
原因在于i.next()會被重復(fù)調(diào)用,導(dǎo)致結(jié)果異常
可以修復(fù)如下


for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ){
    Suit suit = i.next();
    for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
        deck.add(new Card(suit, j.next()));
}

雖然解決了問題,但是很丑,更簡潔的寫法如下

for (Suit suit : suits)
    for (Rank rank : ranks)
        deck.add(new Card(suit, rank));

盡可能的使用增強(qiáng)for循環(huán)

Item 47: Know and use the libraries

不要重復(fù)造輪子
如果需要一個常用的功能,去發(fā)現(xiàn)一個庫并使用它,往往比自己寫的要好
因?yàn)閹鞎S著時(shí)間推移更新迭代越來越好,自己也節(jié)約了時(shí)間
這并不會評論一個人的能力

Item 48: Avoid float and double if exact answers are required

當(dāng)需要一個精確的答案時(shí),避免使用float或double類型的變量

float或double是為了科學(xué)和工程計(jì)算而設(shè)計(jì)的,特別不適用于貨幣計(jì)算

推薦使用int或long代替

Item 49: Prefer primitive types to boxed primitives

使用原始類型替代裝箱類型

byte short char int long float double boolbean等是Java中的基本類型,也有對應(yīng)的裝箱類型,如 Integer, Double, and Boolean.應(yīng)當(dāng)謹(jǐn)慎對待這兩者之間的差別

首先第一個差別,原始類型僅僅包含對應(yīng)的值,裝箱類型既包含對應(yīng)的值也有對應(yīng)的引用,第二個差別是裝箱類型有可能為null,第三個差別是原始類型在時(shí)間和空間消耗中更高效

Item 50: Avoid strings where other types are more appropriate

如果有更合適的類型,避免使用String

string被設(shè)計(jì)成描述文本類型的數(shù)據(jù),而且干得很好,本章主要討論將string用于其它情況的錯誤用法

string不能替代值類型 如果我們正在等待鍵盤的輸入,或者從網(wǎng)絡(luò)獲取某個值,我們很方便的使用string作為接收類型,但是如果我們輸入的是數(shù)字或者真假值的話,對應(yīng)的int或boolean能更好的標(biāo)識輸入 雖然這條規(guī)則很明顯,但是經(jīng)常被違反

string不能替代枚舉Item30:枚舉討論的那樣,對于靜態(tài)常量使用枚舉

string不能替代聚合字符

String compoundKey = className + "#" + i.next();//bad

我們經(jīng)常寫上述代碼,這樣的寫法有很多缺點(diǎn).如果我們想使用某一部分字段需要解析字符串,很耗時(shí)而且容易出錯.String提供的equals,compareTo等方法也不能使用

比較好的方式是寫一個靜態(tài)內(nèi)部類來表示聚合字符

static class CompoundKey{
    private String className;
    private String next;
    ...
}
Item 51: Beware the performance of string concatenation

考慮字符連接(+)的性能

使用(+)能很方便的拼接若干個字符串,但是我們也要考慮到開銷

如下兩個代碼都是拼接字符

//方式一
public String statement() {
    String result = "";
    for (int i = 0; i < numItems(); i++)
        result += lineForItem(i); // String concatenation
    return result;
}

//方式二
public String statement() {
    StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH);
    for (int i = 0; i < numItems(); i++)
        b.append(lineForItem(i));
    return b.toString();
}

當(dāng)numItems()==100;lineForItem(i)返回80個長度的字符時(shí),在作者的機(jī)器上方式二比方式一快85倍

如果我們需要拼接大量字符時(shí),使用StringBuilder代替

Item 52: Refer to objects by their interfaces

使用接口代替對象引用

如果有一個合適的接口來描述當(dāng)前類的時(shí)候使用這個接口來引用,如

// Good - uses interface as type
List<Subscriber> subscribers = new Vector<Subscriber>();

// Bad - uses class as type!
Vector<Subscriber> subscribers = new Vector<Subscriber>();

如果你養(yǎng)成了這個習(xí)慣,那么你的代碼將會更靈活
有幾種情況不能使用接口
1.沒有合適的接口聲明
2.接口中沒有想要的某個方法
3.使用的類集成自framework,并且是一個抽象類

Item 53: Prefer interfaces to reflection

使用接口代替反射

反射核心類java.lang.reflect提供了對加載類信息的訪問

例如源祈,Method.Invoke允許在任何類的任何對象上調(diào)用任何方法(受通常的安全約束)色迂。 即使編譯后的類不存在,也允許一個類使用另一個類赫悄。然而,這種力量是有代價(jià)的姑隅。

  • 你不能在編譯時(shí)檢查出異常 如果你使用了對應(yīng)的反射操作,在發(fā)生異常時(shí),只有在運(yùn)行時(shí)才能發(fā)現(xiàn)

  • 閱讀性極差

  • 性能有影響 使用反射比普通調(diào)用要慢些,因?yàn)槭芎芏嘁蛩氐挠绊?慢多少很難說,在作者的機(jī)器上,速度差在兩倍到五十倍不止

反射核心用在基于組件設(shè)計(jì)的應(yīng)用,為了按需加載類,使用反射找到對應(yīng)的類構(gòu)造與否;普通應(yīng)用盡量不要使用反射,找到代替的接口或者父類對象

Item 54: Use native methods judiciously

明智地使用本地方法

JNI允許Java調(diào)用C或C++寫的本地方法

從歷史上看讲仰,本地方法有三種主要用途痪蝇。

1.它們提供了對特定于平臺的設(shè)施的訪問,例如注冊表和文件鎖趁矾。

2.他們提供了對舊代碼庫(Java想使用歷史上C或C++寫的庫)

的訪問给僵,可以反過來提供對舊數(shù)據(jù)的訪問。

3.使用本地方法用本地語言編寫應(yīng)用程序關(guān)鍵部分蔓同,以提高性能蹲诀。

Java平臺在不斷發(fā)展,訪問特定平臺設(shè)施,使用Java提供的工具類就可做到,而且也不建議使用本地方法來提升程序性能

使用本地方法有嚴(yán)重的缺點(diǎn)

1.本地語言是不安全的,會受機(jī)器內(nèi)存錯誤的影響

2.依賴于平臺,不便于移植

3.本地代碼很難調(diào)式

4.訪問本地代碼開銷很大

5.本地代碼不宜閱讀

總之,要再三思考是否使用本地代碼,如果需要使用以前的代碼庫,請盡可能減少本地代碼片段并加強(qiáng)測試,很小很小的本地代碼錯誤將破壞你的整個程序

Item 55: Optimize judiciously

明智的優(yōu)化

有三個人人都應(yīng)該知道的優(yōu)化格言

More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason—including blind stupidity.
—William A. Wulf [Wulf72]
更多的計(jì)算罪惡是以效率的名義犯下的(不一定要達(dá)到這一目的)脯爪,而不是因?yàn)槿魏纹渌麊我坏脑?包括盲目的愚蠢

?

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
—Donald E. Knuth [Knuth74]
我們應(yīng)該忽略小效率,大約百分之97的情況下,邪惡之源就是過早優(yōu)化

?

We follow two rules in the matter of optimization:
Rule 1. Don’t do it.
Rule 2 (for experts only). Don’t do it yet—that is, not until you have a
perfectly clear and unoptimized solution.
—M. A. Jackson [Jackson75]
關(guān)于優(yōu)化遵循如下兩個原則:
原則1.別這樣做
原則2.(僅對專家而言).還不要做,直到你找到一個清晰的解決方案或者一個未優(yōu)化的解決辦法

不要為了性能而損壞架構(gòu),致力于寫出好的程序而不是快的程序,如果一個好的程序還不夠快,它的架構(gòu)會允許優(yōu)化.

這并不意味著當(dāng)你的程序完后不需要優(yōu)化,你應(yīng)該在設(shè)計(jì)階段就考慮到性能

盡量避免影響性能的設(shè)計(jì),已經(jīng)實(shí)現(xiàn)好的組件很難在改變,尤其是API,數(shù)據(jù)結(jié)構(gòu),多方約定好的協(xié)議

考慮好API使用效果,設(shè)計(jì)一個可變的類可能會導(dǎo)致后續(xù)使用中出現(xiàn)過多的深拷貝,造成對象分配的額外開銷

API的設(shè)計(jì)對性能有很真實(shí)的影響,如java.awt.Component中的getSize()方法,每調(diào)用一次就會返回一個新的 Dimension 實(shí)例(JDK1.2版本已經(jīng)修復(fù)),雖然分配一個實(shí)例的開銷很小,但是成百上千次調(diào)用也會對程序有嚴(yán)重影響

幸運(yùn)的是,好的API設(shè)計(jì)自然帶來了好的性能

總之,致力于寫出好的程序,快隨之而來 當(dāng)做出一部分改變后就要測量代碼的性能,對于Android來說內(nèi)存,卡頓,anr等方面

參考DDMS性能調(diào)優(yōu)


?

Item 56: Adhere to generally accepted naming conventions

遵循公共的命名規(guī)范,參考阿里巴巴發(fā)布的Android手冊


上一章:方法
下一章:異常

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尚揣,一起剝皮案震驚了整個濱河市守屉,隨后出現(xiàn)的幾起案子拇泛,更是在濱河造成了極大的恐慌,老刑警劉巖俺叭,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熄守,死亡現(xiàn)場離奇詭異耗跛,居然都是意外死亡调塌,警方通過查閱死者的電腦和手機(jī)惠猿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姜凄,“玉大人趾访,你說我怎么就攤上這事∩暧悖” “怎么了藏鹊?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵盘寡,是天一觀的道長撮慨。 經(jīng)常有香客問我,道長影涉,這世上最難降的妖魔是什么规伐? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮猖闪,結(jié)果婚禮上鲜棠,老公的妹妹穿的比我還像新娘。我一直安慰自己培慌,他們只是感情好豁陆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吵护,像睡著了一般盒音。 火紅的嫁衣襯著肌膚如雪表鳍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天祥诽,我揣著相機(jī)與錄音譬圣,去河邊找鬼雄坪。 笑死胁镐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诸衔。 我是一名探鬼主播盯漂,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笨农!你這毒婦竟也來了就缆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤谒亦,失蹤者是張志新(化名)和其女友劉穎竭宰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體份招,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡切揭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锁摔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片廓旬。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谐腰,靈堂內(nèi)的尸體忽然破棺而出孕豹,到底是詐尸還是另有隱情,我是刑警寧澤十气,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布励背,位于F島的核電站,受9級特大地震影響砸西,放射性物質(zhì)發(fā)生泄漏叶眉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一芹枷、第九天 我趴在偏房一處隱蔽的房頂上張望衅疙。 院中可真熱鬧,春花似錦杖狼、人聲如沸炼蛤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽理朋。三九已至絮识,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗽上,已是汗流浹背次舌。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兽愤,地道東北人彼念。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像浅萧,于是被迫代替她去往敵國和親逐沙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容