如何成爲Android高手

若立志成爲 Android 高手 ,若有耐心, 「 一瓶一鉢足矣 」 。 

   「 天下事有難易乎?爲之,則難者亦易矣;不爲,則易者亦難矣。人之爲學有難易乎?學之,則難者亦易矣;不學,則易者亦難矣。 」 想成爲 Android 高手?這可不是想象中寫幾行代碼 那麼容易的事情,但也不是不可實現。java

 


  如何作? 

   1 ,學會懶惰!奇怪吧?可是,你必定也據說過和感覺過這個世界某種程度上是由懶人推進的,生命在於懶惰,懶人創造世界。固然,懶惰也是真的傻傻的呆在 那裏什麼都不作,而是說要善於想出作事情的更好的方式,這樣就能夠節約大量的時間,也就有更多的機會懶惰了,同事也懶出了境界。在 Android 中如何懶 惰?《如何成爲Android 高手》一文就如何在 Android 中學會懶惰和朋友們進行了分享。程序員

沒搞錯吧?居然讓程序開發人員學會懶惰?程序開發人員多是世界上最爲忙碌的一類人啦!對,沒錯,學會懶惰!正由於程序開發人員忙碌,正由於程序開發人員可能會在客戶無限變化的需求之下沒日沒夜的加班,因此要學會懶惰,這樣,你就能夠把更多的時間浪費在美好的事物身上! 

如何懶惰: 

1 , Don't Reinvent the Wheel (不要重複發明輪子)。 

2 , Inventing the Wheel( 發明輪子 ) 。 



1 , Don't Reinvent the Wheel (不要重複發明輪子)。 

   「 輪子理論 」 ,也即 「 不要重複發明輪子 」 ,這是西方國家的一句諺語,原話是: Don't Reinvent the Wheel。 「 不要重複發明輪子 」 意思是企業中任何一項工做實際上都有人作過,咱們所須要作的就是找到作過這件事情的人。拿到軟件領域中就是指有的項目或功能,別人已經作過,咱們須要用 的時候,直接拿來用便可,而不要從新制造。   

   Android 號稱是首個爲移動終端打造的真正開放和完整的移動軟件。 Android 發佈後不久 Google 公司就發佈了操做系統核心 ( Kernel )與部分驅動程序的源代碼,到目前位置除了 Google Map 等 Google 公司的核心組件沒有開放源代碼外, Android 基本完成了徹底的開源,這就極大的促進了 Android 的普及和移植。受到 Android 開放行爲和開源精神的影響,在世界各地,有成千上萬的程序員喜歡和別人分享本身的聰明才智和本身編寫的代碼。你能夠在 Google 的 Android 討論組或者 Google 搜索引擎上搜索到不少優秀的程序代碼。這樣作並非鼓勵你們成天等着讓別人爲你編寫代碼,而是你能夠 「 站在偉人的肩 膀上 」 ,充分發揚 「 拿來主義 」 ,聰明地應用別人的程序代碼能夠節省你大量的時間。web

2 , Inventing the Wheel( 發明輪子 ) 。 

  發明輪子?不錯,發明輪子!咱們不只要發明輪子,更要成爲努力成爲世界上發明輪子的主導力量,惟有這樣,才能談的上中華名族軟件大業的真正強大。在 Android ,要發明輪子,就是咱們要主動的是解決一些世界上他人未解決的難題或者創造新的編程框架或者對 Android 進行深度的改造以適合本身的業 務發展須要。 Google 發佈了 Android 後不久,中國移動便投入了大量的人力和物力,在 Android 的基礎上建立融入本身業務並開發、封裝了新的 功能的和框架的 OMS ,這是 Android 中發明輪子的一個很是重要的例子。可能你會說,這發明輪子也太難了吧,別急,咱們慢慢來,開發一個框架特定領域 的框架吧!你可能會一臉無辜的說,開發一個框架是說的那麼容易嗎?固然不是啦。可是也並不是不可能,首先,咱們分析一下框架的魅力的源泉,看看 Spring 、 Struts 等 Java EE框架,在看看 .NET 框架,固然也能夠看看發展的如火如荼、層出不窮的 PHP 框架,她們的強大和魅力的源泉都在於: IoC(Inversion of Control) 。 

