Java開發規範及性能優化

代碼優化,可能提及來一些人以爲沒用.但是我以爲應該平時開發過程當中,就儘可能要求本身,養成良好習慣,一個個小的優化點,積攢起來絕對是有大幅度效率提高的。java

代碼優化目標:算法

1.減小代碼體積數據庫

2.提升整個系統的運行效率,代碼細節憂化編程

3.儘可能指定類,方法的final修飾符,帶有final修飾符的類是不可派生的,在Java核心APl中,有許多應用final的例子,例如java.lang.string,整個類都是finaI的.爲類指定final修飾符可讓類不能夠被繼承,爲方法指定finaI可讓方法不能夠被重寫,若是指定一個類爲finaI,該類的全部方法都是final的- Java編譯器會尋找機會內聯全部的final方法,內聯對於提高Java運行效率做用重大,具體參見Java這行期優化,此舉可以提高性能平均50%。數組

4. 儘可能重用對象安全

特別是 string對象的使用,出現字特申連接時應該使用 StringBuilder/StringBuffer代替-因爲 Java虛擬機不只要花時間生成對象,之後可能還須要花時間對這些對象進行垃圾回收和處理,所以,,生成過多的對象將會給程序的性能帶來很大的影響,併發

5.儘量使用局部対象app

調用方法時傳選的參數以及在調用中創jvm

建的臨時變量都保存在淺中速度較快,,其餘變量,如靜態變量、實例變量等,,都在堆中建立,速度較慢-另外, 棧中建立的變量,隨着方法的運行結東,,這些內容就沒了,不須要額外的垃圾回收,函數

6. 及時關閉流

Java編程中, 進行數據庫鏈接、l/o流操做時務必當心,在使用完畢後,及時關閉以釋放資源-由於對這些大對象的操做會形成系統大的開銷,稍有不慎,將會致使嚴重的後果。

7. 儘可能減小對變量的重複計算

明確一個概念,對方法的調用,即便方法中只有一句語句,也是有消耗的,包括建立棧幀、調用方法時保護現場、調用方法完畢時恢復現場等-因此例以下面的操做:

for(int i= 01 i< list.size0; i++)

{_}建議答換爲, for(int i= 0, int length= list.size(); i< length; i++) {_}這樣,在list.size0很大的時候.就減少了不少的消耗

八、儘可能採用懶加載的策略.即在須要的時候才建立

例如:String str= "aaa";

if(i== 1) {

list.add(str); }建議替換爲:  if(i== l) {String str= •aaan;

list.add(str); }

九、慎用異常

異常對性能不利-拋出異常首先要建立一個新的對象,  Throwable接口的構造函數調用名爲 filllnstackrrace0的本地同步方法,  filllnstackrrace0方法檢査棧堆,收集調用跟蹤信息-只要有異常被拋出, Java虛擬機就必須調整調用棧堆,由於在處理過程當中建立了一個新的對象-異常只能用於錯誤處理,不該該用來控制程序流程。

十、不要在循環中使用try_catch_ ,應該把其放在最外,除非不得已-若是毫無理由地這麼寫了,只要你的領導資深一點、有強追症一點,八成就要罵你爲何寫出這種垃圾代碼來了。

十一、若是能估計到帶加的內容長度,爲底層以數組方式實現的集合、 工具類指定初始長度

好比ArrayList、 LinkedLlist、StringBuiIder、 StringBufler、HashMap、 HashSet等等,以StringBuilder爲例:

(l) StringBuilder//般認分配l6個字符的空間

(2) StringBuilder(int size) //默認分配size個字符的空間

(3) StringBuilder(String str) //般認分配l6個字符+str.length個字符空間,能夠經過類(這裏指的不只僅是上面的

stringBuilder)的來設定它的初始化容量, 這樣能夠明顯地提高性能-好比StringBuilder吧, length表示當前的stringBuilder能保持的字待數量.由於當stringBuilder達到最大容量的時候,它會將自身容量增長到當前的2倍再加2 , 不管什麼時候只要stringBuilder達到它的最大容量, 它就不得不建立一個新的字符數組而後將l日的字符數組內容拷貝到新字符數組中一一這是十分耗費性能的一個操做-試想,若是能預估到字符數組中大概要存放5ooo個字符而不指定長度,最接近5ooo的2次冪是4o96,每次擴容加的2 無論,那麼:

(l)在4096的基礎上.再申請 8194個大小的字符數組,加起來至關於一次申清了l229o個大小的字符數組,若是一開始能指定5ooo個大小的字特數組,就節省了一倍以上的空間

(2)把原來的4o96個字符持貝到新的的字特數組中去、這樣,既浪費內存空間,又下降代得運行效率-因此,給底層以數組實現的集合、工具類設置一個合理的初始化容量是不錯的,這會帶來立竿見影的效果-可是,注意,像HashMap這種是以數組+鏈表實現的集合, 別把初始大小和你估計的大小設置得同樣,由於一個table上只連接一個對象的可能性幾乎爲o,初始大小建議設置爲2的N次冪,若是能估計到有2ooo個元素,設置成new HashMap(l28)、 new HashMap(256) 均可以。

