30個精簡代碼的小技巧,一塊兒學起來!

30個精簡代碼的小技巧,一塊兒學起來!

前言

優化代碼,一個很重要的課題。可能有些人以爲沒用,一些細小的地方有什麼好修改的,改與不改對代碼的運行效率有什麼影響呢?這個問題我是真麼考慮的,比如人吃飯,吃一粒米,沒用,可是一萬粒,十萬粒呢,這樣的效率就很可觀了。java

代碼優化的目標是:算法

  • 減少代碼體積;
  • 提升代碼運行效率。

具體方法:

1.儘可能指定類,方法的final修飾符

帶有final的修飾符的類是不可派生的。在java核心API中,有許多應用final的例子,例如:java.long.String,整個類都是final的。爲類指定final修飾符可讓類不可被繼承,爲方法指定final修飾符可讓方法不被重寫。若是指定了一個類爲final,則該類全部的方法都是final的。數據庫

java編譯器會尋找機會內聯全部的final方法,內聯對於提高java運行效率做用重大,大概能使性能提高50%。編程

內聯:一般是用來消除調用函數時所須要的時間數組

2.儘可能複用對象

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

3.儘量使用局部變量

調用方法是傳遞的參數以及在調用中建立的臨時變量都保存在棧中,相對速度比較快。其餘變量,如,靜態變量,實例變量等,都在堆中建立,速度較慢。另外,棧中建立的變量,隨着方法的運行結束,這些內容就消失了,不須要額外的垃圾回收。服務器

4.及時關閉流

java編程過程當中,進行的數據庫鏈接,I/O流等操做時務必小心,在使用完畢後,應及時關閉流以釋放資源。由於這些大對象的操做會形成系統大的開銷,會大大影響程序運行效率。併發

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

明確概念,對方法的調用,即便方法中只有一條語句,也是要加載的。包括建立堆棧。
調用方法時保護現場,方法結束時恢復現場等。如:app

for(int i = 0 ; i < list.size();i++) {
     }

能夠替換爲:dom

for(int i = 0,length=list.size();i < length;i++) {
     }

這樣,若是list.size()裏的數據有不少時(如2000000左右),會減小不少性能消耗。

6.儘可能使用懶加載策略,即在須要時才建立

如:

String str = 「aaa」;
if(i == 1) {
list.add(str);
}

能夠替換爲:

if(i == 1) {
String str = 「aaa」;
list.add(str);
}

7.慎用異常

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

8.不要在循環中使用try···catch···

應該把它放到最外層。

9.工具類指定初始長度。

若是能估計到待添加的內容長度,爲底層以數組方式實現的集合,工具類指定初始長度好比ArrayList、LinkedList、StringBuilder、StringBuffer、HashMap、HashSet等等,

以StringBuilder爲例:

(1) StringBuilder() //默認分配16個字符空間
(2) StringBuilder(int size) //默認分配size個字符空間
(3) StringBuilder(String str) //默認分配16個字符+str.length()個空間

能夠經過類(不只僅是StringBuilder)來設定它的初始化容量,這樣能夠明顯提高性能。

好比,StringBuilder吧,length表示當前的StringBuilder能保持的字符數量。由於當StringBuilder達到最大容量的時候,它會將自身容量加到當前的2倍在加2,不管什麼時候,只要StringBuilder達到它的最大容量值,它就會建立一個新的字符數組而後將舊的字符數組裏面的內容拷貝到新數組裏是一個十分耗時的工做。

好比,一個字符數組大概要放5000個字符而不指定長度,最接近5000的2次冪是4096,那麼:

  • 在4096的基礎上,在申請8194+2個大小的新數組,加起來至關於共申請了12292的內存空間,若是一開始就指定長度5000或5500的話,就能節省一倍的空間。
  • 把原來的4096個字符拷貝到新的字符數組中,這樣不只浪費內存又下降代碼運行效率。

因此,給底層以數組實現的集合、工具類設置一個合理的初始值是不會有錯的。

