改善java代碼的建議

這篇文章是在對《編寫高質量代碼:改善Java程序的151個建議》這本書中提到的內容做筆記。

通用的方法和準則

三元操作符的類型務必唯一

<span style="font-size:18px;">public class Client {
	public static void main(String[] args) {
		int i = 80;
		String s = String.valueOf(i<100?90:100);
		String s1 = String.valueOf(i<100?90:100.0);
		System.out.println("兩者是否相等:"+s.equals(s1));
	}
}</span>
可以看到輸出結果是false,因爲三元操作符的返回值必須唯一s的返回是int型,而s1是浮點型。

別人null值和空值影響變長方法

<span style="font-size:18px;">public class Client {
	public void methodA(String str,Integer... is){		
		System.out.println("Integer");
	}
	
	public void methodA(String str,String... strs){		
		System.out.println("String");
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.methodA("China", 0);
		client.methodA("China", "People");
//		client.methodA("China");
//		client.methodA("China",null);
	}
}</span>

//client.methodA("China")  由於變長方法重載,都滿足只有一個String的參數,編譯器沒辦法知道選在哪個函數,因此編譯不通過。

//client.methodA("China",null)  null是沒有類型的。編譯器沒辦法知道選在哪個函數,因此編譯不通過。

可以改用如下寫法:明確告訴編譯器strs類型,編譯通過

public class Client {
	public void methodA(String str,Integer... is){		
		System.out.println("Integer");
	}
	
	public void methodA(String str,String... strs){		
		System.out.println("String");
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		Integer[] strs = null;
		client.methodA("China",strs);
	}
}

警惕自增的陷阱

public static void main(String[] args) {
		int count =0;
		for(int i=0;i<10;i++){
			count=count++;
		}
		System.out.println("count="+count);
	}

可以看到,count最終的輸出值是0.

在jvm中的執行步驟如下:
1、jvm 把count值(0)拷貝到臨時變量區。
2、count值加1,此count爲1.
3、返回臨時變量區的值,此時是0,值沒有改變。
4、返回值賦值給count,count重新變爲0。

大概類似於如下代碼
public static int mockAdd(int count){
		//先保存初始值
		int temp =count;
		//做自增操作
		count = count+1;
		//返回原始值
		return temp;
	}

基本類型

用偶判斷,不用奇判斷

public static void main(String[] args) {
		//接收鍵盤輸入參數
		Scanner input = new Scanner(System.in);
		System.out.print("請輸入多個數字判斷奇偶:");
		while(input.hasNextInt()){
			int i = input.nextInt();
			String str =i+ "->" + (i%2 ==1?"奇數":"偶數");
			System.out.println(str);
			
		}
	}

可以看到,當輸入數字 -1 時,運行結果爲偶數。模擬下java %符號的運行代碼
<span style="font-size:18px;">//模擬取餘計算,dividend被除數,divisor除數
	</span><span style="font-size:14px;">public static int remainder(int dividend,int divisor){
		return dividend - dividend / divisor * divisor;
	}</span>
可以看到java執行 %操作時原來是這麼取值的。

因此修改這段代碼只需改爲 i%2==0?"偶數":"奇數"就可以。

邊界、邊界、邊界

public class Client {
	//一個會員擁有產品的最大數量
	public final static int LIMIT = 2000;
	public static void main(String[] args) {
		//會員當前擁有產品數量
		int cur = 1000;
		Scanner input = new Scanner(System.in);
		System.out.print("請輸入需要預定的數量:");
		while(input.hasNextInt()){
			int order = input.nextInt();
			//當前擁有的與準備訂購的產品數量之
			if(order>0 && order+cur<=LIMIT){
				System.out.println("你已經成功預定的"+order+"個產品!");
			}else{
				System.out.println("超過限額,預訂失敗!");
			
			}
		}
	}
}
這段代碼看似沒有問題,但是當輸入人員輸入2147483647時,發現提示訂購成功!沒錯,因爲這個到達了int的邊界,2147483647 + 1000 = -xxxxxxxxxx當然小於2000了。!!所以邊界一定要注意!!!

優先使用整形池