一樣道理,應儘量的以指明容量大小的方式對ArrayList進行實例化

public ArrayList();默認的構造器,JAVA以10個元素的大小初始化

public ArrayList(int);用指定的容量大小大小初始化

不指明容量大小時,如集合容量不夠,則JAVA會以1.5倍的容量遞增擴充,並且每次擴充,系統會從新拷貝一遍已經加入到ArrayList的數據,從而致使額外的內存開銷。

補充:使用StringBuilder來鏈接字符串

有不少不一樣的選項來鏈接Java中的字符串。例如,您可使用簡單的+或+ =,StringBuffer或一個StringBuilder。

那麼,你應該選擇哪一種方法?

答案取決於鏈接字符串的代碼。若是以編程方式將新內容添加到字符串中,例如在for循環中,則應使用StringBuilder。它很容易使用,並提供比StringBuffer更好的性能。但請記住,與StringBuffer相比,StringBuilder不是線程安全的,可能不適合全部用例。

你只須要實例化一個新的StringBuilder並調用append方法來向String中添加一個新的部分。而當你添加了全部的部分,你能夠調用toString()方法來檢索鏈接的字符串。

有不少不一樣的選項來鏈接Java中的字符串。例如,您可使用簡單的+或+ =,StringBuffer或一個StringBuilder。

那麼,你應該選擇哪一種方法?

答案取決於鏈接字符串的代碼。若是以編程方式將新內容添加到字符串中,例如在for循環中,則應使用StringBuilder。它很容易使用,並提供比StringBuffer更好的性能。但請記住,與StringBuffer相比,StringBuilder不是線程安全的,可能不適合全部用例。

你只須要實例化一個新的StringBuilder並調用append方法來向String中添加一個新的部分。而當你添加了全部的部分,你能夠調用toString()方法來檢索鏈接的字符串。

在一個語句中使用+鏈接字符串

當你用Java實現你的第一個應用程序時,可能有人告訴過你不該該用+來鏈接字符串。 若是您在應用程序邏輯中鏈接字符串,這是正確的。 字符串是不可變的,每一個字符串鏈接的結果都存儲在一個新的String對象中。 這須要額外的內存,並減慢你的應用程序,特別是若是你在一個循環內鏈接多個字符串。

在這些狀況下,您應該遵循上面的規則並使用StringBuilder。

可是,若是您只是將字符串分紅多行來改善代碼的可讀性,狀況並不是如此。

Java性能調優準則(絕對乾貨)

在這些狀況下,你應該用一個簡單的+來鏈接你的字符串。 您的Java編譯器將優化這個並在編譯時執行鏈接。 因此,在運行時,你的代碼將只使用1個字符串,不須要鏈接。

 

十二、當複製大量數據時,使用 System.arraycopy()命令

1三、乘法和除法使用移位操做

例如: for(val= 0; val< l00000; val +=5){a=val*8:b=val/2;}用移位操做能夠極大地提升性能,由於在計算機底層,對位的操做是最方便、最快的,所以建議修改成: for(val= o:val<100000; val+= 5) { a=val << 3; b = val>> 1;}移位操做星然快,可是可能會使代碼不太好理解,所以最好加上相應的註釋

1四、循環內不要不斷建立對象引用及不要在循環體內聲明變量

例如: for(inti= l:i<=count; i++) {0bject obj= new 〇bject0; }這種作法會致使內存中有count份object對象引用存在, count很大的話,就耗費內存了,,建議爲改成: 〇bject obj= null;for(int i= 01 i<=count; i十十) {obj= new object0; }這樣的話,內存中只有一份Object對象引用,每次new完object0的時候, Object對象引用指向不一樣的0bject 罷了,可是內存中只有一份, 這樣就大大節省了內存空間 -

l五、基於效率和類型檢査的考慮, 應該儘量使用array,沒法肯定數組大小時才使用ArrayList

l六、 儘可能使用HashMap、 ArrayList、stringBuilder,除非線程安全須要,不然不推薦使用Hashtable、 Vector、stringBuffer,後三者因爲使用同步機制而致使了性能開銷

l七、不要將數組聲明爲 public static final由於這室無心義, 這樣只是定義了引用爲 static final, 數組的內容仍是能夠隨意改變的,將數組聲明爲 public更是一個安全構洞,這意味着這個數組能夠被外部類所改變

l八、 儘可能在合適的場合使用單例

使用單例能夠減輕加裁的負擔、結短加載的時間、提升加裁的效率,但並非全部地方都適用於単例,笛単來講,単例主要適用於如下三個方面:

(l)控制資源的使用,經過線程同步來控制資源的併發訪問

(2)控制實例的產生,以達到節約資源的目的

(3)控制數據的共事,在不創建直接關聯的條件下,讓多個不相關的進程或線程之間實現通訊

1九、 儘可能進免隨意使用靜態變量

要知道,當某個對象被定義爲 static的變量所引用,那麼 gc一般是不會回收這個対象所佔有的堆內存的,如:

public class A{private static B b= new B0; }

20、避免使用包裝類構造函數