可是,注意,向HashMap這種以數組+鏈表實現的集合,別把初始值大小和你預估的大小設置的同樣,由於一個table上鍊接一個對象的機率幾乎爲0。

建議初始大小值設爲2的N此冪,若是預估是2000個元素,設置成 new HashMap(128)、new HashMap(256)均可以。

10.當複製大量數據時,使用System.arraycopy()命令。

11.乘法和除法使用移位操做

如:

for(val = 0;val < 100000;val += 5) {
            a = val * 8;
            b = val / 2;
        }

用移位操做能夠極大的提高性能,由於在計算機底層,對位的操做是最方便的
能夠替換爲:

for(val = 0;val < 100000;val += 5) {
            a = val << 3;
            b = val >> 1;
        }

注:移位操做雖然方便,可是可能使代碼不太好理解,所以須要加上相應的註釋。

12.循環內不要不斷建立對象引用

如:

for(int i = 0;i <= count; i++) {
            Object obj = new Object();
        }

這種作法會致使內存中有count份Object對象引用存在,count很大的話,就耗費內存了,能夠替換爲:

Object obj = null;
        for(int i = 0;i <= count;i++) {
            obj = new Object();
        }

這樣的話,內存中只有一份Object對象引用,每次new Object()的時候,Object對象引用指向不一樣的Object而已,可是內存中只有一份,就能夠節省不少內存空間了。

13. 儘量使用array

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

14. 不推薦使用Hashtable,Vector,StringBuffer,

儘可能使用除非線程安全須要,不然不推薦使用Hashtable,Vector,StringBuffer,後三者因爲使用同步機制而致使了性能開銷。

15.不要將數組聲明爲public static final

由於這樣毫無心義,這樣知識定義了引用爲static,final,數組的內容仍是能夠隨意改變的,將數組聲明爲一個public更是一個安全漏洞,這意味着整個數組能夠被外部類所改變。

16.儘可能在什麼時候的場合使用單例

使用單例能夠減輕加載的負擔,縮短加載時的時間,提升加載的效率,但並非全部的地方都適用於單例,簡單說,單例主要適用於如下三個方面:

控制資源的使用,經過線程同步來控制資源的併發訪問;
控制實例的產生,達到節約資源的目的;
控制數據的共享,在不創建直接關聯的條件下,讓多個不相關的進程或線程之間實現通訊。

17.儘可能避免隨意使用靜態變量

由於當某個對象被定義爲static時,gc一般是不會回收這個對象所佔有的堆內存的,
如:

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

此時靜態變量b的生命週期與A類相同,若是A類不被卸載,那麼引用B指向的B對象會一直存在內存中,直到程序終止。

18.及時清除再也不須要的會話

當應用服務器須要保存更多會話時,若是內存不足,操做系統會把部分數據轉移到磁盤裏,應用服務器也可能根據MRU(最近頻繁使用的會話)算法,把部分不活躍的會話轉存到磁盤裏,甚至可能拋出內存不足的異常。若是會話要被轉存到磁盤,就必須先序列化,在大規模集羣中,對對象進行序列化代價是很大的。所以,應及時調用HttpSession的invalidate()方法清除會話。

19. 使用for循環遍歷

實現RandomAccess接口的集合好比ArrayList,應當使用for循環而不是foreach來遍歷JDK API對於RandomAccess接口的解釋是:實現RandomAccess接口用來代表其支持快速隨機訪問,此接口的主要目的是容許通常的算法更改其行爲,從而將其應用到隨機或連續訪問列表時可以提供良好的性能。

實現RandomAccess接口類實例,加入是隨機訪問的,使用for循環比foreach效率高;若是不是隨機訪問的使用foreach效率高。
如:

if(list instanceof RandomAccess) {
                for(int i = 0 ;i < list.size();i++){} 
            } else {
                for(List li : list) {
                    System.out.println(li);
                }
            }

foreach底層實現原理就是迭代器(iterator)

