java最小访问原则_Android基础进阶之EffectiveJava翻译系列(第七章:通用原则)
本章主要討論語言的具體內容。它討論了局部變量的處理、控制結構、庫的使用、各種數據類型的使用,以及使用反射和本地方法。最后,討論了優(yōu)化和命名約定
Item 45:最小化局部變量作用域
作用域:一個花括號{}包裹起來的區(qū)域
此條例同Item13相似:最小化類和成員變量的訪問權限
Java允許你在任何地方聲明變量,但是最重要的是在首次使用的地方聲明變量,并初始化
循環(huá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! 應該是i2
doSomethingElse(i2.next());
}
當我們寫一個差不多的代碼,從一個地方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
對于不需要下標來做特殊操作的遍歷,推薦使用增強for循環(huán)
你能發(fā)現下面的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ā)現不了也不要難過,很多有經驗的程序員也會犯這個錯誤
原因在于i.next()會被重復調用,導致結果異常
可以修復如下
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ā)現一個庫并使用它,往往比自己寫的要好
因為庫會隨著時間推移更新迭代越來越好,自己也節(jié)約了時間
這并不會評論一個人的能力
Item 48: Avoid float and double if exact answers are required
當需要一個精確的答案時,避免使用float或double類型的變量
float或double是為了科學和工程計算而設計的,特別不適用于貨幣計算
推薦使用int或long代替
Item 49: Prefer primitive types to boxed primitives
使用原始類型替代裝箱類型
byte short char int long float double boolbean等是Java中的基本類型,也有對應的裝箱類型,如 Integer, Double, and Boolean.應當謹慎對待這兩者之間的差別
首先第一個差別,原始類型僅僅包含對應的值,裝箱類型既包含對應的值也有對應的引用,第二個差別是裝箱類型有可能為null,第三個差別是原始類型在時間和空間消耗中更高效
Item 50: Avoid strings where other types are more appropriate
如果有更合適的類型,避免使用String
string被設計成描述文本類型的數據,而且干得很好,本章主要討論將string用于其它情況的錯誤用法
string不能替代值類型 如果我們正在等待鍵盤的輸入,或者從網絡獲取某個值,我們很方便的使用string作為接收類型,但是如果我們輸入的是數字或者真假值的話,對應的int或boolean能更好的標識輸入 雖然這條規(guī)則很明顯,但是經常被違反
string不能替代枚舉 如Item30:枚舉討論的那樣,對于靜態(tài)常量使用枚舉
string不能替代聚合字符
String compoundKey = className + "#" + i.next();//bad
我們經常寫上述代碼,這樣的寫法有很多缺點.如果我們想使用某一部分字段需要解析字符串,很耗時而且容易出錯.String提供的equals,compareTo等方法也不能使用
比較好的方式是寫一個靜態(tà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();
}
當numItems()==100;lineForItem(i)返回80個長度的字符時,在作者的機器上方式二比方式一快85倍
如果我們需要拼接大量字符時,使用StringBuilder代替
Item 52: Refer to objects by their interfaces
使用接口代替對象引用
如果有一個合適的接口來描述當前類的時候使用這個接口來引用,如
// Good - uses interface as type
Listsubscribers = new Vector();
// Bad - uses class as type!
Vectorsubscribers = new Vector();
如果你養(yǎng)成了這個習慣,那么你的代碼將會更靈活
有幾種情況不能使用接口
1.沒有合適的接口聲明
2.接口中沒有想要的某個方法
3.使用的類集成自framework,并且是一個抽象類
Item 53: Prefer interfaces to reflection
使用接口代替反射
反射核心類java.lang.reflect提供了對加載類信息的訪問
例如,Method.Invoke允許在任何類的任何對象上調用任何方法(受通常的安全約束)。 即使編譯后的類不存在,也允許一個類使用另一個類。然而,這種力量是有代價的。
你不能在編譯時檢查出異常 如果你使用了對應的反射操作,在發(fā)生異常時,只有在運行時才能發(fā)現
閱讀性極差
性能有影響 使用反射比普通調用要慢些,因為受很多因素的影響,慢多少很難說,在作者的機器上,速度差在兩倍到五十倍不止
反射核心用在基于組件設計的應用,為了按需加載類,使用反射找到對應的類構造與否;普通應用盡量不要使用反射,找到代替的接口或者父類對象
Item 54: Use native methods judiciously
明智地使用本地方法
JNI允許Java調用C或C++寫的本地方法
從歷史上看,本地方法有三種主要用途。
1.它們提供了對特定于平臺的設施的訪問,例如注冊表和文件鎖。
2.他們提供了對舊代碼庫(Java想使用歷史上C或C++寫的庫)
的訪問,可以反過來提供對舊數據的訪問。
3.使用本地方法用本地語言編寫應用程序關鍵部分,以提高性能。
Java平臺在不斷發(fā)展,訪問特定平臺設施,使用Java提供的工具類就可做到,而且也不建議使用本地方法來提升程序性能
使用本地方法有嚴重的缺點
1.本地語言是不安全的,會受機器內存錯誤的影響
2.依賴于平臺,不便于移植
3.本地代碼很難調式
4.訪問本地代碼開銷很大
5.本地代碼不宜閱讀
總之,要再三思考是否使用本地代碼,如果需要使用以前的代碼庫,請盡可能減少本地代碼片段并加強測試,很小很小的本地代碼錯誤將破壞你的整個程序
Item 55: Optimize judiciously
明智的優(yōu)化
有三個人人都應該知道的優(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]
我們應該忽略小效率,大約百分之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]
關于優(yōu)化遵循如下兩個原則:
原則1.別這樣做
原則2.(僅對專家而言).還不要做,直到你找到一個清晰的解決方案或者一個未優(yōu)化的解決辦法
不要為了性能而損壞架構,致力于寫出好的程序而不是快的程序,如果一個好的程序還不夠快,它的架構會允許優(yōu)化.
這并不意味著當你的程序完后不需要優(yōu)化,你應該在設計階段就考慮到性能
盡量避免影響性能的設計,已經實現好的組件很難在改變,尤其是API,數據結構,多方約定好的協(xié)議
考慮好API使用效果,設計一個可變的類可能會導致后續(xù)使用中出現過多的深拷貝,造成對象分配的額外開銷
API的設計對性能有很真實的影響,如java.awt.Component中的getSize()方法,每調用一次就會返回一個新的 Dimension 實例(JDK1.2版本已經修復),雖然分配一個實例的開銷很小,但是成百上千次調用也會對程序有嚴重影響
幸運的是,好的API設計自然帶來了好的性能
總之,致力于寫出好的程序,快隨之而來 當做出一部分改變后就要測量代碼的性能,對于Android來說內存,卡頓,anr等方面
參考DDMS性能調優(yōu)
?
Item 56: Adhere to generally accepted naming conventions
遵循公共的命名規(guī)范,參考阿里巴巴發(fā)布的Android手冊
上一章:方法
下一章:異常
總結
以上是生活随笔為你收集整理的java最小访问原则_Android基础进阶之EffectiveJava翻译系列(第七章:通用原则)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java头像交互式差分演变_一种基于交互
- 下一篇: 华为Android9.0谷歌框架,华为M