ublic static void main(String[] args) {		
		Scanner input = new Scanner(System.in);
		while(input.hasNextInt()){
			int ii = input.nextInt();
			System.out.println("\n===="+ii+" 的相等判斷======");
			//兩個通過new產生的Integer對象
			Integer i =new Integer(ii);
			Integer j = new Integer(ii);
			System.out.println("new產生的對象:" + (i==j));
			
			//基本類型轉爲裝箱類型後比較
			i=ii;
			j=ii;
			System.out.println("基本類型轉換的對象:" + (i==j));
			
			//通過靜態方法生成一個實例
			i=Integer.valueOf(ii);
			j = Integer.valueOf(ii);
			System.out.println("valueOf產生的對象:"  + (i==j));			
		}		
	}
當輸入127時結果爲:
====127 的相等判斷======
new產生的對象:false
基本類型轉換的對象:true
valueOf產生的對象:true

當輸入128時結果爲:
====128 的相等判斷======
new產生的對象:false
基本類型轉換的對象:false
valueOf產生的對象:false

127的包裝類產生的對象是同一個,Integer.valueOf()產生的也是同一個對象,然後128就不同了,這是什麼原因了?
看下Integer.valueOf()的代碼發現問題:
public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
當數字小128&&大於-127時使用的是整數池中的對象,否則返回的是new的新對象。
private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low));
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }
因此在包裝類進行比較時要使用equals()方法而不要使用==,如果需要創建包裝對象時,使用valueOf()能夠節約內存並提高性能。

類、對象和方法

使用構造代碼塊精煉程序

代碼塊分爲4種:
1、普通代碼塊:在方法後面使用{}括起來的代碼片段,不能單獨執行,必須通過方法名調用執行。
2、靜態代碼塊:在類中使用static修飾,並使用{}括起來的代碼片段,用於靜態變量的初始化或對象創建前的環境初始化。
3、同步代碼塊:使用  synchronized關鍵字修飾,並使用{}括起來的代碼片段,他表示同一時間只能有一個線程進入到該方法塊中,是一種多線程保護機制。
4、構造代碼塊:在類中沒有任何的前綴或後綴,並使用{}括起來的代碼片段。
這四種代碼塊是怎執行的?來看下下面的代碼:
public class Client {
	{
		//構造代碼塊
		System.out.println("執行構造代碼塊");
	}
	
	public Client(){
		System.out.println("執行無參構造");
	}
	
	public Client(String _str){
		System.out.println("執行有參構造");
	}
}
當我們new一個Client對象時,發現,會先輸出執行構造代碼塊。由於構造代碼塊無法獨立運行,實際編譯器會把每個構造代碼塊插入到構造函數的最前端,相當於如下代碼:
public class Client {
	
	public Client(){
		System.out.println("執行構造代碼塊");
		System.out.println("執行無參構造");
	}
	
	public Client(String _str){
		System.out.println("執行構造代碼塊");
		System.out.println("執行有參構造");
	}
}

構造代碼塊不是在構造函數之前執行,而是依託於構造函數執行,我們可以把構造代碼塊運用於如下場景中。
1、初始化實例變量(Instance Variable)
如果每個構造函數都要初始化變量,可以通過構造代碼塊實現。當然我們也可以通過定義一個方法,然後通過在構造函數中調用這個方法來實現,但是這樣需要在每個構造函數中都添加方法調用。如果採用構造代碼塊,則不用定義和調用,會由編譯器寫入構造函數中,這纔是解決此類問題的最佳方案。

2、初始化實例環境
一個對象必須在適當的場景下才能存在,如果沒有適當的場景,則就需要在創建對象時創建此場景。例如J2EE中,要產生HTTP Request必須首先建立HTTP Session,在創建HTTP Request 時就可以通過構造代碼塊來檢查HTTP Session是否存在,不存在就創建之。

是用匿名類的構造函數

看如下代碼
public static void main(String[] args) {
		String[] strs = {"aaa","bbb","ccc"};		
		
		List l1 = new ArrayList();
		List l2 = new ArrayList(){};
		List l3 = new ArrayList(){{}};
		System.out.println(l1.getClass());
		System.out.println(l2.getClass());
		System.out.println(l3.getClass());
		System.out.println(l1.getClass() == l2.getClass());
		System.out.println(l2.getClass() == l3.getClass());
		System.out.println(l1.getClass() == l3.getClass());
	}
可以看到輸出結果爲:


這個是什麼原因呢?原來
l2代表的是一個匿名類的聲明與賦值,他定義了一個繼承於ArrayList的匿名類,只是沒有任何的複寫方法而已,其代碼類似於:
public static void main(String[] args) {	
		//定義一個繼承ArrayList的內部類
		class Sub extends ArrayList{			
		}
		//聲明和賦值
		List l2 = new Sub();
	}
l3分開來看也就會明白:
public static void main(String[] args) {	
		//定義一個繼承ArrayList的內部類
		class Sub extends ArrayList{
			{
				//初始化塊
			}
		}
		//聲明和賦值
		List l3 = new Sub();
	}


字符串

正確使用String、StringBuffer、StringBuilder

CharSequence 有三個實現類與字符串有關,分別是String、StringBuffer、StringBuilder。
String類是不可改變的量,一旦創建完以後不能再修改,每次改變String對象值得時候,即使是使用String提供的方法,也是要麼創建一個新的字符串對象,要麼返回自己。例如String a = "abc"  SubString(1)返回"bc"是一個新對象,而subString(0)返回自己。

StringBuffer則是一個可變的字符序列,StringBuilder和StringBuffer基本相同,區別在於StringBuffer是線程安全的,而StringBuilder是線程不安全的,查看一下源碼就可以知道StringBuffer的方法前都有synchronized關鍵字,因此StringBuilder的效率要比StringBuffer高的多。因此String、StringBuffer、StringBuilder的應用場景也會有一些不同:

1、使用String類的場景。
在字符串不常變化的場景中使用String類,例如常量的申聲明、少量的變量運算等。

2、使用StringBuffer的場景。
在頻繁進行字符串運算(如拼接、替換、刪除等)並且運行在多線程環境中,則可以考慮StringBuffer,例如XML解析、HTTP參數解析和封裝等。

3、使用StringBuilder的場景。
在頻繁進行字符串的運算(如拼接、替換、刪除等)並且運行在多線程環境中,則可以考慮StringBuilder,例如SQL語句的拼裝、JSON封裝等。

自由使用字符串拼接

字符串拼接方法共有三種 加號、contact和StringBuilder的append方法。

加號方法相當於如下代碼
str = new StringBuilder(str).append("c").toString();
效率較低,因爲不斷創建StringBuilder對象,並且會執行toString方法返回,因此效率較低。

contact方法最後是return new String(buf, true),所以每次contact都會返回一個新的對象。

StringBuilder的append方法都是在做字符數組的處理,如加長和拷貝,因此append方法效率最高。

但是,“+”加號操作更加符合編程習慣,因此如果不是因爲性能產生瓶頸的話,可以使用+來進行拼接。

數組和集合

不同列表選擇不同的遍歷方式

public static int average(List<Integer> list) {
		int sum = 0;		
		if (list instanceof RandomAccess) {
			//可以隨機存取,則使用下標遍歷
			for (int i = 0, size = list.size(); i < size; i++) {
				sum += list.get(i);
			}
		} else {
			//有序存取,使用foreach方式
			for (int i : list) {
				sum += i;
			}
		}
		// 除以人數,計算平均值
		return sum / list.size();
	}

由於ArrayList實現了RandomAccess的接口,因此用get(i)下標的方式訪問速度將很快,如果使用迭代器將慢很多,而LinkedList 使用迭代器將提高速度。

優雅的使用對集合進行運算(並集、交集、差集)

並集:list1.addAll( list2 )
交集:list1.retainAll( list2 )
差集:  list1.removeAll( list2 )
無重複的並集 :
 //刪除在1中出現的元素
list2.removeAll( list1 )
//把剩餘的list2 添加到list1中
list1.addAll( list2 )

枚舉和註解

推薦使用枚舉定義常量


泛型和反射

Java的泛型是類型擦除的

Java的泛型是在編譯時的,在編以後會擦除Java的泛型類型,相當於如下代碼:
public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("abc");
		String str = list.get(0);
	}
	
	public void doSomething(){
		List list = new ArrayList();
		list.add("abc");
		String str = (String)list.get(0);
	}
理解了這個,就可以解釋爲什麼如下代碼的執行結果爲true
public static void main(String[] args) {
		List<String> ls = new ArrayList<String>();
		List<Integer> li = new ArrayList<Integer>();
		System.out.println(li.getClass() == li.getClass());
	}

建議採用的順序是List<T>、List<?>、List<Object>

