Java進階 建立和銷燬物件
阿新 • • 發佈:2019-02-19
最近準備寫點Javase的東西,希望可以幫助大家寫出更好的程式碼。
1、給不可例項化的類提供私有構造器
比如:每個專案中都有很多工具類,提供了很多static型別的方法供大家使用,誰也不希望看到下面的程式碼:
TextUtils textUtils = new TextUtils();
if(textUtils.isDigitsOnly("123"))
{
//doSometing
}else
{
//doSomething
}
自己寫個工具類,總有人喜歡先初始化個例項在呼叫方法,然後還附帶一個警告:The static method isDigitsOnly(CharSequence) from the type TextUtils should be accessed in a static way 。 你建議他使用類名.方法,人家還不樂意,我又沒出錯,幹嘛要改,錯了你負責麼。所以最好的方式,讓他沒辦法new例項。為工具類新增私有構造器:
public class TextUtils {
private TextUtils() { /* cannot be instantiated */ }
這是android的TextUtils的原始碼,這樣就可以了,讓他妹的初始化例項~,當然你也可以在私有方法裡面扔個異常。
public class TextUtils { private TextUtils() { /* cannot be instantiated */ throw new UnsupportedOperationException("cannot be instantiated"); } }
對於異常的使用,一儘量使用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 提供了自動裝箱和解箱操作,雖然方便了程式碼的編寫,但是如果不注意,可能帶來不好的效果。
看下面的程式碼:
long start = System.nanoTime();
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++)
{
sum += i;
}
System.out.println(sum);
System.out.println(System.nanoTime() - start);//20995956735
如果你觀察了記憶體,會發現,一直GC一直在記憶體回收,並且計算時間需要20多秒,如果我說這段程式碼有個bug,導致程式碼執行很慢,以及耗費記憶體,你能找到嗎?
下面我修改下程式碼:
long start = System.nanoTime();
long sum = 0l;
for (long i = 0; i < Integer.MAX_VALUE; i++)
{
sum += i;
}
System.out.println(sum);
System.out.println(System.nanoTime() - start);//5029758632
這次執行不會出現GC一直回收記憶體,且速度也只需要5秒左右,可能眼神不好的,沒有發現哪個地方修改了。
問題就出在自動裝箱、解箱上。第一次的程式sum為Long型別,在計算sum+=i;時會把sum自動解箱成long sum 然後運算,運算完成後,再裝箱成Long sum,導致程式構造了大約2的32次方個多餘Long例項。所以各位且用且嚴謹。
4、對於自己管理記憶體的類,一定要清除不必要的物件引用,防止記憶體洩漏
看下面的程式碼:
package com.zhy._01;
import java.util.Arrays;
/*
* 使用陣列模擬棧
*/
public class MyStack
{
private static final int DEFAULT_INIT_SIZE = 10;
private Object[] eles = new Object[DEFAULT_INIT_SIZE];
/**
* 當前棧頂索引
*/
private int currentIndex;
/**
* 彈棧
*
* @return
*/
public Object pop()
{
if (currentIndex == 0)
throw new ArrayIndexOutOfBoundsException("stack is empty");
return eles[--currentIndex];
}
/**
* 壓棧
*
* @param o
*/
public void push(Object o)
{
ensureCapacity();
eles[currentIndex++] = o;
}
private void ensureCapacity()
{
if (eles.length == currentIndex)
{
eles = Arrays.copyOf(eles, currentIndex * 2 + 1);
}
}
}
程式碼中存在一個地方,導致了記憶體洩漏,你可以發現不?
return eles[--currentIndex];
這行程式碼導致,如果棧增長了特別大,然後呼叫多次pop彈棧,雖然currentIndex小了,但是棧始終保持中之前pop出的過期物件的引用,這就導致了記憶體洩漏。如果不注意甚至最終造成OOM。
應該改為:
/**
* 彈棧
*
* @return
*/
public Object pop()
{
if (currentIndex == 0)
throw new ArrayIndexOutOfBoundsException("stack is empty");
Object tmp = eles[--currentIndex];
eles[currentIndex] = null ;
return tmp ;
}
當然了,不要因為擔心記憶體洩漏,在每個變數使用完成後都新增xxx=null,對於消除過期引用的最好方法,就是讓包含該引用的變數結束生命週期,而不是顯示的清空。一般情況下,對於類自己管理的記憶體,應當警惕。
好了,就到這裡,這些內容都是我個人覺得值得知道,且在專案中會常遇到的,希望可以幫助到大家,嘿嘿,求評論,求贊。