Don't call us, we'll call you (別找我,我會來找你的)。 

   2 ,精通 Android 體系架構、 MVC 、常見的設計 模式、控制反轉( IoC ):這一點難嗎? 「 學之,則難者亦易矣;不學,則易者亦難矣。 」

   3 ,編寫可重用、可擴展、可維護、靈活性高的代碼: Android 應用程序開發 的使用純粹面向對象的 Java 做爲開發語言,天然也就繼承了關於 Java 關於面向對象的優秀想思想,如何作?《如何成爲 Android 高手》一文就如何在 Android 中編寫可重用、可擴展、可維護、靈活性高的代碼和朋友們進行了分享。 

   4 ,高效的編寫高效的代碼:高效的編寫代碼和編寫高效的代碼好像天生就是死敵。彷佛開發速度上去了,程序的執行效率就下去了;程序的執行效率上去,開發速度就下去了。如何解決 兩者的忙着,請聽《如何成爲Android 高手》一文想你們娓娓道來。 

   5 ,學會至少一門服務器端開發技術 :沒搞錯吧,成爲 Android 高手還須要學習 服務端開發技術?對,須要!《如何成爲 Android 高手》一文就該問題和你們進行了分享。 

   「 蜀之鄙,有二僧:其一貧,其一富。貧者語於富者曰: " 吾欲之南海,何如? " 富者曰: " 子何恃而往? "曰: " 吾一瓶一鉢足矣。 " 富者曰: " 吾數年來欲 買舟而下,猶未能也。子何恃而往! " 越明年,貧者自南海還,以告富者,富者有慚色。西蜀之去南海,不知幾千裏也,僧富者不能至,而貧者至之,人之立志,顧 不如蜀鄙之僧哉 」

  若立志成爲 Android 高手,若有耐心, 「 一瓶一鉢足矣 」 。 

Android 一出生就被打上了富二代的胎記,不只僅是由於誕生於當今的網絡 霸主 Google ,更主要還有一個空前強大和壯觀的開放手機 聯盟 OHA ( Open Handset Alliance )提供全力的支持。 OHA 是什麼? OHA 涵蓋了中國移動、 T-Mobile 、 Sprint 等移動運營商,包括 HTC 、 Motolora 、三星等手機制造商,有 Google 爲表明的手機軟件 商,還有 Inter 、 Nvidia 爲標誌的底層硬件廠商和 Astonishing Tribe 等商業運做公司,該組織聲稱組織的全部成員都會基於 Android 來開發新的手機業務。 

  可是,要成爲 Android 高手並非一件容易的事情。並非不少人想象的可以飛快的寫出幾行漂亮的代碼去解決一些困難的問題就是 Android 高手 了。真正的 Android 高手須要考慮的問題遠遠不是寫些漂亮的代碼就足夠的。下面是成爲一名真正的 Android 高手必須掌握和遵循的一些準則: 

1 ,學會懶惰 

2 ,精通 Android 體系架構、 MVC 、常見的設計模式、控制反轉( IoC )算法

1 ,請看某個著名的 IT 公司一則招聘信息的其中一條要求: 「 熟悉 Android 系統架構及相關技術, 1 年以上實際Android 平臺開發經驗; 」 ,裏面很是明確的說道要求熟練 Android 系統架構,這從某種程度上說明了對 Android體系架構的理解的重要性。 
Linux Kernel :負責硬件的驅動程序、網絡、電源、系統安全以及內存管理等功能。 

Libraries 和 Android Runtime : Libraries :即 C/C++ 函數庫部分,大多數都是開放源代碼的函數庫,例如WebKit ,該函數庫負責 Android 網頁瀏覽器 的運行,例如標準的 C 函數庫 Libc 、 OpenSSL 、 SQLite 等,固然也包括支持遊戲開發 2D SGL 和 3D OpenGL | ES ,在多媒體方面有 MediaFramework 框架來支持各類影音和圖形文件的播放與顯示,例如 MPEG4 、 H.264 、 MP3 、 AAC 、 AMR 、 JPG 和 PNG 等衆多的多媒體文件格式。Android 的 Runtime 負責解釋和執行生成的 Dalvik 格式的字節碼。 

   Application Framework (應用軟件架構), Java 應用程序開發人員主要是使用該層封裝好的 API 進行快速開發。 

