Android應用開發提高系列(2)——《Practical Java 中文版》讀書筆記(下)

聲明

  歡迎轉載,但請保留文章原始出處:) 

    博客園:http://www.cnblogs.com

    農民伯伯: http://over140.cnblogs.com

 

系列

  Android應用開發提高系列(1)——《Practical Java 中文版》讀書筆記(上) 

 

正文 

  注意:條目和用語可能與書籍有所出入,但儘量保持原樣加一些自己的理解。
  一、性能

    1. 先把焦點放在設計、數據結構和算法身上

      備註:良好的設計、明智的選擇數據結構和算法可能比高效代碼更重要。

 

    2.  不要依賴編譯器優化技術

 

    3.  理解運行時(runtime)代碼優化

      備註:JIT將bytecode於運行時轉換爲本地二進制碼,從而提高性能。因此編譯後代碼被執行次數越多,本機代碼生成代價就很合算。

 

    4.  連接字符串使用StringBuffer要比String快,尤其是大量字符串拼接

 

    5.  將對象創建成本降至最小

      備註:複用既有對象,不要創建非必要的對象,只在需要的時候才創建它們。

 

    6.  將同步化(synchronization)降至最低

      備註:如果synchronized函數拋出異常,則在異常離開函數之前,鎖會自動釋放。如果整個函數都需要被同步化,爲了產生體積較小且執行速度較快的代碼,請優先使用函數修飾符,而不是在函數內使用synchronized代碼塊。

 

    7.  儘可能使用stack變量

      備註:如果在函數中頻繁訪問成員變量、靜態變量,可以用本地(local)變量替代,最後操作完後再賦值給成員/靜態變量。

 

    8.  儘可能的使用static、final和private函數

      備註:此類函數可以在編譯期間被靜態決議(statically resolved),而不需要動態議決(dynamic resolved)。(子類無法覆寫)

 

    9.  類的成員變量、靜態變量都有缺省值,務須重複初始化

      備註:記住,本地變量沒有缺省值(例如函數內定義的變量)。

 

    10.  儘可能的使用基本數據類型

      備註:如int、short、char、boolean,使得代碼更快更小。

 

    11.  不要使用枚舉器(Enumeration)和迭代器(Iterator)來遍歷Vector

      備註:使用for循環+get()

 

    12.  使用System.arraycopy()來複制數組

      備註:使用System.arraycopy()代替for循環,可以產生更快的代碼。如:

        public void copyArray(int[] src, int[] dest) {
            int size = src.length;
            System.arraycopy(src, 0, dest, 0, size);
        }

      System.arraycopy()是以native method實現的,可以直接、高效的移動原始數組到目標數組,因此它執行速度更快。

 

    13.  優先使用數組,然後才考慮Vector和ArrayList,理由:

      a).  Vector的get()是同步的

      b).  ArrayList基本上就是一個非線程同步的Vector,比Vector要快

      c).  ArrayList和Vector添加元素或移除元素都需要重新整理數組。

      備註:不要僅僅因爲手上有個數不定的數據需要存儲,就毫無選擇的使用Vector或ArrayList。可以考慮創建一個足夠大的數組,通常這樣可能會浪費內存,但性能上的收益可能超過內存方面的代價。

 

    14.  手工優化代碼

      a).  剔除空白函數和無用代碼

      b).  削減強度

        備註:以更高效的操作替換成本昂貴的操作。一個常見的優化手法是使用複式複製操作符(如+=、-=)。

      c).  合併常量

        備註:將變量聲明爲final,使得操作在編譯器就進行。

      d).  刪減相同的子表達式

        備註:可用一個臨時變量代替重複的表達式。

      e).  展開循環

        備註:如循環次數少且已知循環次數,可展開去掉循環結構,直接訪問數組元素。缺點是會產生更多代碼。

      f).  簡化代數

        備註:使用數學技巧來簡化表達式。(例如從1+..+100的問題)

      g).  搬移循環內的不變式

        備註:循環內不變化的表達式可用移至循環外,不必重複計算表達式。

 

    15.  編譯爲本機代碼

      備註:將程序的某部分編譯爲本機二進制代碼,然後通過JNI訪問。

 

  二、多線程

    1.  對於實例(instance)函數,同步機制鎖定的是對象,而不是函數和代碼塊。

      備註:函數或代碼塊被聲明爲synchronized並非意味它在同一時刻只能有一個線程執行(同一對象不同線程調用會阻塞)。Java語言不允許將構造函數聲明爲synchronized。

 

    2.  同步實例函數和同步靜態函數爭取的是不同的locks。

      備註:兩者均非多線程安全,可以使用實例變量進行同步控制,如(byte[] lock = new byte[0]),比其他任何對象都經濟。

 

    3.  對於synchronized函數中可被修改的數據,應使之成爲private,並根據需要提供訪問函數。如果訪問函數返回的是可變對象,那麼可以先克隆該對象。

 

    4.  避免無謂的同步控制

      備註:過度的同步控制可能導致代碼死鎖或執行緩慢。再次提醒,當一個函數聲明爲synchronized,所獲得的lock是隸屬於調用此函數的那個對象。

 

    5.  訪問共享變量時請使用synchronized或volatile

      備註:如果併發性很重要,而且不需要更新很多變量,則可以考慮使用volatile。一旦變量被聲明爲volatile,在每次訪問它們時,它們就與主內存進行一致化。如果使用synchronized,只在取得lock和釋放lock時候才一致化。

 

    6.  在單一操作(single operation)中鎖定所有用到的對象

      備註:如果某個同步函數調用了某個非同步實例函數來修改對象,它是線程安全的。使用同步控制時,一定要對關鍵字synchronized所作所爲牢記在心。它鎖定的是對象而非函數或代碼。

 

    7.  以固定而全局性的順序取得多個locks(機制)以避免死鎖。P/181~P/185

      備註:嵌入[鎖定順序]需要額外的一些工作、內存和執行時間。

 

    8.  優先使用notifyAll()而非notify()

      備註:notify()和notifyAll()用以喚醒處以等待狀態的線程,waite()則讓線程進入等待狀態。notify()僅僅喚醒一個線程。

 

    9.  針對wait()和notifyAll()使用旋轉鎖(spin locks)

      備註:旋轉鎖模式(spin-lock pattern)簡潔、廉價,而且能確保等待着某個條件變量的代碼能循規蹈矩。

 

    10.  使用wait()和notifyAll()替代輪詢(polling loops)

      備註:調用wait()時會釋放同步對象鎖,暫停(虛懸,suspend)此線程。被暫停的線程不會佔用CPU時間,直到被喚醒。如:

