Java 創建和銷燬對象

1、給不可實例化的類提供私有構造器

比如:每個項目中都有很多工具類,提供了很多static類型的方法供大家使用,誰也不希望看到下面的代碼:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. TextUtils textUtils = new TextUtils();  
  2.         if(textUtils.isDigitsOnly("123"))  
  3.         {  
  4.             //doSometing  
  5.         }else  
  6.         {  
  7.             //doSomething  
  8.         }  
自己寫個工具類,總有人喜歡先初始化個實例在調用方法,然後還附帶一個警告:The static method isDigitsOnly(CharSequence) from the type TextUtils should be accessed in a static way 。 你建議他使用類名.方法,人家還不樂意,我又沒出錯,幹嘛要改,錯了你負責麼。所以最好的方式,讓他沒辦法new實例。

爲工具類添加私有構造器:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class TextUtils {  
  2.     private TextUtils() { /* cannot be instantiated */ }  

這是android的TextUtils的源碼,這樣就可以了,讓他妹的初始化實例~,當然你也可以在私有方法裏面扔個異常。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class TextUtils  
  2.     {  
  3.         private TextUtils()   
  4.         {   
  5.             /* cannot be instantiated */   
  6.                 throw new UnsupportedOperationException("cannot be instantiated");  
  7.         }  
  8.       }  

對於異常的使用,一儘量使用Java提供的異常類,這樣可以使你的API比較易讀和易懂。


2、正確使用String,避免創建不必要的對象

很多人面試的時候都遇到過這樣的問題:String s = new String("abc");請問創建了幾個對象。也從側面說明了這是個反面的代碼寫法:

a、String s = new String("abc");“abc”本身就是一個String的實例,所以new String創建了不必要的String實例

b、如果改寫成 String s = "abc",不僅只創建了一個實例,而且在同一臺VM中,對於“abc”(字符串的字面常量)還會重用。


3、優先使用基本類型,Java提供了8種基本類型,以及對應的裝箱基本類型,且在Java1.5 提供了自動裝箱和解箱操作,雖然方便了代碼的編寫,但是如果不注意,可能帶來不好的效果。

看下面的代碼:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. long start = System.nanoTime();  
  2.         Long sum = 0L;  
  3.         for (long i = 0; i < Integer.MAX_VALUE; i++)  
  4.         {  
  5.             sum += i;  
  6.         }  
  7.         System.out.println(sum);  
  8.         System.out.println(System.nanoTime() - start);//20995956735  

如果你觀察了內存,會發現,一直GC一直在內存回收,並且計算時間需要20多秒,如果我說這段代碼有個bug,導致代碼運行很慢,以及耗費內存,你能找到嗎?

下面我修改下代碼:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. long start = System.nanoTime();  
  2.         long sum = 0l;  
  3.         for (long i = 0; i < Integer.MAX_VALUE; i++)  
  4.         {  
  5.             sum += i;  
  6.         }  
  7.         System.out.println(sum);  
  8.         System.out.println(System.nanoTime() - start);//5029758632  

這次運行不會出現GC一直回收內存,且速度也只需要5秒左右,可能眼神不好的,沒有發現哪個地方修改了。

問題就出在自動裝箱、解箱上。第一次的程序sum爲Long類型,在計算sum+=i;時會把sum自動解箱成long sum 然後運算,運算完成後,再裝箱成Long sum,導致程序構造了大約2的32次方個多餘Long實例。所以各位且用且嚴謹。


4、對於自己管理內存的類,一定要清除不必要的對象引用,防止內存泄漏

看下面的代碼:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.zhy._01;  
  2.   
  3. import java.util.Arrays;  
  4.   
  5. /* 
  6.  * 使用數組模擬棧 
  7.  */  
  8. public class MyStack  
  9. {  
  10.     private static final int DEFAULT_INIT_SIZE = 10;  
  11.   
  12.     private Object[] eles = new Object[DEFAULT_INIT_SIZE];  
  13.     /** 
  14.      * 當前棧頂索引 
  15.      */  
  16.     private int currentIndex;  
  17.   
  18.     /** 
  19.      * 彈棧 
  20.      *  
  21.      * @return 
  22.      */  
  23.     public Object pop()  
  24.     {  
  25.         if (currentIndex == 0)  
  26.             throw new ArrayIndexOutOfBoundsException("stack is empty");  
  27.         return eles[--currentIndex];  
  28.     }  
  29.   
  30.     /** 
  31.      * 壓棧 
  32.      *  
  33.      * @param o 
  34.      */  
  35.     public void push(Object o)  
  36.     {  
  37.         ensureCapacity();  
  38.         eles[currentIndex++] = o;  
  39.     }  
  40.   
  41.     private void ensureCapacity()  
  42.     {  
  43.         if (eles.length == currentIndex)  
  44.         {  
  45.             eles = Arrays.copyOf(eles, currentIndex * 2 + 1);  
  46.         }  
  47.     }  
  48.   
  49. }  

代碼中存在一個地方,導致了內存泄漏,你可以發現不?

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. return eles[--currentIndex];  

這行代碼導致,如果棧增長了特別大,然後調用多次pop彈棧,雖然currentIndex小了,但是棧始終保持中之前pop出的過期對象的引用,這就導致了內存泄漏。如果不注意甚至最終造成OOM。

應該改爲:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 彈棧 
  3.      *  
  4.      * @return 
  5.      */  
  6.     public Object pop()  
  7.     {  
  8.         if (currentIndex == 0)  
  9.             throw new ArrayIndexOutOfBoundsException("stack is empty");  
  10.         Object tmp = eles[--currentIndex];  
  11.         eles[currentIndex] = null ;   
  12.         return tmp ;   
  13.     }  

當然了,不要因爲擔心內存泄漏,在每個變量使用完成後都添加xxx=null,對於消除過期引用的最好方法,就是讓包含該引用的變量結束生命週期,而不是顯示的清空。一般情況下,對於類自己管理的內存,應當警惕。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章