java最小访问原则_Android基础进阶之EffectiveJava翻译系列(第七章:通用原则)
本章主要討論語言的具體內(nèi)容。它討論了局部變量的處理、控制結(jié)構(gòu)、庫的使用、各種數(shù)據(jù)類型的使用,以及使用反射和本地方法。最后,討論了優(yōu)化和命名約定
Item 45:最小化局部變量作用域
作用域:一個花括號{}包裹起來的區(qū)域
此條例同Item13相似:最小化類和成員變量的訪問權(quán)限
Java允許你在任何地方聲明變量,但是最重要的是在首次使用的地方聲明變量,并初始化
循環(huán)提供了一種實現(xiàn)此種方式的機制,而且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
Iteratori = c.iterator();
while (i.hasNext()) {
doSomething(i.next());
}
...
Iteratori2 = c2.iterator();
while (i.hasNext()) { // BUG! 應(yīng)該是i2
doSomethingElse(i2.next());
}
當(dāng)我們寫一個差不多的代碼,從一個地方copy過來的時候,很有可能忘記修改某個變量值(如i2),它不會在編譯期報錯,我們很可能長時間遺留這個bug
使用for循環(huán)可以避免這個bug
for (Iteratori = c.iterator(); i.hasNext(); ) {
doSomething(i.next());
}
...
// Compile-time error - cannot find symbol i
for (Iteratori2 = c2.iterator(); i.hasNext(); ) {
doSomething(i2.next());
}
這就是最小化作用域的好處
Item 46: Prefer for-each loops to traditional for loops
對于不需要下標(biāo)來做特殊操作的遍歷,推薦使用增強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 }
...
Collectionsuits = Arrays.asList(Suit.values());
Collectionranks = Arrays.asList(Rank.values());
Listdeck = new ArrayList();
for (Iteratori = suits.iterator(); i.hasNext(); )
for (Iteratorj = ranks.iterator(); j.hasNext(); )
deck.add(new Card(i.next(), j.next()));
...
...
...
...
...
...
發(fā)現(xiàn)不了也不要難過,很多有經(jīng)驗的程序員也會犯這個錯誤
原因在于i.next()會被重復(fù)調(diào)用,導(dǎo)致結(jié)果異常
可以修復(fù)如下
for (Iteratori = suits.iterator(); i.hasNext(); ){
Suit suit = i.next();
for (Iteratorj = 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));
盡可能的使用增強for循環(huán)
Item 47: Know and use the libraries
不要重復(fù)造輪子
如果需要一個常用的功能,去發(fā)現(xiàn)一個庫并使用它,往往比自己寫的要好
因為庫會隨著時間推移更新迭代越來越好,自己也節(jié)約了時間
這并不會評論一個人的能力
Item 48: Avoid float and double if exact answers are required
當(dāng)需要一個精確的答案時,避免使用float或double類型的變量
float或double是為了科學(xué)和工程計算而設(shè)計的,特別不適用于貨幣計算
推薦使用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,第三個差別是原始類型在時間和空間消耗中更高效
Item 50: Avoid strings where other types are more appropriate
如果有更合適的類型,避免使用String
string被設(shè)計成描述文本類型的數(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)常寫上述代碼,這樣的寫法有很多缺點.如果我們想使用某一部分字段需要解析字符串,很耗時而且容易出錯.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個長度的字符時,在作者的機器上方式二比方式一快85倍
如果我們需要拼接大量字符時,使用StringBuilder代替
Item 52: Refer to objects by their interfaces
使用接口代替對象引用
如果有一個合適的接口來描述當(dāng)前類的時候使用這個接口來引用,如
// Good - uses interface as type
Listsubscribers = new Vector();
// Bad - uses class as type!
Vectorsubscribers = new Vector();
如果你養(yǎng)成了這個習(xí)慣,那么你的代碼將會更靈活
有幾種情況不能使用接口
1.沒有合適的接口聲明
2.接口中沒有想要的某個方法
3.使用的類集成自framework,并且是一個抽象類
Item 53: Prefer interfaces to reflection
使用接口代替反射
反射核心類java.lang.reflect提供了對加載類信息的訪問
例如,Method.Invoke允許在任何類的任何對象上調(diào)用任何方法(受通常的安全約束)。 即使編譯后的類不存在,也允許一個類使用另一個類。然而,這種力量是有代價的。
你不能在編譯時檢查出異常 如果你使用了對應(yīng)的反射操作,在發(fā)生異常時,只有在運行時才能發(fā)現(xiàn)
閱讀性極差
性能有影響 使用反射比普通調(diào)用要慢些,因為受很多因素的影響,慢多少很難說,在作者的機器上,速度差在兩倍到五十倍不止
反射核心用在基于組件設(shè)計的應(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)重的缺點
1.本地語言是不安全的,會受機器內(nèi)存錯誤的影響
2.依賴于平臺,不便于移植
3.本地代碼很難調(diào)式
4.訪問本地代碼開銷很大
5.本地代碼不宜閱讀
總之,要再三思考是否使用本地代碼,如果需要使用以前的代碼庫,請盡可能減少本地代碼片段并加強測試,很小很小的本地代碼錯誤將破壞你的整個程序
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]
更多的計算罪惡是以效率的名義犯下的(不一定要達到這一目的),而不是因為任何其他單一的原因-包括盲目的愚蠢
?
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è)計階段就考慮到性能
盡量避免影響性能的設(shè)計,已經(jīng)實現(xiàn)好的組件很難在改變,尤其是API,數(shù)據(jù)結(jié)構(gòu),多方約定好的協(xié)議
考慮好API使用效果,設(shè)計一個可變的類可能會導(dǎo)致后續(xù)使用中出現(xiàn)過多的深拷貝,造成對象分配的額外開銷
API的設(shè)計對性能有很真實的影響,如java.awt.Component中的getSize()方法,每調(diào)用一次就會返回一個新的 Dimension 實例(JDK1.2版本已經(jīng)修復(fù)),雖然分配一個實例的開銷很小,但是成百上千次調(diào)用也會對程序有嚴(yán)重影響
幸運的是,好的API設(shè)計自然帶來了好的性能
總之,致力于寫出好的程序,快隨之而來 當(dāng)做出一部分改變后就要測量代碼的性能,對于Android來說內(nèi)存,卡頓,anr等方面
參考DDMS性能調(diào)優(yōu)
?
Item 56: Adhere to generally accepted naming conventions
遵循公共的命名規(guī)范,參考阿里巴巴發(fā)布的Android手冊
上一章:方法
下一章:異常
總結(jié)
以上是生活随笔為你收集整理的java最小访问原则_Android基础进阶之EffectiveJava翻译系列(第七章:通用原则)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java头像交互式差分演变_一种基于交互
- 下一篇: java map移除key为空_Java