1. 程式人生 > >java只使用try和finally不使用catch的原因和場景

java只使用try和finally不使用catch的原因和場景

JDK併發工具包中,很多異常處理都使用瞭如下的結構,如AbstractExecutorService,即只有try和finally沒有catch。

class X 
{
    private final ReentrantLock lock = new ReentrantLock();
    // ...
 
    public void m()
    {
	lock.lock();  // block until condition holds
	try 
	{
		// ... method body
	} finally
	{
		lock.unlock()
	}
     }
}

為什麼要使用這種結構?有什麼好處呢?先看下面的程式碼

 public void testTryAndFinally(String name)
 {
        try
        {
            name.length();// NullPointerException
        }
        finally
        {
            System.out.println("aa");
        }
 }

傳遞null呼叫該方法的執行結果是:在控制檯列印aa,並丟擲NullPointerException。即程式的執行流程是先執行try塊,出現異常後執行finally塊,最後向呼叫者丟擲try中的異常。這種執行結果是很正常的,因為沒有catch異常處理器,所有該方法只能將產生的異常向外拋;因為有finally,所以會在方法返回丟擲異常之前,先執行finally程式碼塊中的清理工作。

這種做法的好處是什麼呢?對於testTryAndFinally來說,它做了自己必須要做的事(finally),並向外丟擲自己無法處理的異常;對於呼叫者來說,能夠感知出現的異常,並可以按照需要進行處理。也就是說這種結構實現了職責的分離,實現了異常處理(throw)與異常清理(finally)的解耦,讓不同的方法專注於自己應該做的事。

那什麼時候使用try-finally,什麼時候使用try-catch-finally呢?很顯然這取決於方法本身是否能夠處理try中出現的異常。如果自己可以處理,那麼直接catch住,不用拋給方法的呼叫者;如果自己不知道怎麼處理,就應該將異常向外拋,能夠讓呼叫者知道發生了異常。即在方法的簽名中宣告throws可能出現而自己又無法處理的異常,但是在方法內部做自己應該的事情。

這可以參考ExecutorService.invokeAny()的方法簽名

 <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;