Applications: 該層是 Java 的應用程序層, Android 內置的 Google Maps 、 E-mail 、即時通訊工具、瀏覽器、MP3 播放器等處於該層, Java 開發人員開發的程序也處於該層,並且和內置的應用程序具備平等的位置, 能夠調用內置的應用程序,也能夠替換內置的應用程序。 

上面的四個層次,下層爲上層服務,上層須要下層的支持,調用下層的服務,這種嚴格分層的方式帶來的極大的穩定性、靈活性和可擴展性,使得不一樣層的開發人員能夠按照規範專心特定層的開發。 

Android 應用程序使用框架的 API 並在框架下運行,這就帶來了程序開發的高度一致性,另外一方面也告訴咱們,要想寫出優質高效的程序就必須對整個 Application Framework 進行很是深刻的理解。精通 Application Framework ,你就能夠真正的理解 Android 的設計和運行機制,也就更可以駕馭整個應用層的開發。數據庫

2 , Android 的官方建議應用程序的開發採用 MVC 模式。 
3 ,設計模式和 IoC( 控制反轉 )

毫無疑問, Android 的之因此可以成爲一個開放的氣象萬千的系統,與設計模式的精妙應用是分不開的,只要你稍微用心觀察,就會發如今 Android 中處處都是 A 設計模式或者設計模式的聯合運用,一下的設計模式是您想遊刃有餘的駕馭 Android 必須掌握的: 

l Template Method 模式 

l Factory Method 模式 

l Observer 模式 

l Abstract Factory 模式 

l Adapter 模式 

l Composite 模式 

l Strategy 模式 

l State 模式 

l Proxy 模式 

l Bridge 模式 

l Iterator 模式 

l Mediator 模式 

l Façade 模式 

Android 框架魅力的源泉在於 IoC ,在開發 Android 的過程當中你會時刻感覺到 IoC 帶來的巨大方便,就拿 Activity來講,下面的函數是框架調用自動調用的: 

protected void onCreate(Bundle savedInstanceState) ; 

不是程序編寫者主動去調用,反而是用戶寫的代碼被框架調用,這也就反轉了!固然 IoC 自己的內涵遠遠不止這些,可是從這個例子中也能夠窺視出 IoC 帶來的 巨大好處。此類的例子在 Android 隨處可見,例如說數據庫的管理類,例如說 Android 中 SAX 的 Handler 的調用等。有時候,您甚至須要本身 編寫簡單的 IoC 實現,上面展現的多線程如今就是一個說明。 

3 ,編寫可重用、可擴展、可維護、靈活性高的代碼 

4 ,高效的編寫高效的代碼 

5 ,學會至少一門服務器端開發技術編程

可能有朋友會問:學習 Android 應用程序開發爲何還須要學習學會至少一門服務器端開發技術呢?答案以下:一方面 Android 號稱是首個爲移動終端 打造的真正開放和完整的移動軟件。做爲一種移動終端,必須與服務器端結合才能發揮巨大的做用。簡言之,須要:雲端 + 雲的方式。 Android 是爲移動互聯 網時代量身打造的,移動互聯網時代的服務模式是 「 手機終端 + 互聯網絡 + 應用軟件 」 ,移動互聯網時代應用技術之一的 Android 只是用於開發移動終端軟 件,而服務端技術用於開發互聯網絡應用,因此將來移動互聯網時代軟件的主流應用模式將是 「 手機客戶端+ 互聯網絡應用服務端 」 ,這種模式要求作移動互聯網開 發的程序員不但要掌握像 Android 這樣的手機終端軟件技術還要掌握開發互聯網絡應用的服務器端技術。目前,軟件企業廣泛存在這樣的問題,作移動互聯網 開發 Android終端軟件的程序員不瞭解 web 應用技術,而作 web 應用的程序員不瞭解移動終端技術,這樣就致使了客戶端與服務端在銜接上出現了問題。 目前的現狀是:既掌握移動互聯網 Android 終端技術,又掌握 web 應用技術的程序員比較稀缺,隨着中國步入移動互聯網時代,企業對這種移動互聯網時代 綜合性人才的需求很旺盛。若是不瞭解 web應用技術,最終會遇到了技術和發展的瓶頸;另外一方面, Google 聯合 OHA 推出的真正優點之一也在於和和互聯網結合, Google 的用意之一也是想開闢新的終端去使用 Google 的優點服務。 