List<T>、List<?>、List<Object>這三者都可以容納所有的對象,但使用的順序應該首選List<T>、次之List<?>,最後List<Object>原因如下:

(1)List<T>是確定的某一個類型

List<T>表示的是list列表中的所有元素都是T類型,具體類型在運行時決定;List<?>表示是任意類型,與List<T>類似,而List<Object>則表示集合中所有元素都爲Object類型,因爲Object是所有類型的父類,所以List<Object>也可以容納所有的類類型,從這一字面意義上分析,List<T>更符合習慣:編碼者知道他是某一類型,只是在運行時期才確定而已。

(2)List<T>可以進行讀寫操作

List<T>可以進行add,remove等操作,因爲他的類型是固定的T類型,在編碼期不需要進行任何的轉型操作。

List<?>是隻讀類型的,不能進行增加、修改的操作,因爲編譯器不知道List<?>中容納的是什麼類型的元素,也就無法類型是否安全了,而List<?>讀出的全部都是Object類型,需要主動轉型,所以它經常用於泛型方法的返回值。需要注意List<?>雖然無法添加修改,但是可以做刪除操作。比如remove,clear,因爲刪除動作與泛型類型無關。

List<Object>也可以進行讀寫操作,但是他執行寫入操作時需要向上轉型(Up Cast),在讀取數據後需要向下轉型(Down Cast),而此時已經失去了泛型的意義了。

父類引用指向子類對象,而子類引用不能指向父類對象。

異常

提倡異常封裝

