程序代碼優化要點:java
字符串優化:程序員
String對象特色:正則表達式
String a="123";String b=new String("123");
的時候,編譯器雖然會建立一個新的String實例,可是實際值依然是指向常量池中的已有的123。咱們可使用a.intern(),String的intern方法返回常量池中的引用,intern是一個native本地方法。String a="123";a="456";
只是改變了對象的引用所指向的位置,實際的」123」是不變的。關於內存泄漏:算法
存在內存泄漏的方法:
String:數據庫
關於字符串分割和查找:apache
StringTokenizer的使用:
StringTokenizer是jdk自帶的字符串分割工具,因爲沒有使用正則匹配,因此速度更快,編程
StringBuffer和StringBuilder:設計模式
new StringBuilder().append()
,提升拼接效率,可是在大量循環拼接時,編譯器不夠智能,每次都生成新的StringBuilder,產生大量gc,因此性能不高,最好在循環中使用conact或本身構建StringBuffer或StringBuilder。List接口: 因爲篇幅過長,故拆分,請參考《Java性能優化筆記-List接口分析》//TODO
衡量一個程序是否優質,能夠從多個角度進行分析。其中,最多見的衡量標準是程序的時間複雜度、空間複雜度,以及代碼的可讀性、可擴展性。針對程序的時間複雜度和空間複雜度,想要優化程序代碼,須要對數據結構與算法有深刻的理解,而且熟悉計算機系統的基本概念和原理;而針對代碼的可讀性和可擴展性,想要優化程序代碼,須要深刻理解軟件架構設計,熟知並會應用合適的設計模式數組
首先,現在計算機系統的存儲空間已經足夠大了,達到了 TB 級別,所以相比於空間複雜度,時間複雜度是程序員首要考慮的因素。爲了追求高性能,在某些頻繁操做執行時,甚至能夠考慮用空間換取時間。緩存
1. 針對日誌記錄的優化
2. 針對數據庫鏈接的優化
共享數據庫鏈接。共有 5 次數據庫鏈接操做,每次都需從新創建數據庫鏈接,數據庫插入操做完成以後又當即釋放了,數據庫鏈接沒有被複用。爲了作到共享數據庫鏈接,能夠經過單例模式 (Singleton Pattern)得到一個相同的數據庫鏈接,每次數據庫鏈接操做都共享這個數據庫鏈接。這裏沒有使用數據庫鏈接池(Database Connection Pool)是由於在程序只有少許的數據庫鏈接操做,只有在大量併發數據庫鏈接的時候才須要鏈接池。
共享數據庫鏈接而獲得的性能提高的緣由是,數據庫鏈接是一個耗時耗資源的操做,須要同遠程計算機進行網絡通訊,創建 TCP 鏈接,還須要維護鏈接狀態表,創建數據緩衝區。若是共享數據庫鏈接,則只須要進行一次數據庫鏈接操做,省去了屢次從新創建數據庫鏈接的時間。
3. 針對插入數據庫記錄的優化
4. 針對多線程的優化
使用多線程實現併發 / 並行。清空數據庫表的操做,使用多線程而獲得的性能提高的緣由是,系統部署所在的服務器是多核多處理器的,使用多線程,給每一個任務分配一個線程執行,能夠充分地利用 CPU 計算資源。
6. 針對設計模式的優化,
回顧以上代碼優化過程:關閉日誌記錄、共享數據庫鏈接、使用預編譯 SQL、使用 SQL 批處理、使用多線程實現併發 / 並行、使用 DAO 模式抽象出數據訪問層,程序運行時間從最初的 100 秒左右下降到 15 秒如下,在性能上獲得了很大的提高,同時也具備了更好的可讀性和可擴展性。
十、當複製大量數據時,使用System.arraycopy()命令
十一、乘法和除法使用移位操做
十二、循環內不要不斷建立對象引用
1三、基於效率和類型檢查的考慮,應該儘量使用array,沒法肯定數組大小時才使用ArrayList
1四、儘可能使用HashMap、ArrayList、StringBuilder,除非線程安全須要,不然不推薦使用Hashtable、Vector、StringBuffer,後三者因爲使用同步機制而致使了性能開銷
1六、儘可能在合適的場合使用單例
1七、儘可能避免隨意使用靜態變量
要知道,當某個對象被定義爲static的變量所引用,那麼gc一般是不會回收這個對象所佔有的堆內存的
foreach循環的底層實現原理就是迭代器Iterator,
2一、將常量聲明爲static final,並以大寫命名
這樣在編譯期間就能夠把這些內容放入常量池中,避免運行期間計算生成常量的值。另外,將常量的名字以大寫命名也能夠方便區分出常量與變量
五、使用帶緩衝的輸入輸出流進行IO操做
帶緩衝的輸入輸出流,即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,這能夠極大地提高IO效率
2六、順序插入和隨機訪問比較多的場景使用ArrayList,元素刪除和中間插入比較多的場景使用LinkedList
這個,理解ArrayList和LinkedList的原理就知道了
字符串變量和字符串常量equals的時候將字符串常量寫在前面
30、不要對數組使用toString()方法
31.把一個基本數據類型轉爲字符串,基本數據類型.toString()是最快的方式、String.valueOf(數據)次之、數據+」」最慢
32使用最有效率的方式去遍歷Map
public static void main(String[] args) { HashMap<String, String> hm = new HashMap<String, String>(); hm.put(「111」, 「222」); Set<Map.Entry<String, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<String, String>> iter = entrySet.iterator(); while (iter.hasNext()) { Map.Entry<String, String> entry = iter.next(); System.out.println(entry.getKey() + 「\t」 + entry.getValue()); } }
避免instanceof非預期結果;
(instanceof用來判斷一個對象是不是一個類的實例,只能用於對象的判斷,不能用於基本類型的判斷(編譯不經過),instanceof操做符的左右操做數必須有繼承或實現關係,不然編譯會失敗。例:null instanceof String返回值是false,instanceof特有規則,若左操做數是null,結果就直接返回false,再也不運算右操做數是什麼類)
建議19:斷言絕對不是雞肋;
(防護式編程中常用斷言(Assertion)對參數和環境作出判斷。斷言是爲調試程序服務的。兩個特性:一、默認assert不啓用;二、assert拋出的異常AssertionError是繼承自Error的)。
建議21:用偶判斷,不用奇判斷;
(不要使用奇判斷(i%2 == 1 ? "奇數" : "偶數"),使用偶判斷(i%2 == 0 ? "偶數" : "奇數")。緣由Java中的取餘(%標識符)算法:測試數據輸入1 2 0 -1 -2,奇判斷的時候,當輸入-1時,也會返回偶數。
建議22:用整數類型處理貨幣;
(不要使用float或者double計算貨幣,由於在計算機中浮點數「有可能」是不許確的,它只能無限接近準確值,而不能徹底精確。不能使用計算機中的二進制位來表示如0.4等的浮點數。解決方案:一、使用BigDecimal(優先使用);二、使用整型)。
建議44:推薦使用序列化實現對象的拷貝;
(經過序列化方式來處理,在內存中經過字節流的拷貝來實現深拷貝。使用此方法進行對象拷貝時需注意兩點:一、對象的內部屬性都是可序列化的;二、注意方法和屬性的特殊修飾符,好比final、static、transient變量的序列化問題都會影響拷貝效果。一個簡單辦法,使用Apache下的commons工具包中的SerializationUtils類,直接使用更加簡潔方便)。
建議56:自由選擇字符串拼接方式;
(字符串拼接有三種方法:加號、concat方法及StringBuilder(或StringBuffer)的append方法。字符串拼接性能中,StringBuilder的append方法最快,concat方法次之,加號最慢。
第五章 數組和集合
建議60:性能考慮,數組是首選;
(性能要求較高的場景中使用數組替代集合)(基本類型在棧內存中操做,對象在堆內存中操做。數組中使用基本類型是效率最高的,使用集合類會伴隨着自動裝箱與自動拆箱動做,因此性能相對差一些)
建議64:多種最值算法,適時選擇;
(最值計算時使用集合最簡單,使用數組性能最優,利用Set集合去重,使用TreeSet集合自動排序)。
建議79:集合中的哈希碼不要重複;
(列表查找無論是遍歷查找、鏈表查找或者是二分查找都不夠快。最快的是Hash開頭的集合(如HashMap、HashSet等類)查找,原理:根據hashCode定位元素在數組中的位置。HashMap的table數組存儲元素特色:一、table數組的長度永遠是2的N次冪;二、table數組中的元素是Entry類型;三、table數組中的元素位置是不連續的;每一個Entry都有一個next變量,它會指向下一個鍵值對,用來鏈表的方式來處理Hash衝突的問題。若是Hash碼相同,則添加的元素都使用鏈表處理,在查找的時候這部分的性能與ArrayList性能差很少)。
第六章 枚舉和註解
建議83:推薦使用枚舉定義常量;
(在項目開發中,推薦使用枚舉常量替代接口常量和類常量)(常量分爲:類常量、接口常量、枚舉常量;枚舉常量優勢:一、枚舉常量更簡單;二、枚舉常量屬於穩態性(不容許發生越界);三、枚舉具備內置方法,values方法能夠獲取到全部枚舉值;四、枚舉能夠自定義方法)。
建議85:當心switch帶來的空值異常;
(使用枚舉值做爲switch(枚舉類);語句的條件值時,須要對枚舉類進行判斷是否爲null值。由於Java中的switch語句只能判斷byte、short、char、int類型,JDK7能夠判斷String類型,使用switch語句判斷枚舉類型時,會根據枚舉的排序值匹配。若是傳入的只是null的話,獲取排序值須要調用如season.ordinal()方法時會拋出NullPointerException異常)。
建議98:建議採用的順序是List<T>,List<?>,List<Object>;
(一、List<T>是肯定的某一個類型,編碼者知道它是一個類型,只是在運行期才肯定而已;二、List<T>能夠進行讀寫操做,List<?>是隻讀類型,由於編譯器不知道List中容納的是什麼類型的元素,沒法增長、修改,可是能刪除,List<Object>也能夠讀寫操做,只是此時已經失去了泛型存在的意義了)。
適時選擇不一樣的線程池來實現;
(Java的線程池實現從根本上來講只有兩個:ThreadPoolExecutor類和ScheduledThreadPoolExecutor類,仍是父子關係。爲了簡化並行計算,Java還提供了一個Executors的靜態類,它能夠直接生成多種不一樣的線程池執行器,好比單線程執行器、帶緩衝功能的執行器等,歸根結底仍是以上兩個類的封裝類)。
建議128:預防線程死鎖;
(線程死鎖(DeadLock)是多線程編碼中最頭疼問題,也是最難重現的問題,由於Java是單進程多線程語言。要達到線程死鎖須要四個條件:一、互斥條件;二、資源獨佔條件;三、不剝奪條件;四、循環等待條件;按照如下兩種方式來解決:一、避免或減小資源貢獻;二、使用自旋鎖,若是在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操做將「自旋」在那裏,直到該自旋鎖的保持者釋放了鎖爲止)。
一、不要在循環條件中計算,
二、儘量把變量、方法聲明爲final static類型
三、縮小變量的做用範圍,目的是加快GC的回收;
四、頻繁字符串操做使用StringBuilder或StringBuffer