20.使用同步代碼塊代替同步方法

除非能肯定整個方法都是須要進行同步的,不然儘可能使用同步代碼塊,避免對那些不須要同步的代碼也進行同步,從而影響效率。

21.將常量聲明爲 STATIC FINAL

這樣在編譯運行時就能夠把這些內容放入常量池中,避免運行期間計算生成常量的值。另外,將常量的名字進行大寫的緣由。

22.程序運行過程當中避免使用反射

反射是java提供給用戶一個很強大的功能,可是功能強大效率卻不是很高。不建議在程序運行過程當中頻繁是哦那個反射機制,特別是Method的invoke方法。若是確實必要,建議將那些須要經過反射加載的類在項目啓動的時候經過反射實例化出一個對象並放入內存。

23.使用數據庫鏈接池和線程池

這兩個池都是重用與對象的,前者能夠避免頻繁打開和關閉鏈接:後者能夠避免頻繁建立和銷燬線程。

24.使用帶緩衝的輸入,輸出流進行I/O操做

帶緩衝的輸入,輸出流即:

BufferedReader,bufferedWrite,BufferedInputStream,BufferedOutputStream

它們能夠大大提高I/O的效率

25.合理使用 ArrayList和LinkedList

順序插入和隨機訪問比較多的場景使用ArrayList,元素刪除和中間插入比較多的場景使用LinkedList理解這兩個集合有何不一樣便可

26.不要讓public方法中有太多形參

public方法是對外提供的方法,若是給這些方法太多形參的話有兩點壞處:

  • 違反面相對象的思想,java講究萬物皆對象,太多形參,和java編程思想不和
  • 參數較多會致使出錯機率增長

27.字符串變量和字符串常量,equals的時候,將字符串常量寫在前面

如:

Strring str = 「123」;
if(str.equals(「123」)) {
}

能夠替換爲:

Strring str = 「123」;
if(「123」.equals(str)) {
}

這麼作是爲了不空指針的出現(中期項目有講過)

28.不要對數組使用toString()方法

本意是想打印數組裏的內容,卻可能由於數組引用對象爲空而致使空指針異常。雖然對數組toString()沒有意義,可是對集合toString()是能夠打印出集合中的內容的,由於集合的父類AbstractCollections重寫了Object的toString()方法。

29.不要對超出範圍的基本數據類型作向下強制轉換,獲得的結果絕對是錯誤的。

30.數據類型轉爲字符串,toString()最快

把一個基本數據類型轉爲字符串,對象點toString()是最快的方法,對象點valueOf(數據)次之,數據+」」最慢,如,想把Integer i轉爲字符串類型,有三種方式:

  1. i.toString()
  2. i.valueOf(i)
  3. i+」」

下面測試

public static void main(String[] args) {
      int loopTime = 50000;
      Integer i = 0;
      long startTime = System.currentTimeMillis();
      for(int j = 0 ;j < loopTime;j++) {
          String str = String.valueOf(i);
      }

          System.out.println("String.valueOf():"+(System.currentTimeMillis()- startTime) +"ms");
          startTime = System.currentTimeMillis();
          for(int j = 0;j < loopTime;j++) {
              String str = i.toString();
          }
              System.out.println("Integer.toString():"+(System.currentTimeMillis()- startTime) +"ms");

              startTime = System.currentTimeMillis();
              for(int j = 0 ;j < loopTime;j++) {
                  String str = i + "";
              }
                  System.out.println("i+\"\":"+(System.currentTimeMillis()- startTime) +"ms");
              }

結果:

String.valueOf():11ms;
Integer.toString():5ms;
i + "":25ms;

原理是:

  1. String.valueOf()方法調用了Integer.toString()方法,可是在調用前先作了一次空判斷;
  2. Integer.toString()是直接調用;
  3. i + 「」是使用了StringBuilder實現,先用了append方法拼接,在用toString()獲取字符串
相關文章
相關標籤/搜索