服務器端開發技術目前主流的有 Sun 的 Java EE 、微軟的 .NET ,開源的以 PHP 和 MySQL 爲表明的 LAMP 體系,咱們該選擇哪種呢?從理論上講,不少人傾向於選擇 Java EE ,畢竟它們都是使用 Java 做爲開發語言的,可是不少人面對 Java EE 衆多的框架就望而生畏,其實在學習 Java EE 的時候能夠從 Struts 入手,隨着業務的需求逐步深刻。固然,選擇微軟的 .NET 也行,畢竟該技術體系也佔有很大 市場份額。其實,筆者認爲,選擇 LAMP能夠是會得到最高的 「 性價比 」 的,一方面 PHP 是如今 Web 方面的主流語言,大多數新型的網站尤爲是創業性質的網 站通常都會選用 PHP 做爲服務端開發語言,另外一方面,前面也說過, Android 是爲移動互聯而生的,二者達到了完美的契合。canvas

 

 

三:編寫可重用、可擴展、可維護、靈活性高的代碼 

Android 應用程序 的開發 是使用 Java 編寫,在架構上使用 MVC ,鼓勵組件之間的若耦合。開發出編寫可重用、可擴展、可維護、靈活性高的代碼須要經歷遵循如下原則: 

l " 開 - 閉 " 原則 (OCP) :一個軟件 實體應當對擴展開放,對修改關閉。這個原則說的是,在設計 一個模塊的時候,應當使這個模塊能夠在不被修改的前提下被擴展。換言之,應當能夠在沒必要修改源代碼 的狀況下改變這個模塊的行爲。 

l 里氏代換原則( LSP ):一個軟件實體若是使用的是一個基類的話,那麼必定使用於其子類,並且它根本不能察覺出基類對象和子類對象的區別。 

l 依賴倒轉原則 (DIP) :要依賴於抽象 , 不要依賴於具體。 

l 接口隔離原則( ISP ):使用多個專門的接口比使用單一的總接口要好。一個類對另一個類的依賴性應當是創建在最小的接口上的。 

l 合成 / 聚合複用原則( CARP ):又稱合成複用原則 (CRP), 就是在一個新的對象裏面使用一些已有的對象,使之成爲新對象的一部分;新的對象經過向這些對象的委派達到複用已有功能 的目的。簡而言之就是:要儘可能使用合成/ 聚合,儘可能不要使用繼承。 

l 迪米特法則( LoD ):又稱最少知識原則( LKP ),就是說一個對象應當對其餘對象儘量少的瞭解。狹義的迪米特法則是指若是兩個類沒必要彼此直接通訊 , 那 麼這兩個類就不該當發生直接的相互做用 . 若是其中一個類須要調用另外一個類的方法的話 , 能夠經過第三者轉發這個調用 . 。廣義的迪米特法則是指一個模塊設計得 好壞的一個重要的標誌就是該模塊在多大的程度上將本身的內部數據 與實現有關的細節隱藏起來。信息的隱藏很是重要的緣由在於, 它可使各個子系統 之間脫耦 , 從而容許它們獨立地被開發 , 優化 , 使用閱讀以及修改 . 。 

靈活的使用設計模式能夠在面對變幻無窮的業務需求是編寫出可重用、可擴展、可維護、靈活性高的代碼。 



固然,因爲 Android 是運行 在移動設備上的,而移動設備的處理能力是有限的,因此有時間必須在編寫可重用、可擴展、可維護、靈活性高的代碼與高效的代碼之間作出適當的平衡。 



四:高效的編寫高效的代碼 