(1)提高系統的友好性
public class Client {
	public static void main(String[] args) {
		try {
			doStuff();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void doStuff() throws Exception {
		InputStream is = new FileInputStream("無效文件.txt");
		/*文件操作*/
	}
	
	public static void doStuff2() throws MyBussinessException{
		try {
			InputStream is = new FileInputStream("無效文件.txt");
		} catch (FileNotFoundException e) {
			//爲方便開發和維護人員而設置的異常信息
			e.printStackTrace();
			//拋出業務異常
			throw new MyBussinessException(e);
		}
		/*文件操作*/
	}
}

class MyBussinessException extends Exception{
	public MyBussinessException(Throwable t){
		super(t);
	}
}


(2)提高系統的可維護性

(3)解決java異常機制自身的缺陷
java中的異常只能拋出一個,但是可以使用封裝來解決
public class Client {
	public static void main(String[] args) {
		try {
			doStuff();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void doStuff() throws MyException {
		List<Throwable> list = new ArrayList<Throwable>();
		// 第一個邏輯片段
		try {
			// Do Something
		} catch (Exception e) {
			
			list.add(e);
		}
		// 第二個邏輯片段
		try {
			// Do Something
		} catch (Exception e) {
			list.add(e);
		}

		if (list.size() > 0) {
			throw new MyException(list);
		}

	}
}

class MyException extends Exception {
	// 容納所有的異常
	private List<Throwable> causes = new ArrayList<Throwable>();

	// 構造函數,傳遞一個異常列表
	public MyException(List<? extends Throwable> _causes) {
		causes.addAll(_causes);
	}

	// 讀取所有的異常
	public List<Throwable> getExceptions() {
		return causes;
	}
}

不要再finally代碼塊中處理返回值

public class Client {
	public static void main(String[] args) {
		try {
			doStuff(-1);
			doStuff(100);
		} catch (Exception e) {
			System.out.println("這裏是永遠都不會到達的");
		}
	}

	public static int doStuff(int _p) throws Exception {
		try {
			if (_p < 0) {
				throw new DataFormatException("數據格式錯誤");
			} else {
				return _p;
			}
		} catch (Exception e) {
			//異常處理
			throw e;
		} finally {
			return -1;
		}
	}
}

上面這段代碼的doStuff(-1) 和doStuff(100)的返回值都是-1,導致這個問題的原因有兩個:
(1)覆蓋了try代碼塊中的return 返回值。
public class Client {
	
	public static void main(String[] args) {
		System.out.println(doStuff());
		System.out.println(doStuff2().getName());
	}
	
	public static int doStuff() {
		int a = 1;
		try {
			return a;
		} catch (Exception e) {

		} finally {
			//重新修改一下返回值
			a = -1;
		}
	
		return 0;
	}
	
	
	public static Person doStuff2() {
		Person person = new Person();
		person.setName("張三");
		try {
			return person;
		} catch (Exception e) {
		} finally {
			//重新修改一下返回值
			person.setName("李四");
		}
		person.setName("王五");
		return person;
	}
}

class Person{
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
doStuff()永遠都返回1,因爲返回的是int類型,java對於int型返回的是值。
而doStuff2()返回的都是"李四",因爲try中返回了person的引用,但是在finally中修改了引用中的值,所以返回的是李四。
(2)屏蔽異常
	public static void doSomething() {
		try {
			//正常拋出異常
			throw new RuntimeException();
		} finally {
			return;
		}
	}
	
	public static void main(String[] args) {
		try {
			doSomething();
		} catch (RuntimeException e) {
			System.out.println("這裏永遠都不會到達!");
		}
	}

上面的finally代碼塊中的return已經告訴jvm,doSomething已經正常執行結束了,也沒有任何異常,所以main中永遠都不會捕獲到異常。

多線程、併發

volatile不能保證數據同步



volatile變量能夠直接寫入到主內存中,保證讀取的數據是最新的,但依然存在問題,看如下代碼
public static void main(String[] args) throws Exception {
		// 理想值,並做爲最大循環次數
		int value = 1000;
		// 循環次數,防止出現無限循環造成死機情況
		int loops = 0;
		//主線程組,用於估計活動線程數
		ThreadGroup tg = Thread.currentThread().getThreadGroup();
		while (loops++ < value) {
			// 共享資源清零
			UnsafeThread ut = new UnsafeThread();
			for (int i = 0; i < value; i++) {
				new Thread(ut).start();
			}
			// 先等15毫秒,等待活動線程數量成爲1
			do {
				Thread.sleep(15);
			} while (tg.activeCount() != 1);
			// 檢查實際值與理論值是否一致
			if (ut.getCount() != value) {			
				// 出現線程不安全的情況
				System.out.println("循環到第 " + loops + " 遍,出現線程不安全情況");
				System.out.println("此時,count=" + ut.getCount());
				System.exit(0);
			}
		}
		
		System.out.println("循環結束");
	}
}

class UnsafeThread implements Runnable {
	// 共享資源
	private volatile int count = 0;

	@Override
   public void run() {
		// 爲了增加CPU的繁忙程度
		for (int i = 0; i < 1000; i++) {
			Math.hypot(Math.pow(92456789, i), Math.cos(i));
		}
		// 自增運算
		count++;
	}

	public int getCount() {
		return count;
	}
}
最終的輸出結果

第7遍出現了線程不安全,當然這個數量是隨機的。

異步運算考慮使用Callable接口

	public static void main(String[] args) throws Exception {
		//生成一個單線程的異步執行器
		ExecutorService es = Executors.newSingleThreadExecutor();
		//線程執行後的期望值
		Future<Integer> future = es.submit(new TaxCalculator(100));
		while(!future.isDone()){
			//還沒有運算完成,等待10毫秒
			TimeUnit.MILLISECONDS.sleep(200);
			//輸出進度符號
			System.out.print("#");
		}
		System.out.println("\n計算完成,稅金是:"+ future.get() + " 元");	
		//關閉異步執行器
		es.shutdown();
	}
}

//稅款計算器
class TaxCalculator implements Callable<Integer> {
	//本金
	private int seedMoney;
	//接收主線程提供的參數
	public TaxCalculator(int _seedMoney) {
		seedMoney = _seedMoney;
	}
	@Override
	public Integer call() throws Exception {
		//複雜計算,運行一次需要10秒
		TimeUnit.MILLISECONDS.sleep(10000);
		return seedMoney /10;
	}
}

此類異步計算的好處是:
(1)儘可能多的佔用系統資源,提供快速運算
(2)可以監控線程執行的情況,比如是否執行完畢、是否有返回值,是否有異常
(3)可以爲用戶提供更友好的支持,比如例子中的運算進度

使用CountDownLatch協調子線程

思考這樣一個案例:百米賽跑,多個參加賽跑的人員在聽到發令槍響後,開始跑步,到達終點後結束計時,然後統計平均成績。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Client {
	static class Runner implements Callable<Integer> {
		//開始信號
		private CountDownLatch begin;
		//結束信號
		private CountDownLatch end;

		public Runner(CountDownLatch _begin, CountDownLatch _end) {
			begin = _begin;
			end = _end;
		}

		@Override
		public Integer call() throws Exception {
			// 跑步的成績
			int score = new Random().nextInt(25);
			// 等待發令槍響起
			begin.await();
			// 跑步中……
			TimeUnit.MILLISECONDS.sleep(score);
			// 跑步者已經跑完全程
			end.countDown();
			return score;
		}
	}

	public static void main(String[] args) throws Exception {
		//參加賽跑人數
		int num = 10;
		// 發令槍只響一次
		CountDownLatch begin = new CountDownLatch(1);
		// 參與跑步有多個
		CountDownLatch end = new CountDownLatch(num);
		// 每個跑步者一個跑道
		ExecutorService es = Executors.newFixedThreadPool(num);
		// 記錄比賽成績
		List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
		// 跑步者就位,所有線程處於等待狀態
		for (int i = 0; i < num; i++) {
			futures.add(es.submit(new Runner(begin, end)));
		}
		// 發令槍響,跑步者開始跑步
		begin.countDown();
		// 等待所有跑步者跑完全程
		end.await();
		int count = 0;
		// 統計總分
		for (Future<Integer> f : futures) {
			count += f.get();
		}
		System.out.println("平均分數爲:" + count / num);
	}
}
CountDownLatch是一個倒數的同步計數器,每個線程在執行完畢後執行countDown,表示自己運行結束,這對於多個子任務的計算特別有效。

CyclicBarrier讓多線程齊步走

思考這樣一個案例:兩個工人從兩段挖據隧道,各自獨立奮戰,中間不溝通,如果兩人在匯合點處碰頭了,則表明隧道已經挖通。這描繪的是在多線程編程中,兩個線程獨立運行,在沒有線程間通信的情況下,如何解決兩個線程匯合在同一點的問題。
public class Client {
	static class Worker implements Runnable {
		// 關卡
		private CyclicBarrier cb;

		public Worker(CyclicBarrier _cb) {
			cb = _cb;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(new Random().nextInt(1000));
				System.out.println(Thread.currentThread().getName() + "-到達匯合點");
				// 到達匯合點
				cb.await();
			} catch (Exception e) {
				// 異常處理
			}

		}
	}

	public static void main(String[] args) throws Exception {
		// 設置彙集數量,以及彙集完成後的任務
		CyclicBarrier cb = new CyclicBarrier(2, new Runnable() {
			public void run() {
				System.out.println("隧道已經打通!");
			}
		});
		// 工人1挖隧道
		new Thread(new Worker(cb), "工人1").start();
		// 工人2挖隧道
		new Thread(new Worker(cb), "工人2").start();
	}
}

 CyclicBarrier可以讓全部線程處於等待狀態(阻塞),然後在滿足條件的情況下全部執行,這就好比是一條起跑線,不管是如何達到起跑線的,只要到達這條起跑線就必須等待其他人員,在人員到齊後再各奔東西,CyclicBarrier關注的是匯合點的信息,而不在乎之前或之後如何處理。

性能和效率

提升java新能的基本方法

(1)儘可能將變量、方法生命爲final static類型
加入要將阿拉伯數字轉換爲中文數字
public static String toChineseNum(int num){
		//中文數字
		String[] cns = {"零","壹","貳","叄","肆","伍","陸","柒","捌","玖"};
		return cns[num];
	}
每次調用該方法時都會重新生成一個cns數組,注意該數組不會改變,屬於不變數組,在這種情況下把它聲明爲類變量,並且加上final static修飾會更合適,在類加載後就生成了該數組,每次方法調用則不再重新生成數組對象了,這有助於提高系統新能,代碼如下:
	//中文數字
	final static String[] cns = {"零","壹","貳","叄","肆","伍","陸","柒","捌","玖"};
	
	public String toChineseNum(int num){
		return cns[num];
	}

 (2)縮小變量的作用範圍
變量能在方法內定義就定義在方法內,內在循環內定義就在循環內,能在try...catch中就在代碼塊中,目的是加快GC的回收。
(3)頻繁的字符串操作使用StringBuffer和StringBuilder
 (4)使用非線性檢索
如果在ArrayList中存儲了大量數據,使用indexOf查找會比java.utils.Collections.binarySearch的效率低很多,但是需要注意的是,binarySearch搜索的數據必須是經過排序,否則準確性不可靠。





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