複製代碼
            public void run()
            {
                int data;
                while(true){
                    synchronized (pipe) {
                        while((data = pipe.getDate()) == 0){
                            try{
                                pipe.waite();
                            }
                            catch(InterruptedException e){}
                        }
                    }
                    
                    //Process Data
                }
            }
複製代碼

 

    11.  不要對已鎖定對象的對象引用重新賦值。

 

    12.  不要調用stop()和suspend()

      備註:stop()中止一個線程時,會釋放線程持有的所有locks,有攪亂內部數據的風險;suspend()暫時懸掛起一個線程,但不會釋放持有的locks,可能帶來死鎖的風險。兩種都會引發不可預測的行爲和不正確的行爲。

      當線程的run()結束時,線程就中止了運行。可以用輪詢+變量來控制,如下代碼:

複製代碼
            private volatile boolean stop;
            
            public void stopThread()
            {
                stop = true;
            }
    
            public void run()
            {
                while(!stop){
                    //Process Data
                }
            }
複製代碼

        注意:這裏使用了關鍵字volatile,由於Java允許線程在其 私有專用內存 中保留主內存變量的副本(可以優化),線程1對線程2調用了stopThread(),但線程2可能不會及時察覺到stop主內存變量已變化,導致不能及時中止線程。

 

  三、類與接口 

    1.  實現一個final類(immutable class 不可變類)時,請遵循下列規則:

      a). 聲明所有數據爲private

      b).  只提供取值函數(getter),不提供賦值函數(setter)

      c).  在構造函數中設置有實例數據

      d).  如果函數返回、接受引用final對象,請克隆這個對象。

      e).  區別淺層拷貝和深層拷貝應用場景。如拷貝Vector需要使用深層拷貝。

 

    2.  實現clone()時記得調用super.clone()

      備註:不管是淺層拷貝還是深層拷貝都需要調用super.clone()。

 

    3.  別隻依賴finalize()清理內存以外的資源

      備註:finalize()函數只有在垃圾回收器釋放對象佔用的空間之前纔會被調用,回收時可能並非所有符合回收條件的對象都被回收,也無法保證是否被調用、何時調用。實現finalize()方法時記得調用super.finalize()。

 

    4.  在構造函數內應避免調用非final函數,以免被覆寫而改變初衷。

 

結束

  書是從朋友那邊借過來的,拿到手也有一段時間,磨磨唧唧好多天才看了幾十頁,而餘下部分從上篇文章到這篇文章也不過才3-5天。發現以這種方式來看書也不錯,一方面能加快速度,一方面由於要寫文章更加認真細讀,還能提煉把書讀薄記錄分享出來,實在是很適合我這樣的 :)

發佈了19 篇原創文章 · 獲贊 28 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章