高效快速的編寫代碼和編寫高效率執行的代碼不少時候都是對立的死敵,不少時候,你想快速的開發,代碼的執行效率每每就會慢下來;你想編寫高效的代碼,開發速度就會慢下來。 

不重複發明輪子和發明新的輪子是高效的編寫高效的代碼的正確是道路。 

關於高效的代碼,下面網絡 的一篇文章,直接轉載(不知道是哪位哥們寫的)以下: 

「 現代的手持設備,與其說是電話,更像一臺拿在手中的電腦。可是,即便是 「 最快 」 的手持設備,其性能也趕不上一臺普通的臺式電腦。 

這就是爲何咱們在書寫 Android 應用程序 的時候要格外關注效率。這些設備並無那麼快,而且受電池電量的制約。這意味着,設備沒有更多的能力,咱們必須把程序寫的儘可能有效。 

本文討論了不少能讓開發者 使他們的程序運行更有效的方法,遵守這些方法,你可使你的程序發揮最大的效力。
對於佔用資源 的系統,有兩條基本原則: 
1. 不要作沒必要要的事 
2. 不要分配沒必要要的內存 

全部下面的內容都遵守這兩個原則。 

有些人可能立刻會跳出來,把本節的大部份內容歸於 「 草率的優化 」 ( xing: 參見 [The Root of All Evil] ),不能否認微優化( micro-optimization 。 xing: 代碼優化,相對於結構優化)的確會帶來不少問題,諸如沒法使用更有效的數據結構和算法。可是在手持設備上,你別無選擇。假如你認爲 Android 虛擬機的性能與臺式機至關,你的程序頗有可能一開始就佔用了系統的所有內存 ( xing: 內存很小),這會讓你的程序慢得像蝸牛同樣,更遑論作其餘的操做了。 

Android 的成功依賴於你的程序提供的用戶體驗。而這種用戶體驗,部分依賴於你的程序是響應快速而靈活的,仍是響應緩慢而僵化的。由於全部的程序都運行在同一個設備之上,都在一塊兒,這就若是在同一條路上行駛的汽車。而這篇文檔 就至關於你在取得駕照以前必需要學習 的交通規則。若是你們都按照這些規則去作,駕駛就會很順暢,可是若是你不這樣作,你可能會車毀人亡。這就是爲何這些原則十分重要。 

當咱們開門見山、直擊主題以前,還必需要提醒你們一點:無論 VM 是否支持實時( JIT )編譯 器( xing: 它容許實時地將 Java 解釋型程序自動 編 譯成本機機器語言,以使程序執行的速度更快。有些 JVM 包含 JIT 編譯器。),下面提到的這些原則都是成立的。假如咱們有目標徹底相同的兩個方法,在解釋 執行時 foo() 比 bar()快,那麼編譯以後, foo() 依然會比 bar() 快。因此不要寄但願於編譯器能夠拯救你的程序。設計模式

 

避免創建對象 

世界上沒有免費的對象。雖然 GC 爲每一個線程都創建了臨時對象池,可使建立對象的代價變得小一些,可是分配內存永遠都比不分配內存的代價大。 

若是你在用戶界面 循環中分配對象內存,就會引起週期性的垃圾回收,用戶就會以爲界面像打嗝同樣一頓一頓的。

因此,除非必要,應儘可能避免盡力對象的實例 。下面的例子 將幫助你理解這條原則: 

當你從用戶輸入的數據 中截取一段字符串時,儘可能使用 substring 函數取得原始數據的一個子串,而不是爲子串另外創建一份拷貝。這樣你就有一個新的 String 對象,它與原始數據共享一個 char 數組。 
若是你有一個函數返回一個 String 對象,而你確切的知道這個字符串會被附加到一個 StringBuffer ,那麼,請改變這個函數的參數和實現方式,直接把結果附加到 StringBuffer 中,而不要再創建一個短命的臨時對象。 
一個更極端的例子是,把多維數組分紅多個一維數組。 

