這裏先事先提醒你們一句,本篇文章中討論的編碼優化技巧都是屬於一些「微優化」,也就是說即便咱們都按照本篇文章的技巧來優化代碼,在性能方面也是看不出有什麼顯著的提高的。使用合適的算法與數據結構將永遠是你優化程序性能的最主要手段,但本篇文章中不會討論這一塊的內容。所以,這裏咱們即將學習的並非什麼靈丹妙藥,而是你們應該把這些技巧看成一種好的編碼規範,咱們在平時寫代碼時就能夠潛移默化地使用這些編碼規範,不只可以在微觀層面提高程序必定的性能,也可讓咱們的代碼變得更加專業,下面就讓咱們來一塊兒學習一下這些技巧。java
建立對象歷來都不該該是一件隨意的事情,由於建立一個對象就意味着垃圾回收器須要回收一個對象,而這兩步操做都是須要消耗時間的。雖然說建立一個對象的代價確實很是小,而且Android 2.3版本當中又增長了併發垃圾回收器機制,這讓GC操做時的停頓時間也變得難以察覺,可是這些理由都不足以讓咱們能夠肆意地建立對象,須要建立的對象咱們天然要建立,可是沒必要要的對象咱們就應該儘可能避免建立。算法
下面來看一些咱們能夠避免建立對象的場景:編程
固然上面所說的只是一些表明性的例子,咱們所要遵照的一個基本原則就是儘量地少建立臨時對象,越少的對象意味着越少的GC操做,同時也就意味着越好的程序性能和用戶體驗。數組
若是你並不須要訪問一個對象中的某些字段,只是想調用它的某個方法來去完成一項通用的功能,那麼能夠將這個方法設置成靜態方法,這會讓調用的速度提高15%-20%,同時也不用爲了調用這個方法而去專門建立對象了,這樣還知足了上面的一條原則。另外這也是一種好的編程習慣,由於咱們能夠放心地調用靜態方法,而不用擔憂調用這個方法後是否會改變對象的狀態(靜態方法內沒法訪問非靜態字段)。數據結構
咱們先來看一下在一個類的最頂部定義以下代碼:併發
編譯器會爲上述代碼生成一個初始化方法,稱爲<clinit>方法,該方法會在定義類第一次被使用的時候調用。而後這個方法會將42的值賦值到intVal當中,並從字符串常量表中提取一個引用賦值到strVal上。當賦值完成後,咱們就能夠經過字段搜尋的方式來去訪問具體的值了。性能
可是咱們還能夠經過final關鍵字來對上述代碼進行優化:學習
通過這樣修改以後,定義類就再也不須要一個<clinit>方法了,由於全部的常量都會在dex文件的初始化器當中進行初始化。當咱們調用intVal時能夠直接指向42的值,而調用strVal時會用一種相對輕量級的字符串常量方式,而不是字段搜尋的方式。優化
另外須要你們注意的是,這種優化方式只對基本數據類型以及String類型的常量有效,對於其它數據類型的常量是無效的。不過,對於任何常量都是用static final的關鍵字來進行聲明仍然是一種很是好的習慣。ui
加強型for循環(也被稱爲for-each循環)能夠用於去遍歷實現Iterable接口的集合以及數組,這是jdk 1.5中新增的一種循環模式。固然除了這種新增的循環模式以外,咱們仍然還可使用原有的普通循環模式,只不過它們之間是有效率區別的,咱們來看下面一段代碼:
能夠看到,上述代碼當中咱們使用了三種不一樣的循環方式來對mArray中的全部元素進行求和。其中zero()方法是最慢的一種,由於它是把mArray.length寫在循環當中的,也就是說每循環一次都須要從新計算一次mArray的長度。而one()方法則相對快得多,由於它使用了一個局部變量len來記錄數組的長度,這樣就省去了每次循環時字段搜尋的時間。two()方法在沒有JIT(Just In Time Compiler)的設備上是運行最快的,而在有JIT的設備上運行效率和one()方法不相上下,惟一須要注意的是這種寫法須要JDK 1.5以後才支持。
可是這裏要跟你們提一個特殊狀況,對於ArrayList這種集合,本身手寫的循環要比加強型for循環更快,而其餘的集合就沒有這種狀況。所以,對於咱們來講,默認狀況下能夠都使用加強型for循環,而遍歷ArrayList時就仍是使用傳統的循環方式吧。
Java語言當中其實給咱們提供了很是豐富的API接口,咱們在編寫程序時若是可使用系統提供的API就應該儘可能使用,系統提供的API完成不了咱們須要的功能時才應該本身去寫,由於使用系統的API在不少時候比咱們本身寫的代碼要快得多,它們的不少功能都是經過底層的彙編模式執行的。
好比說String類當中提供的好多API都是擁有極高的效率的,像indexOf()方法和一些其它相關的API,雖然說咱們經過本身編寫算法也可以完成一樣的功能,可是效率方面會和這些方法差的比較遠。這裏舉個例子,若是咱們要實現一個數組拷貝的功能,使用循環的方式來對數組中的每個元素一一進行賦值固然是可行的,可是若是咱們直接使用系統中提供的System.arraycopy()方法將會讓執行效率快9倍以上。
咱們平時寫代碼時都被告知,必定要使用面向對象的思惟去寫代碼,而面向對象的三大特性咱們都知道,封裝、多態和繼承。其中封裝的基本思想就是不要把類內部的字段暴漏給外部,而是提供特定的方法來容許外部操做相應類的內部字段,從而在Java語言當中就出現了Getters/Setters這種封裝技巧。
然而在Android上這個技巧就再也不是那麼的受推崇了,由於字段搜尋要比方法調用效率高得多,咱們直接訪問某個字段可能要比經過getters方法來去訪問這個字段快3到7倍。不過咱們確定不能僅僅由於效率的緣由就將封裝這個技巧給拋棄了,編寫代碼仍是要按照面向對象思惟的,可是咱們能夠在能優化的地方進行優化,好比說避免在內部調用getters/setters方法。
那什麼叫作在內部調用getters/setters方法呢?這裏我舉一個很是簡單的例子:
能夠看到,上面是一個Calculate類,這個類的功能很是簡單,先將one和two這兩個字段進行了封裝,而後提供了getOne()方法獲取one字段的值,提供了getTwo()方法獲取two字段的值,還提供了一個getSum()方法用於獲取總和的值。
這裏咱們注意到,getSum()方法當中的算法就是將one和two的值相加進行返回,可是它獲取one和two的值的方式也是經過getters方法進行獲取的,其實這是一種徹底沒有必要的方式,由於getSum()方法自己就是Calculate類內部的方法,它是能夠直接訪問到Calculate類中的封裝字段的,所以這種寫法在Android上是不推崇的,咱們能夠進行以下修改:
改爲這種寫法以後,咱們就避免了在內部調用getters/setters方法,而對於外部而言Calculate類仍然是具備很好的封裝性的。