1. 程式人生 > >Java進階 建立和銷燬物件

Java進階 建立和銷燬物件

最近準備寫點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,對於消除過期引用的最好方法,就是讓包含該引用的變數結束生命週期,而不是顯示的清空。一般情況下,對於類自己管理的記憶體,應當警惕。

好了,就到這裡,這些內容都是我個人覺得值得知道,且在專案中會常遇到的,希望可以幫助到大家,嘿嘿,求評論,求贊。