int 數組比 Integer 數組好,這也歸納了一個基本事實,兩個平行的 int 數組比 (int,int) 對象數組性能要好不少。同理,這試用於全部基本類型的組合。 
若是你想用一種容器存儲 (Foo,Bar) 元組,嘗試使用兩個單獨的 Foo[] 數組和 Bar[] 數組,必定比 (Foo,Bar) 數組效率更高。(也有例外的狀況,就是當你創建一個 API ,讓別人調用它的時候。這時候你要注重對 API 藉口的設計而犧牲一點兒速度。固然在 API 的內部,你仍要儘量的提升代碼 的效率) 

整體來講,就是避免建立短命的臨時對象。減小對象的建立就能減小垃圾收集,進而減小對用戶體驗的影響。 

使用本地方法 

當你在處理字串的時候,不要吝惜使用 String.indexOf(), String.lastIndexOf() 等特殊實現的方法( specialty methods )。這些方法都是使用 C/C++ 實現的,比起 Java 循環快 10 到 100 倍。 

使用實類比接口好 

假設你有一個 HashMap 對象,你能夠將它聲明爲 HashMap 或者 Map:

Map myMap1 = new HashMap();
HashMap myMap2 = new HashMap();
哪一個更好呢? 

按照傳統的觀點 Map 會更好些,由於這樣你能夠改變他的具體實現類,只要這個類繼承自 Map 接口。傳統的觀點對於傳統的程序 是正確的,可是它並不適合嵌入式系統 。調用一個接口的引用會比調用實體類的引用多花費一倍的時間。 

若是 HashMap 徹底適合你的程序,那麼使用 Map 就沒有什麼價值。若是有些地方你不能肯定,先避免使用 Map,剩下的交給 IDE 提供的重構功能 好了。 ( 固然公共 API 是一個例外:一個好的 API 經常會犧牲一些性能) 

用靜態方法比虛方法好 

若是你不須要訪問一個對象的成員變量,那麼請把方法聲明成 static 。虛方法執行的更快,由於它能夠被直接調用而不須要一個虛函數表。另外你也能夠經過聲明體現出這個函數的調用不會改變對象的狀態。 

不用 getter 和 setter

在不少本地語言如 C++ 中,都會使用 getter (好比: i = getCount() )來避免直接訪問成員變量( i = mCount)。在 C++ 中這是一個很是好的習慣,由於編譯 器可以內聯訪問,若是你須要約束或調試 變量,你能夠在任什麼時候候添加代碼。 

在 Android 上,這就不是個好主意了。虛方法的開銷比直接訪問成員變量大得多。在通用的接口定義 中,能夠依照OO 的方式定義 getters 和 setters ,可是在通常的類中,你應該直接訪問變量。 

將成員變量緩存到本地 

訪問成員變量比訪問本地變量慢得多,下面一段代碼: 

Java 代碼 

1 for (int i = 0; i < this.mCount; i++)   

2     dumpItem(this.mItems[i]);  


最好改爲這樣: 

Java 代碼 

3 int count = this.mCount;   

4 Item[] items = this.mItems;   

5 for (int i = 0; i < count; i++)   

6     dumpItems(items[i]);  


(使用 "this" 是爲了代表這些是成員變量) 

另外一個類似的原則是:永遠不要在 for 的第二個條件中調用任何方法。以下面方法所示,在每次循環的時候都會調用 getCount() 方法,這樣作比你在一個 int 先把結果保存起來開銷大不少。 

Java 代碼 

7 for (int i = 0; i < this.getCount(); i++)   

8     dumpItems(this.getItem(i));  


一樣若是你要屢次訪問一個變量,也最好先爲它創建一個本地變量,例如: 

Java 代碼 

9 protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {   

10     if (isHorizontalScrollBarEnabled()) {   

11         int size = mScrollBar.getSize(false);   

12         if (size <= 0) {   

13             size = mScrollBarSize;   

14         }   

15         mScrollBar.setBounds(0, height - size, width, height);   

16         mScrollBar.setParams(computeHorizontalScrollRange(),computeHorizontalScrollOffset(),computeHorizontalScrollExtent(), false);   

17         mScrollBar.draw(canvas);   

18     }   

19 }  



這裏有 4 次訪問成員變量 mScrollBar ,若是將它緩存到本地, 4 次成員變量訪問就會變成 4 次效率更高的棧變量訪問。 

