java程式碼之美(11)---java程式碼的優化
java程式碼的優化
隨著自己做開發時間的增長,越來越理解雷布斯說的: 敲程式碼要像寫詩一樣美。也能理解有一次面試官問我你對程式碼有潔癖嗎?
一段好的程式碼會讓人看就像詩一樣,也像一個乾淨房間會讓人看去很舒服。
一段好的專案程式碼我覺得可以用這三個維度去分析。1)效能
2)可擴充套件性
3)可讀性
有關程式碼的規範早在很久就有阿里巴巴的java開發手冊,裡面有非常多的規範。太多了,自己也沒完全記住,抽空也會時不時再去翻翻。
接下來就寫一些有關效能和可讀性一些習慣,不全以後想到什麼會再補充進來。
一、效能考慮
1、必須注意: 不對資料庫層做任何操作 如果業務的確需要,那也最好註解說明原因。
2、儘量減少對變數的重複計算。
在不做編譯優化的情況下,在迴圈中,迴圈條件會被反覆計算,如果不使用複雜表示式,而使迴圈條件值不變的話,程式將會執行的更快。
for (int i = 0; i < list.size(); i++) {...} //建議修改成: for (int i = 0, length = list.size(); i < length; i++) {...}
這樣list.size()只會呼叫一次,減少效能消耗。
3、儘量採用懶載入的策略,即在需要的時候才建立。
這個習慣需要培養,在寫邏輯的時候,尤其是建立物件的時候是否需要考慮懶載入。
例如:
A a = new A(); if (i == 1) { list.add(a); } //建議替換為: if (i == 1) { A a = new A(); list.add(a); }
4、字串累加。
1)迴圈外: 字串拼接可以直接使用String的+操作,沒有必要通過StringBuilder進行append.
2)迴圈內: 好的做法是在迴圈外宣告StringBuilder物件,在迴圈內進行手動append。不論迴圈多少層都只有一個 StringBuilder物件。
反編譯出的位元組碼檔案顯示每次迴圈都會 new 出一個 StringBuilder 物件,然後進行 append 操作,最後通過 toString 方法返回 String 物件,造成記憶體資源浪費。
StringBuffer sb = new StringBuffer(); sb.append("a"); sb.append("b"); sb.append("c"); sb.append("d"); //不在迴圈體內其實可以直接用加號,優化後一行程式碼: String sb="a"+"b"+"c"+"d";
有關JDK不同版本對String拼接的優化可以參考:jdk不同版本對String拼接的優化分析
5、儘量避免使用split。
split由於支援正則表示式,所以效率比較低。
替代
String str1="a,b,c,d,,f,g"; //可以考慮使用apache的StringUtils.split(string,char) List<String> list = Arrays.asList(StringUtils.split(str1, ",")); //可以考慮guava工具 List<String> list1=Splitter.on(",").splitToList(str1);
6、確定Stringbuffer的容量
Stringbuffer的構造器會建立一個預設大小(通常是16
)的字元陣列。在使用中,如果超出這個大小,就會重新分配記憶體,建立一個更大的陣列,並將原先的陣列複製過來,再丟棄舊的陣列。在大多數情況下,你可以在建立Stringbuffer的時候指定大小,這樣就避免了在容量不夠的時候自動增長,以提高效能。
例子:
Stringbuffer buffer = new Stringbuffer(); // violation buffer.append ("hello"); //更正好:為stringbuffer提供寢大小。一般迴圈體內使用都可以知道大小 Stringbuffer buffer = new Stringbuffer(max); buffer.append ("hello");
7、使用工具類 Arrays.asList()把陣列轉換成集合時,不能使用其修改集合相關的方 法。
它的 add/remove/clear 方法會丟擲 UnsupportedOperationException 異常。
說明
:asList 的返回物件是一個 Arrays 內部類,並沒有實現集合的修改方法。Arrays.asList 體現的是介面卡模式,只是轉換介面,後臺的資料仍是陣列。
String[] str = new String[] { "a", "b" }; List list = Arrays.asList(str); //第一種情況:list.add("c"); 執行時異常。 //第二種情況:str[0]= "gujin"; 那麼list.get(0)也會隨之修改。
8、查詢陣列元素,可以用Arrays.asList(T[] array).contains(T obj)
二、可讀性考慮
1、推薦儘量少用 else, if-else 的方式
可以考慮:
if(condition){ ... return obj; } // 接著寫 else 的業務邏輯程式碼;
說明
:如果非得使用if()...else if()...else...方式表達邏輯,【強制】請勿超過3層,超過請使用狀態設計模式。
正例
:邏輯上超過 3 層的 if-else 程式碼可以使用衛語句,或者狀態模式來實現。
接下來抽空會寫一篇超過三層if-else更好的解決方案部落格。
2、在 if/else/for/while/do 語句中必須使用大括號,即使只有一行程式碼。
避免使用: if (condition) statements;
3、使用條件操作符替代"if (cond) return; else return;" 結構。
//條件操作符更加的簡捷 if (isdone) { return 0; } else { return 10; } //更正 return (isdone ? 0 : 10);
4、Object 的 equals 方法容易拋空指標異常,應使用常量或確定有值的物件來呼叫 equals。
正例
: "test".equals(object);
反例
: object.equals("test");
說明
:推薦使用java.util.Objects (JDK7引入的工具類)
5、不允許出現任何魔法值(即未經定義的常量)直接出現在程式碼中。
反例
String key="Id#taobao_"+tradeId;cache.put(key, value);
6、取反操作符(!)降低程式的可讀性,所以不要總是使用。
boolean method (boolean a, boolean b) { if (!a) return !a; else return !b; }
7、註釋掉的程式碼儘量要配合說明,而不是簡單的註釋掉。
程式碼被註釋掉有兩種可能性:1)後續會恢復此段程式碼邏輯
。2)永久不用
。前者如果沒 有備註資訊,難以知曉註釋動機。後者建議直接刪掉(程式碼倉庫儲存了歷史程式碼)。
8、特殊註釋標記,請註明標記人與標記時間。
1)待辦事宜(TODO)
:( 標記人,標記時間,[預計處理時間]) 表示需要實現,但目前還未實現的功能。
2)錯誤不能工作(FIXME)
:(標記人,標記時間,[預計處理時間])在註釋中用 FIXME 標記某程式碼是錯誤的,而且不能工作,需要及時糾正的情況。
只要自己變優秀了,其他的事情才會跟著好起來(少將1)