按照SUN公司的說明,使用自動裝箱或靜態工廠方法比使用new一個對象快3到4倍,該規則能夠用在valueOf或其它靜態工廠的調用中(如:Short、Integer, Long、Double, Byte 、Boolean等)。

public static void main(String args[]){
        //不推薦寫法
        int a=new Integer(100);
        System.out.println(a);
        //雖然這對於jvm來講,體現不出來明顯效果。
        //推薦寫法
        int b=Integer.valueOf(100);
        System.out.println(b);
    }

2一、面向接口編程時,推薦使用接口的聲明方式

爲何要用 List list = new ArrayList() ,而不用 ArrayList alist = new ArrayList()呢? 
問題就在於List有多個實現類,如今你用的是ArrayList,也許哪一天你須要換成其它的實現類,如 LinkedList或者Vector等等,這時你只要改變這一行就好了: 
List list = new LinkedList(); 其它使用了list地方的代碼根本不須要改動。 
假設你開始用 ArrayList alist = new ArrayList(), 這下你有的改了,特別是若是你使用了 ArrayList特有的方法和屬性。

List list = new ArrayList();這句建立了一個ArrayList的對象後把上溯到了List。此時它是一個List對象了,有些ArrayList有可是List沒有的屬性和方法,它就不能再用了。 
而ArrayList list=new ArrayList();建立一對象則保留了ArrayList的全部屬性。 

List list = new ArrayList(); 
ArrayList arrayList = new ArrayList();
list.trimToSize(); //錯誤,沒有該方法。
arrayList.trimToSize();   //ArrayList裏有該方法。

2二、單個方法代碼行數儘可能保持在80-100行以內

2三、數據庫及磁盤IO等操做,必須在try-catch-finally塊的finally中執行close()方法

若有多個對象須要關閉,則須分別對每一個對象的close()方法進行try-catch,以防止出現一個對象關閉失敗而致使其餘對象都未關閉的狀況出現

2四、不要在循環體內進行數據庫的「鏈接-關閉」操做

若有大批量的數據須要修改,建議使用PreparedStatement的Batch功能(一次性發送多個操做給數據庫)

2五、大量(如超過五次以上的「+=」運算)的字符串操做應使用StringBuilder或StringBuffer,儘可能避免使用String

2六、不要在循環條件中使用表達式

2七、集合中的數據若是不使用了應該及時釋放

因爲集合保存了對象的引用,虛擬機的垃圾收集器就不會回收

2八、非正常運行產生的異常被捕獲後,必須對異常進行處理

在非finally塊代碼中catch的異常應該從新拋出通過封裝的異常,在finally中再次catch的異常不該該再次拋出,應該寫日誌。不管是拋出異常仍是記錄日誌,都要傳遞異常對象。日誌應記錄詳細的描述信息,避免調用異常對象的getMessage()方法,直接將該異常對象做爲參數傳遞。

2九、儘可能定位異常類型,不要一概catch(Exception ex)

當須要在某些出口捕獲全部可能出現的運行時異常或Error時,能夠catch Exception 或Throwable,儘可能避免一個方法中一個大的try塊,catch一個Exception的代碼方式,若是有必要可使用多個try-catch塊分別處理

30、不要將賦值運算符用在容易與相等關係運算符混淆的地方,如:咱們可將「if (a == b && c == d)」改成可讀性更強的「if ((a == b) && (c == d)) 」,可將「x >= 0 ? x : -x;"改成」(x >= 0) ? x : -x;

3一、儘量使用基本數據

避免任何開銷並提升應用程序性能的另外一種簡便快速的方法是使用基本類型而不是其包裝類。 因此,最好使用int來代替Integer,或者使用double來代替Double。 這容許您的JVM將值存儲在棧而不是在堆中,以減小內存消耗,並更高效地處理它。

3二、儘可能避免使用BigInteger和BigDecimal

因爲咱們已經在討論數據類型,因此咱們也應該快速瀏覽一下BigInteger和BigDecimal。 尤爲是後者因其精確性而受歡迎。 可是這是有代價的。

BigInteger和BigDecimal須要更多的內存比一個long或double,而且看起來會下降全部的運行效率。 因此,若是你須要額外的精度,或者若是你的數字將超過一個長的範圍,最好三思。 這多是您須要更改以解決性能問題的惟一方法,特別是在實施數學算法時。

3三、使用Apache Commons的StringUtils.Replace來替代String.replace

通常來講,String.replace方法工做正常,效率很高,尤爲是在使用Java 9的狀況下。可是,若是您的應用程序須要大量的替換操做,而且沒有更新到最新的Java版本,那麼它仍然是有意義的 檢查更快和更有效的替代品。

一個候選項是Apache Commons Lang的StringUtils.replace方法。 正如Lukas Eder在他最近的一篇博客文章中所描述的,它遠遠超過了Java 8的String.replace方法。

並且這隻須要很小的改動。 您須要將Apache的Commons Lang項目的Maven依賴項添加到您的應用程序pom.xml中,並將String.replace方法的全部調用替換爲StringUtils.replace方法。

相關文章
相關標籤/搜索