另外就是方法的參數與本地變量的效率相同。 

使用常量 

讓咱們來看看這兩段在類前面的聲明: 

Java 代碼 

20 static int intVal = 42;   

21 static String strVal = "Hello, world!";  


必以其會生成一個叫作 <clinit> 的初始化類的方法,當類第一次被使用的時候這個方法會被執行。方法會將 42 賦給intVal ,而後把一個指向類中常量表的引用賦給 strVal 。當之後要用到這些值的時候,會在成員變量表中查找到他們。 

下面咱們作些改進,使用 「final" 關鍵字: 

Java 代碼 

22 static final int intVal = 42;   

23 static final String strVal = "Hello, world!";  


如今,類再也不須要 <clinit> 方法,由於在成員變量初始化的時候,會將常量直接保存到類文件 中。用到 intVal 的代碼被直接替換成 42 ,而使用 strVal 的會指向一個字符串常量,而不是使用成員變量。 

將一個方法或類聲明爲 "final" 不會帶來性能的提高,可是會幫助編譯器優化代碼。舉例說,若是編譯器知道一個"getter" 方法不會被重載,那麼編譯器會對其採用內聯調用。 

你也能夠將本地變量聲明爲 "final" ,一樣,這也不會帶來性能的提高。使用 "final" 只能使本地變量看起來更清晰些(可是也有些時候這是必須的, 好比在使用匿名內部類的時候)( xing: 原文是 or you have to, e.g. for use in an anonymous inner class ) 

謹慎使用 foreach

foreach 能夠用在實現了 Iterable 接口的集合類型上。 foreach 會給這些對象分配一個 iterator ,而後調用hasNext() 和 next() 方法。你最好使用 foreach 處理 ArrayList 對象,可是對其餘集合對象, foreach 至關於使用iterator 。 

下面展現了 foreach 一種可接受的用法: 

Java 代碼 

24 public class Foo {   

25     int mSplat;   

26     static Foo mArray[] = new Foo[27];    

27   

28     public static void zero() {   

29         int sum = 0;   

30         for (int i = 0; i < mArray.length; i++) {   

31             sum += mArray[i].mSplat;   

32         }   

33     }   

34   

35     public static void one() {   

36         int sum = 0;   

37         Foo[] localArray = mArray;   

38         int len = localArray.length;   

39         for (int i = 0; i < len; i++) {   

40         sum += localArray[i].mSplat;   

41         }   

42     }    

43   

44     public static void two() {   

45         int sum = 0;   

46         for (Foo a: mArray) {   

47             sum += a.mSplat;   

48         }   

49     }   

50 }  



在 zero() 中,每次循環都會訪問兩次靜態成員變量,取得一次數組的長度。 
retrieves the static field twice and gets the array length once for every iteration through the loop.

在 one() 中,將全部成員變量存儲到本地變量。 
pulls everything out into local variables, avoiding the lookups.

two() 使用了在 java1.5 中引入的 foreach 語法。編譯器會將對數組的引用和數組的長度保存到本地變量中,這對訪問數組元素很是好。可是編譯 器還會在每次循環中產生一個額外的對本地變量的存儲操做(對變量 a 的存取)這樣會比 one() 多出 4 個字節,速度要稍微慢一些。 

綜上所述: foreach 語法在運用於 array 時性能很好,可是運用於其餘集合對象時要當心,由於它會產生額外的對象。 

避免使用枚舉 

枚舉變量很是方便,但不幸的是它會犧牲執行的速度和並大幅增長文件體積。例如: 

public class Foo {public enum Shrubbery { GROUND, CRAWLING, HANGING }}

會產生一個 900 字節的 .class 文件 (Foo$Shubbery.class) 。在它被首次調用時,這個類會調用初始化方法來準備每一個枚舉變量。每一個 枚舉項都會被聲明成一個靜態變量,並被賦值。而後將這些靜態變量放在一個名爲"$VALUES" 的靜態數組變量中。而這麼一大堆代碼,僅僅是爲了使用三個 整數。 

這樣 :

Shrubbery shrub = Shrubbery.GROUND; 會引發一個對靜態變量的引用,若是這個靜態變量是 final int ,那麼編譯器會直接內聯這個常數。 

一方面說,使用枚舉變量可讓你的 API 更出色,並能提供編譯時的檢查。因此在一般的時候你毫無疑問應該爲公共 API 選擇枚舉變量。可是當性能方面有所限制的時候,你就應該避免這種作法了。 

有些狀況下,使用 ordinal() 方法獲取 枚舉變量的整數值會更好一些,舉例來講,將: 

Java 代碼 

51 for (int n = 0; n < list.size(); n++) {   

52     if (list.items[n].e == MyEnum.VAL_X)// do stuff 1  

53     else if (list.items[n].e == MyEnum.VAL_Y)// do stuff 2  

54 }  



替換爲 :

Java 代碼 

55 int valX = MyEnum.VAL_X.ordinal();   

56 int valY = MyEnum.VAL_Y.ordinal();   

57 int count = list.size();   

58 MyItem items = list.items();   

59 for (int n = 0; n < count; n++) {   

60     int valItem = items[n].e.ordinal();   

61     if (valItem == valX)// do stuff 1  

62     else if (valItem == valY)// do stuff 2  

63 }  



會使性能獲得一些改善,但這並非最終的解決 之道。 

將與內部類一同使用的變量聲明在包範圍內 

請看下面的類定義: 

Java 代碼 

64 public class Foo {   

65     private int mValue;    

66     public void run() {   

67         Inner in = new Inner();   

68         mValue = 27;   

69         in.stuff();   

70     }   

71   

72     private void doStuff(int value) {   

73         System.out.println("Value is " + value);   

74     }   

75   

76     private class Inner {   

77         void stuff() {   

78             Foo.this.doStuff(Foo.this.mValue);   

79         }   

80     }   

81 }  



這其中的關鍵是,咱們定義了一個內部類 (Foo$Inner) ,它須要訪問外部類的私有域變量和函數。這是合法的,而且會打印出咱們但願的結果 "Value is 27" 。 

問題是在技術 上來說(在幕後) Foo$Inner 是一個徹底獨立的類,它要直接訪問 Foo 的私有成員是非法的。要跨越這個鴻溝,編譯器須要生成一組方法: 

Java 代碼 

82 static int Foo.access$100(Foo foo) {   

83     return foo.mValue;   

84 }   

85   

86 static void Foo.access$200(Foo foo, int value) {   

87     foo.doStuff(value);   

88 }  



內部類在每次訪問 "mValue" 和 "doStuff" 方法時,都會調用這些靜態方法。就是說,上面的代碼說明了一個問題,你是在經過接口方法訪問這些成 員變量和函數而不是直接調用它們。在前面咱們已經說過,使用接口方法( getter、 setter )比直接訪問速度要慢。因此這個例子就是在特定語法下面 產生的一個 「 隱性的 」 性能障礙。 

經過將內部類訪問的變量和函數聲明由私有範圍改成包範圍,咱們能夠避免這個問題。這樣作可讓代碼運行 更快,而且避免產生額外的靜態方法。(遺憾的是,這些域和方法能夠被同一個包內的其餘類直接訪問,這與經典的OO 原則相違背。所以當你設計公共 API 的時候應該謹慎使用這條優化原則) 

避免使用浮點數 

在奔騰 CPU 出現以前,遊戲 設計者作得最多的就是整數運算。隨着奔騰的到來,浮點運算處理器成爲了 CPU 內置的特性,浮點和整數配合使用,可以讓你的遊戲運行得更順暢。一般在桌面電腦上,你能夠隨意的使用浮點運算。 

可是很是遺憾,嵌入式處理器一般沒有支持浮點運算的硬件,全部對 "float" 和 "double" 的運算都是經過軟件 實現的。一些基本的浮點運算,甚至須要毫秒級的時間才能完成。 

甚至是整數,一些芯片有對乘法的硬件支持而缺乏對除法的支持。這種狀況下,整數的除法和取模運算也是有軟件來完成的。因此當你在使用哈希表或者作大量數學運算時必定要當心謹慎。 」數組

相關文章
相關標籤/搜索