This page looks plain and unstyled because you're using a non-standard compliant browser. To see it in its best form, please visit upgrade to a browser that supports web standards. It's free and painless.

iT老爸碎碎唸 會員登入 會員註冊

喝咖啡已經變成了每天的習慣,但每天去外面買好貴,我的星巴克隨行卡有13點紅利,你就知道我花了多少錢在星巴克上,還不算其它像是全家或是-4買的咖啡。可是自己家裡搞台傳統的咖啡機也蠻麻煩的,主要是喝的量沒有那麼大,咖啡豆放在機子裡,太久味道也會變。如果要現磨現泡,又太搞工了(台語),沒那種美國時間。

自從小舅子他家看過Nespresso這麼方便的咖啡機後,加上看到網路上網友分享的Nespresso 咖啡機,心裡就很癢,網路上查了一下資料,雖然台灣也有代理 Nespresso (信義誠品美食街那有攤位),不過機型不多, 網路上大家都還是去日本或是香港買。剛好這週出差到深圳,趁週末時殺到香港中環 IFC Mall裡面的 Nespresso 專賣站,狠下心來,給它敗下去了!

妹子生日送把拔的禮物是什麼呢?(咦)…

IMG_3157

哇!! 是大銘鼎鼎的 Nespresso 咖啡機耶!而且是台灣買不到的 PIXIE 機型喔!!

IMG_3158

來看看它的廬山真面目

IMG_3159

登登!! 是漂亮的橘色!真是美啊~~

IMG_3160

正面照來一張
IMG_3161

上空照(哈~)
IMG_3162

把水箱加水,準備來煮咖啡囉~
IMG_3167

把水箱掛上
IMG_3168

全機只有三個按鈕,後面中間的電源開關(黑),另外兩個發亮的是大杯、小杯按鈕
IMG_3170

前面也會發光喔~~
IMG_3172

隨機附贈的全16種口味的咖啡膠囊, 目前市面上買得到的就這16種啦! 不過聽說還有限量的口味
IMG_3163

喝哪一種好呢?
IMG_3175

先最咖啡因最低的 Decaffeinato (Fruity and delicate Intensity 2)
IMG_3178

打開彈匣
IMG_3180

投彈
IMG_3183

膠囊在彈匣中了

IMG_3185

看了說明圖示,是要泡小杯的,所以就按下小杯的按鈕....
IMG_3186

泡好了耶! 呃...水量好像不太對,看起來不是 40ml, 而是更小的25ml, 而且好酸啊~~
IMG_3189

退彈後,用過的膠囊會掉到下方的盒子裡,抽出來就可以看到了
IMG_3196

把膠囊拿出來瞧瞧(好燙),正面被搓了好多個小孔(5x5)

IMG_3192

背後只有三個孔
IMG_3194

真的是很方便,不過要先16種口味全試過,才知道自己偏好哪幾種啊!因為去店裡買膠囊,每種口味一條就是10個,買到不喜歡喝的,就很痛苦啦!!

另外還有一件比較麻煩的事就是電壓的問題,香港買的話,電壓是220V;日本買的話,電壓是100V。網路上分享的說是日本的好像直接用台灣的110V也還好,但香港的220V就要想辦法啦...總不能在冷氣機下泡咖啡吧 :P

有朋友是請水電再從總開關那裡拉另一個220的插座,看來這是唯一的方法了。 

來看看操作的影片吧!

 

2011 11 11 09 06 49 pm

終於!! 拿到了六萬塊的得獎資格!不過這屆大家都好強啊,好多人分享了不少比我更好的知識和文章,看來重金之下,真的能引出勇夫們來做很棒的分享。也因為有這樣的動力,才能完成整個 Java SE 7 新功能的研究。

最後三個主題, NIO2,Concurrency Updates(JSR-166y) 還有  Da Vinci Machine (JSR-292),都還沒完整深入的介紹,雖然已經滿三十篇文章了,但在比賽結束前的這幾天,我會找時間把它們給補上,讓這個系例的文章能完整!

最後,請大家期待最新改版的 Java 全方位學習 (Java SE 7增修版)~ 不過要等到明年了吧 :P

Java SE 在 1.4 時加入了 NIO (New I/O) 的新API,事隔多年後,在 Java SE 7 裡加入了第二代 NIO - NIO2 (JSR-203)。

第二代的 NIO2 新增了三個主要的功能:

1. File System API 的增強

2. Asynchronous IO

3. 其它

[b]**********[/b]

File System API 從 Java 1.0 開始,十幾年來一點都沒改進,而且功能少的可以!這次的 NIO2 總算增強、補齊了這塊拼圖。主要的改進有:

[b]* 不同平台的完整支援[/b]

早期的 File System API 不論是對 Windows 或是 Linux 平台的檔案系統都不友善,你只能用它來做基本的檔案存取,沒有辦法做進階地處理,像是檔案的權限等…。而新的 File System API 則是依各個平台的不同,而有不同的實作,能完整地支援各個平台其檔案系統的特色。而且程式開發者可以透過標準且統一的介面,來操作檔案系統,不需要擔心底層到底是什麼平台。

[b]* 完整的檔案操作[/b]

舊的 File System API 只能簡單地刪除檔案,想要拷備或是搬移檔案,都得自己處理。而新的 File System API 提供了完整的 拷備(copy)、搬移(move)和刪除(delete)的方法,讓你能輕易地操作這個檔案。

[b]* 檔案捷徑(symbolic link)的支援[/b]

檔案捷徑不是一個完整的檔案,而是某一個實體檔案的替身,因為它不是一個完整的檔案,所以舊的 File System API 並沒有辦法正確的處理它,現在透過新的 File System API 就沒這個困擾了。

[b]* 完整檔案屬性的存取[/b]

一個檔案有許多不同的屬性(attribute),例如是誰建立了這個檔案?哪些人有這個檔案的存取權限等…而不同的平台上會有一些該平台特別的屬性,像是隱藏檔案的屬性,在Windows 和 Linux 平台上就不一樣。新的 File System API 提供了完整存取不同平台檔案屬性的方法。

[b]* 其它 [/b]

其它新的 File System API 功能有,像是能走訪整個目錄(包含目錄下的檔案及其子目錄)的 File tree walk API;還有檔案系統的監看API (WatchService),你可以用它來監看檔案系統裡有哪些檔案被新增、修改或刪除了。還有像是檔案過濾器(Glob),你可以在讀取檔案列表時,設定你要的過濾條件,只列出你想要的檔案,例如所有的 .java 檔案或是 .jpg 案。新的 File System API 真的是大大補完了原本所欠缺的功能,應該能滿足你絕大部份的檔案操作需求。

 

[b]**********[/b]

在 Java SE 7之前,所有的 I/O 操作都是同步的(synchronous),所謂的同步操作是指檔案存取時,程式必需等在那裡,一直到檔案資料都讀完/存完後,才會執行下一行程式碼。而 asynchronous 是非同步的意思,跟同步最大的差別在於,例如我要把一張很大的圖片存起來,我只要執行了存檔的程式碼後,程式就可以去執行別的程式碼,當存好後,Java 會自動通知你存好了,然後你再回來處理下一個動作。非同步 I/O 對於大量的檔案處理是巨大的檔案處理時,非常地有效率,程式不會因為長時間的檔案存取,而整個程式像當機一樣卡在那裡。

 

[b]**********[/b]

其它新的 NIO2 的功能還有支援 GB 級的存取緩衝區(buffer)、網路廣播(Multicasting)等。

 

NIO2 的東西實在太多了,在這裡只能給大家一個簡單的介紹,有興趣的讀者可以進一步查閱 NIO2 的相關文件,筆者也會慢慢地把相關的範例程式給補上,敬請期待~

JDBC 是 Java Database Connectivity 的縮寫,它是讓你的 Java 程式跟資料庫溝通的一組 API,透過這個統一的 API 介面,你可以連接各式不同的資料庫系統,對資料庫裡的資料做新增、刪除、修改、查詢等動作。

Java SE 7 裡支援 JDBC 4.1 的規格,跟JDBC 4.0 版比較起來,有兩個主要的改進。第一個就是前面也介紹過的 try-with-resource 語法的支援,第二就是新的 RowSetFactory 這個新的 API。

前面在介紹(第21到24天) try-with-resource 語法時,我們只介紹了 java.io 的部份(還有自創的類別),而 JDBC 4.1 裡的 [b]java.sql.Connection[\b]、[b]java.sql.ResultSet[\b]、[b]java.sql.Statement[\b] 這三個介面(interface) 也都繼承了 [b]java.lang.AutoCloseable[/b] 介面,所以當你用到實作這三個介面的 SQL 物件時,也能將宣告這些物件的陳述式寫在 try-with-resource 的陳述式裡,這樣就不怕忘記關閉資料庫資源了。

範例:

[code]

package idv.jacky.ironman4.day29;

 

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

 

public class Day29Example1 {

 

public static void sampleQueryProcessing(Connection sampleCon) throws SQLException {

String sampleQuery = "select * from STUDENT";

try (Statement sampleStmt = sampleCon.createStatement()) {

ResultSet rs = sampleStmt.executeQuery(sampleQuery);

while (rs.next()) {

String studentName = rs.getString("NAME");

String studentAge = rs.getString("AGE");

System.out.printf(" NAME: %S, AGE: %s%n", studentName, studentAge);

}

}

}

}

[/code]

 

RowSetFactory 是一組新的 API,早期沒有 RowSetFactory 時,我們必需把實作 RowSet 的類刟名稱寫死在程式碼裡(因為各家的資料庫都會提供自己的類別)。現在則可以透過 RowSetFactory 這個 API 來統一產生 RowSet 物件,而不需要去管到底是什麼類別實作的。這樣一來,你的程式就更有彈性,只需要更改系統的參數,例如在執行 Java 程式前,用命令列參數來設定:

[b]java -Djavax.sql.rowset.RowSetFactory=com.sun.rowset.RowSetFactoryImpl[/b]

如此你就可以隨時抽換使用不同的實作類別,相當的方便。而當你不指定時,Java 會預設使用 com.sun.rowset.RowSetFactoryImpl。

範例:

[code]

package idv.jacky.ironman4.day29;

 

import java.sql.SQLException;

 

import javax.sql.rowset.JdbcRowSet;

import javax.sql.rowset.RowSetFactory;

import javax.sql.rowset.RowSetProvider;

 

public class Day29Example2 {

public void sampleMethod(String sampleUserName, String samplePassword) throws SQLException {

RowSetFactory sampleRowSetFactory = null;

JdbcRowSet sampleRowSet = null;

try {

sampleRowSetFactory = RowSetProvider.newFactory();

sampleRowSet = sampleRowSetFactory.createJdbcRowSet();

sampleRowSet.setUrl("jdbc:sampleDriver:sampleAttribute");

sampleRowSet.setUsername(sampleUserName);

sampleRowSet.setPassword(samplePassword);

sampleRowSet.setCommand("select * from STUDENT");

sampleRowSet.execute();

} catch(Exception e) {

e.printStackTrace();

}

}

}

[/code]

 

 

介紹完了 Project Coin,接著我們來看看 Java SE 7 裡其它的新功能。首先我們要看的新功能是 Unicode 6.0 的支援!

Unicode 是一種文字編碼的方式,什麼是文字編碼?簡單的說就是把我們用的文字,例如中文、日文、英文等…轉換成電腦看得種的符號,基本上電腦只認得 0 和 1,早期美國發明電腦時,電腦只認得英文字母和一些基本的符號,後來隨著作業系統的發展,漸漸地也能顯示各種不同語言的文字,但電腦還是只認得 0 和 1 啊!所以我們要將一個個的文字對應到 0 和 1 的表示方式,而這種對應我們就稱之為文字編碼,為了人類容易閱讀,我們將 0 和 1 的數值用 16 進位的方式來表示。

不同的語言,不同人整理文字順序的方式,也就產生了各式各樣不同的編碼方式,像英文常用的是 ISO 8859-1,繁體中文常用的是 Big5,簡體中文常用的是 GB2312等…。可是不同的文字編碼所造成的困擾是,當我們在交換文書檔案時,如果用錯了編碼方式,那麼那份文件輕則內容變成亂碼,重則可能會損毀檔案無法開敵。所以一定要有一種統一的編碼方式讓大家可以遵循,以降低不同語言的系統在交換檔案時的困擾。因此就誕生了 Unicode!

Unicode 從1987年發想,到1991年10月,Unicode 1.0 版公佈,到1993 年6月的 1.1 版開始支援中文字。一直到去年(2010)10月的 6.0 版,已經支援世界上大部份語言的文字,共109,449個字。Unicode 的特點就是,不管什麼語言的文字和符號,都有一個唯一的編號,所以不會重覆。只要是原生支援 Uicode 的系統,像是Mac OS X、iPhone,就能直接顯示各式個樣不同的文字,不需另外安裝 patch。

Unicode 6.0 除了加入了一些印度、中東的方言外,還加入了撲克牌(playing card)、交通(transport)、地圖(map)符號,而最精彩的是加入了表情符號(emoticon)和繪文字(emoji)

表情符號就是我們平常在 MSN 裡使用的那些基本的表情:

http://www.unicode.org/charts/PDF/U1F600.pdf

2011 11 08 09 59 05 pm

 

繪文字是日本電信商們專有的圖示符號,可以表現的表情圖示比表情符號還多,日系的手機都有支援,其它的手機,可以透過特定的方法,來開啟它的輸入法,例如iPhone。

http://www.unicode.org/charts/PDF/U1F300.pdf

2011 11 08 09 58 45 pm

真的很難想像幾年後的 Unicode 7.0 要加入什麼文字!火星文?克林貢文? 呵呵….

Project Coin 裡的最後一個新功能叫 簡化變動參數方法的呼叫 (Simplified varargs method invocation),從名稱上看不出個所以然,它又跟昨天提到的 Heap Pollution 有什麼關係呢?

變動參數也是在 Java 1.5 中新增的功能,而它特殊的 [b]…[/b] 語法,實際上就是轉換成陣列使用,再配合泛型的不特定型別的宣告,在某些情況下,也是會造成 Heap Pollution。請看下面這個例子:

[code]

package idv.jacky.ironman4.day26;

 

public class Day26Example1 {

public static void main(String[] args) {

System.out.printf("Sum = %d%n", Util2.sum(args));

}

}

[/code]

 

[code]

package idv.jacky.ironman4.day26;

 

public class Util2 {

public static <T> int sum(T...numbers) {

int sum = 0;

for(T i : numbers)

sum += ((Number)i).intValue();

return sum;

}

}

[/code]

 

我們把 sum 方法的參數,改成使用 varargs 的語法,我們大譫地假設呼叫 sum 方法的人,所傳進來的物件,都是 Number 類別的物件,或其子類別物件,例如 Integer, Double等…然後呼叫 Number 的 intValue 方法,再把它們通通加起來。

問題就出在我們的「大膽假設」上,[b]T[/b] 指的是不定型別,也就是說我們不把 sum 方法的參數型別給寫死,完全在執行時期時,才去動態的傳入。問題就在於如果傳入的不是 Number 或其子類別物件的話,那就會遇到 Heap Pollution 的問題。

上面的範例,在編譯時加上 [b]XIint:unchecked[/b] 的參數的話,就會看到 Java 所丟出來的警告訊息:

[b]

/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/bin/javac -Xlint:unchecked Util2.java

Util2.java:6: warning: [unchecked] Possible heap pollution from parameterized vararg type T

public static <T> int sum(T...numbers) {

^  where T is a type-variable:

T extends Object declared in method <T>sum(T...)

1 warning

[b]

這次 Java 就很明確的告訴你,Util2.sum 方法可能會有 Heap Pollution 的問題!

如果我們真的非常肯定,Util2.sum 方法只有我們自己會用,且一定會傳入正確型別的變數進去,例如:

[code]

package idv.jacky.ironman4.day26;
import java.util.ArrayList;import java.util.List;

public class Day26Example2 { public static void main(String[] args) { List<Double> numbers = new ArrayList<>(); for(String str : args) numbers.add(Double.parseDouble(str)); System.out.printf("Sum = %d%n", Util2.sum(numbers)); }}

 

[/code]

我們不想在編譯時看到剛剛的警告訊息的話,在 Java SE 7 之前,我們可以用一個 Annotation - [b]@SuppressWarings("unchecked")[/b] 來壓制這個警告。在 Java SE 7 裡則多了一個新的 Annotation 特別設計給 varrags 用的,就是 [b]@SafeVarargs[/b]。效果跟 [b]@SuppressWarings("unchecked")[/b] 一樣,我們在 sum 方法宣告的的上一行加上 [b]@SafeVarargs[/b],

[code]

@SafeVarargs

//@SuppressWarnings("unchecked")

public static <T> int sum(T...numbers) {

[/code]

 

這樣編譯器就會認為你已經知道 Heap Pollution 的風險,而略過對這個方法提出警告。

不過,這只是把警告給壓制下來,實際上 Heap Pollution 的問題並沒有消失,所以在呼叫這類的方法時,還是得特別地注意。

自從敗家天王 486 在部落格上介紹 Philips 氣炸鍋 之後,它應該是2011年末詢問度最高的家電商品吧!上週三團購一開始,短短的三小時內,800台搶購一!是誰說景氣不好的 :P

朋友們看我在臉書上 po 了一堆氣炸鍋相關的訊息,都保持著高度的興趣!不過團購一下就結束了,有位朋友聽說百貨公司的專櫃也已經開賣了,趁著週年慶有優惠,一組人馬殺到百貨公司,搶走了最後一台!哈哈哈~~

因為週年慶的關係,價格不會比團購還差,再加上團購要等到12月才會到貨,拿現貨的感覺爽度有差!可惜團購不能退貨,不然我們也在百貨公司買了!百貨公司訂價8450, 特價 7988, 遠百寶慶店週年慶, 滿五千送500, 現抵! 再送西提牛排卷一張! 但特價只到今晚(11/06)! :P

朋友阿莎力的刷卡帶走後,我們迫不及待地衝到朋友家去試用囉~~

Philips 氣炸鍋好大一個啊~

P1020117

 

在百貨公司搶到的最後一組,當然要小心點拆.

P1020118

P1020119

 

露出面貌了~

P1020120

 

初次見面,請多指教~

P1020121

 

主角登場~~

P1020122

 

真的好大一鍋啊!!

P1020123

 

拿底鍋來跟我們家妹妹的頭比較一下

P1020124

 

氣炸鍋本身只有兩個旋鈕,上方的是溫度旋紐,下方的是時間旋鈕。

P1020126

 

第一次就先找最容易的下手吧,說明書上說可以薯條和雞塊一起炸,用隨機附的檔板,把兩種食材隔開

P1020128

 

機身上有貼了六種基本食材炸的溫度和時間,薯條和雞塊一起炸的話是180度, 18~25分鐘...給它開下去。機身面版上還有兩個燈號,上面的綠燈是電源燈,下面的橘燈是加熱燈。

P1020130

 

在加熱的同時,來看看附贈的食譜吧~ 有中文的一本(10道菜),英文的一本(沒數幾道菜,但比中文的厚上一倍)。

P1020131

 

炸魚

P1020132

 

鹽酥雞好像不錯,下次來試試

P1020133

 

說明書上說,量多的話, 炸到一半可以拿出來拌一下,受熱均勻一點。直接拉出來就好了! 沒有什麼暫停鍵之類的~

P1020135

P1020136

 

噹! 炸好囉~~

P1020138

 

顏色看起來炸的不錯喔~~

P1020139

 

真的把一堆油都炸出來了耶!

P1020140

 

豪邁地整個倒在盤子上

P1020142

 

失敗!炸太久了,已經乾掉了~~ 後來反覆試了三遍,薯條其實十分鐘就夠了,而且我們也偷懶沒拿出來攪拌 :P 雞塊也15分鐘就很夠了~

P1020143

 

這次換炸牛排!!

P1020144

 

牛排就很成功!又嫩又好吃~~(頂好買的美國牛肉)

P1020146

 

換筊白筍

P1020148

 

沒有油脂的食材,其實就像烤的一樣,不要看表皮焦焦的,咬一口下去,汁很多呢! 好吃~

P1020149

 

結論:

連贈品的價值加加起來,七千左右的價格來說,算是蠻超值的!而且真的很好用,只需要調好度、設定時間,就搞定了!雖然火候上還是需要經驗值來拿捏一下,但整體來說算是簡單易用,又能做出好吃料理的好家電!

我們一群人四大兩小,兩個小時就喀掉了一大袋的薯條(剛買時,還以為買太多了,看來下次要去 Costco 買了),一袋雞塊、一小份牛排和筊白筍,真的是很有效率的家電!趁百貨公司週年慶有特價,想買的人快去敗家吧,你不會失望的!

 

Java 從1.5 版加入泛型的功能後,一直有個潛在的使用問題,那就是 [b]Heap Pollution[/b]。

我們先來看看下面這個範例:

[code]

package idv.jacky.ironman4.day25;
import java.util.Arrays;import java.util.List;
public class Day25Example1 {
public static void main(String[] args) { List numbers = Arrays.asList(args); System.out.printf("Sum = %d%n", Util.sum(numbers)); }}

[/code]

[code]

package idv.jacky.ironman4.day25;

 

import java.util.List;

 

public class Util {

public static int sum(List<Integer> ls) {

int sum = 0;

for(Integer i : ls)

sum += i;

return sum;

}

}

[/code]

你應該一眼就能看出來上面這個範例程式哪兒出了問題,問題就在於 Util.sum 方法只能接受 List<Integer> 的參數,但我們卻在第10行把一個 List 物件傳進去,而 List 物件則是透過 Arrays.asList 的方法,把 main 方法 的參數 args 字串陣列,轉成 List 物件。

程式在編譯時,Java 編譯器會丟出一個警告訊息:

[b]

javac Day25Example1.java

Note: Day25Example1.java uses unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

[/b]

意思是說程式裡有個未受檢查或不安全的程式碼,建議你在編譯時加上 [b]-Xlint:unchecked[/b] 來看詳細的資料。

[b]

javac -Xlint:unchecked Day25Example1.java

Day25Example1.java:10: warning: [unchecked] unchecked conversion

found   : java.util.List

required: java.util.List<java.lang.Integer>

System.out.printf("Sum = %d%n", sum(numbers));

^

1 warning

[/b]

Java 編譯器有檢查到,sum 方法的參數型別應該是 List<Integer>,但你給的是 List。

程式依然可以編譯成功,但執行時就會有例外出現:

2011 11 05 06 48 34 pm

所以 [b]Heap Pollution[/b] 指的就是將一個未定參數型別的 Collection 物件,指定給一個有指定參數型別的 Collection 物件後,所隱藏的潛在問題。

既然知道這樣會有潛在的危險,因為我們不能確定所有呼叫 sum 方法的人,傳進來的都是 List<Integer> 物件,那為什麼不能在編譯時就給個錯誤,不讓程式編譯成功呢?主要的原因是,Java 無法知道誰會呼叫 sum 方法。我們的範例很簡單,你一下就看出來哪邊有問題,但今天如果 Util 類別是要給別人使用的,你並沒有辦法知道別人會不會正確的使用它。而 Java 程式裡變數的型別檢查,都是在編譯時其做的,在執行時期,是完全沒有型別資料的。一直要到發生例外時,我們才知道問題出在哪。

所以當你在編譯 Java 程式碼時,如果要你使用 [b]-Xlint:unchecked[/b] 來察看詳細的警告資料時,請花點時間看一下,修正可能會發生錯的程式碼,這樣能確保你的程式能更安全、穩固地執行。

https://sites.google.com/a/prever.co.kr/josh/java/whatisheappollution

除了後開先關的規則之外,在使用 try-with-resource 語法時,還有一點要注意的,就是 [b]例外的壓制(Exception Suppressed)[/b]。

我們已經知道 close 方法有可能會丟出例外,那如果在 try 區塊裡也不小心丟出例外時,Java 會怎麼處理這兩個例外呢?我們先宣設計另一個可關閉的類別:

[code]

package idv.jacky.ironman4.day24;

 

public class MyResource3 implements AutoCloseable{

 

@Override

public void close() throws Exception {

System.out.println("Close resource 3.");

throw new Exception("MyResource3 Close Exception");

}

 

}

[/code]

在 MyResource3.close 方法裡,我們它丟出一個 Exception 例外。然後我們這樣來測試一下:

[code]

package idv.jacky.ironman4.day24;

 

public class Day24Example1 {

 

public static void main(String[] args) {

try {

foo();

} catch (Exception e) {

System.out.println(e);

}

}

public static void foo() throws Exception {

try (MyResource3 r3 = new MyResource3()) {

System.out.println("Do something...");

throw new Exception("Something error!");

}

}

 

}

[/code]

我們有個 foo 的方法,方法裡會產生 MyResource3 的物件,然後在 foo 方法的 try 區塊裡也丟出一個例外。而呼叫 foo 的 main 方法裡,用個try-catch 區塊來補捉 foo 方法所丟出來的例外。最後你猜猜在程式的第9行會印出什麼?

2011 11 03 10 00 34 pm

很意外嗎? 在 close 方法裡的 "MyResource3 Close Exception" 跑哪去?因為 catch 陳述式裡一次只能補捉一個例外,try 區塊中已經丟出一個例外了,Java 會優先補捉它。一般來說 try 區塊的程式碼是主要的,所以有例外發生時,通常會需要特地去處理;而 close 所丟出來的例外是次要的,你可以依情況進一步處理。

close 方法丟出的例外並沒有不見,只是被壓制(Suppressed)了,Java SE 7 的 Throwable 介面裡(所有的 Exception 都實作這個介面)有個 [b]getSuppressed[/b] 的方法,它就是用來取出所有被壓制的例外!我們來改寫一下剛剛的例子:

[code]

package idv.jacky.ironman4.day24;

 

public class Day24Example2 {

 

public static void main(String[] args) {

try {

foo();

} catch (Exception e) {

System.out.println(e);

System.out.println("Suppressed Exceptions: ");

Throwable[] th = e.getSuppressed();

for(Throwable t : th)

System.out.println(t);

}

}

public static void foo() throws Exception {

try (MyResource3 r3 = new MyResource3()) {

System.out.println("Do something...");

throw new Exception("Something error!");

}

}

 

}

[/code]

 

我們在 main 方法裡補捉到例外後(第21行丟出來的),呼叫 getSuppressed 方法來取得被壓制的例外,也就是 MyResource3 裡第8行所丟出來的。getSuppressed 會回傳一個 Throwable 物件陣列,因為 try-with-resource 陳述式裡可以有多個可關閉物件的宣告。然後我們再用個 for 迴圈把這裡被壓制的例外也印出來(本範例中只有一個),程式執行結果如下:

2011 11 03 10 15 06 pm

現在你知道 MyResource3.close 方法丟出的例外跑哪去了吧!

在 Java SE 7 裡,還有另一個繼承 java.lang.AutoCloseable 的介面,所有 java.io package 裡的資料串流類別其實是實作這個介面,反而不是直接實作 java.lang.AutoCloseable 介面。這個介面就是 java.io.Closeable!

就兩個介面的宣告上來說,幾乎沒什麼差別,唯一的差別就是 java.io.Closeable 的 close 方法宣告會丟出 IOException 例外,而 java.lang.AutoCloseable 的 close 方法宣告會丟出 Exception 例外。當然每個資料串流類別實作 close 方法的細節不同,介面的好處就是,不管底層類別如何實作,介面的方法呼叫還是一樣。

我們再來設計一個實作 java.io.Closeable 介面的類別:

[code]

package idv.jacky.ironman4.day23;

import java.io.Closeable;import java.io.IOException;
public class MyResource2 implements Closeable{
public void close() throws IOException { System.out.println("Close resource 2."); }
}

[/code]

MyResource2 類別跟昨天的 MyResource1 類別差不多,只差實作的介面不同。

有時因為程式的需要,我們可以在 try-with-resource 陳述式裡,宣告多個可關閉的物件嗎?答案當然是可以的,你就把 try-with-resource 的小括號當成一般的程式碼區塊,把要宣告的可關閉物件寫在裡面,每個物件的宣告用分號(;)隔開,例如:

[code]

package idv.jacky.ironman4.day23;

 

import idv.jacky.ironman4.day22.MyResource1;

 

public class Day23Example {

 

public static void main (String[] args) throws Exception {

try (MyResource1 r1 = new MyResource1();

MyResource2 r2 = new MyResource2()) {

System.out.println("Do something...");

}

}

}

[/code]

現在要考考大家,這個範例程式會印出三個字串,請問順序會如何?毫無疑問的,"Do something…" 一定會是第一個,那接下來呢? MyResource1 和 MyResource2 哪個物件會先被關閉呢?規則是,後產生的物件會先被關閉,所以執行的結果會是:

2011 11 03 09 37 05 pm

為什麼會後宣告的先關閉呢?因為有可能你會在後宣告的物件裡,使用前面宣告的物件,例如:

[code]

package idv.jacky.ironman4.day23;

 

import java.io.BufferedReader;

import java.io.FileReader;

 

public class Day23Example2 {

 

public static void main(String[] args) throws Exception {

try (FileReader f = new FileReader("c:\\temp.txt");

BufferedReader b = new BufferedReader(f);) {

String line = b.readLine();

}

}

}

[/code]

 

如果我們先關閉了第一個物件,有可能會讓第二個物件無法順利操作!所以在使用 tr-with-resource 陳述式時,請記得 [b]後開先關/b] 的原則!

那 Java 是如何知道/判斷,在 try-with-resource 陳述式裡的物件是可以關閉(close)的呢?

在 Java SE 7 裡有一個新的介面(Interface)叫 java.lang.AutoCloseable,裡面只定義了一個 close 的方法,所以實作這個介面的類別,其所生成的物件,都可以放在 try-with-resource 陳述式裡執行。所以我們來自己寫一個實作 java.lang.AutoCloseable 的類別:

[code]

package idv.jacky.ironman4.day22;

 

public class MyResource1 implements AutoCloseable{

 

@Override

public void close() throws Exception {

System.out.println("Close resource 1.");

}

 

}

[/code]

我們把實作 java.lang.AutoCloseable 的類別命名為 MyResource1,然後實作 close 這個方法,裡面只叫 Java 印出 "Close resource 1" 這個字串。接著我們來看看怎麼使用它:

[code]

package idv.jacky.ironman4.day22;

 

public class Day22Example1 {

 

public static void main (String[] args) throws Exception {

try (MyResource1 r1 = new MyResource1()) {

System.out.println("Do something...");

}

}

}

 

[/code]

 

就像昨天的範例一樣,我們在 try-with-resource 陳述式裡宣告了一個 MyResource1 的物件 r1,然後再 try 區塊裡印出 "Do something…" 的字串。然後下面就是程式執行的結果:

2011 11 03 09 16 04 pm

Java 會先執行第7行,印出 "Do something…"後,離開 try 區塊時會去關閉實作 java.lang.AutoCloseable 的 MyResource1 物件,而在 MyResource1.close 的方法裡我們請 Java 印出 "Close resource 1"。因為我們沒有 catch 區塊來補捉 close 方法可能丟出來的例外,所以我們要在 main 方法的宣告上多宣告會丟出 Exception 例外!

這樣是不是有點初步的概念了呢!

昨天的範例程式裡,我們得多做一些事情才能正確地關閉資料串流,像是在 try-catch 區塊外先宣告物件變數,然後在 final 區塊裡得先檢查物件變數是不是 null 等。Java SE 7 裡提供了一個簡便的新陳述式,來簡化這些事。

新的陳述式就叫做 try-with-resource 。簡單的說,就是把宣告資料串流物件這樣的程式碼,直接在在 try 的陳述式裡。我們直接看範例比較快:

[code]

[/code]

你可以看到程式碼第10行,原本 try 陳述式就只有一個 try 加上左大括號,現在變得跟方法一樣,多個小括號,然後把需要關閉的資料串流物件變數宣裡裡面,然後在 try 區塊裡,我們一樣能自由地使用 br 物件,也不用多個 final 區塊來關閉它,當程式離開 try-catch 區塊時,會自動把宣告在 try-with-resource 陳述式裡的資料串流物件給關閉!

Java 只是幫你呼叫 close 方法,雖然你沒看到也沒寫,但 close 方法會丟出的 IOException 一樣得處理。不過我們已經有 catch 了,close 和 readLine 方法都是在 catch 之前呼叫的,所以會一起被 catch 下來。如果你省略了 catch, 那就得在方法宣告上多宣告會丟出 IOException 喔!

[code]

[/code]

還記得第15天的 範例 嗎?那個範例其實不夠完整!

在那個範例程式裡,我們開啟了在 C 磁碟根目錄下的 temp.txt 檔案(第12行),然後程式讀取一行(第13行)就結束了。我們少做了一件事,那就是把檔案給關閉!雖然程式很短,沒有關閉似乎也不影響什麼,但當你的程式愈寫愈大時,可能會存取某些案上千上萬次,若是忽略了這個小小的動作,輕則造成程式當機,重則保貴的資料毀損,那可就得不嘗失了~

所以好習慣的養成,就是靠著每個小細節,那該怎麼正確地關閉程式中開敵的檔案呢?請看下面這個例子:

[code]

[/code]

上面的例子,我們做得完整一點,程式同樣開啟 C 磁碟根目錄下的 temp.txt 檔案,接著透過一個 while 迴圈,將案的內容一行讀取出來後印出在螢幕上。因為產生 FileReader 物件和 BufferedReader.readLine 方法可能會丟出 IOException,所以我們必需要 catch 這個例外。而關閉檔案的程式碼我們就寫在 final 這個區塊裡,之所以要寫在 final 區塊裡主要的原因是,除了程式突然中斷執行外,在 main 方法結束前,一定會執行 final 區塊裡的程式碼!這樣我們就不怕不會沒有執行到關閉檔案的程式碼。

我們將 BufferedReader 物件變數宣告 try-catch 區塊之前,原因是如果宣告在 try 區塊內的話,在離開 try 區塊之後,這個變數就失效了,那麼 final 區塊就沒辦法使用 br 變數了!在 final 區塊裡,我們得先確認一下 br 變數是不是 null,因為我們在第10行宣告時,並沒有對它做初始化的動作,程式有可能在第12行產生 BufferedReader 變數時出錯,程式會直接跳到第17行執行後,接著執行 final 區塊。如果我們沒有判斷而直接執行第20行程式碼的話,程式將會丟出 NullPointerException。

程式第20行呼叫的 close 方法,就是關閉 BufferedReader 物件資料流的方法,它也會順便幫你把 FileReader 物件資料流給關閉。所有在 java.io 這個 package 下的 Stream 和 Reader 還有 Writer 類別等,都有提供 close 這個方法,讓你來關閉相關的資料流。

這樣就好了嗎?close 方法也會有可能丟出 IOException 例外,所以怎麼辦呢?一種是再 final 區塊裡,再用一個 try-catch 來包住 close 程式碼;或是我們想交給呼叫這個方法的人來處理,那就在方法的宣告上,多宣告這個方法會丟出 IOException 例外(第9行)。這樣整個範例就完整了!

Java SE 7 又提供了這個偷懶的方法,那使用上有什麼例外或限制嗎?

在 Java 裡,所有的數字類別(Integer, Long, Float, Double 等),都是繼承 Number 類別,還記得前兩天的 Constants 類別嗎?我們想說讓 Constant 類別更有彈性一點,所以寫了下面這樣的程式碼:

[code]

package idv.jacky.ironman4;

 

import idv.jacky.ironman4.day17.Constant;

 

public class Day19Example {

 

 public static void main(String[] args) {

 Constant<Double> pi = new Constant<>(3.14);

 Constant<Number> c = new Constant<>(3.14);

 }

}

[/code]

既然 Double 是一種 Number,那程式碼第10行應該沒問題吧?不過,在編譯時,我們遇到這樣的錯誤訊息:

[b]

Exception in thread "main" java.lang.Error: Unresolved compilation problem:

 Type mismatch: cannot convert from Constant<Double> to Constant<Number>

 at idv.jacky.ironman4.Day19Example.main(Day19Example.java:10)

[/b]

 

怎麼會!根據 Java 多型的特性,Double 應該可以轉型成 Number 物件啊!例如:

[code]

Number n = new Double(3.14);

[/code]

很不幸地,泛型就是要你指定確定的單一型別,所以像 Constant<Double> 這種複合型別是沒辦法自動轉型的,因此我們還是得乖乖地指定確定的型別才行。 

 

那泛型在 Java SE 7裡有什麼樣新的功能呢?

 

泛型很好用,Java 也強迫你在使用 Collection 類別時,一定要用泛型來指定型別,不然編譯時會出現警告訊息。但泛型用習慣了,每次都要輸入長長的型別宣告,例如

 

[code]

Map<String, List<Integer>> numbers = new HashMap<String, List<Integer>>();

[/code]

我在寫程式時,還真的常常用到這樣落落長的泛型宣告,Java難到不能聰明一點,看到前面已經宣告過了,後面就直接省略不就好了。是的!Java SE 7裡就提供了這樣的省略功能,剛剛的那行程式碼就可以簡略成:

 

[code]

Map<String, List<Integer>> numbers = new HashMap<>();

[/code]

 

是的,後面的實體類別就直接用一個空的角刮號就搞定了。Java 編譯器會聰明地幫你把完整的程式碼補上。

 

昨天的程式碼我們也可以這樣改造一下:

 

[code]

package idv.jacky.ironman4;

 

import idv.jacky.ironman4.day16.Apple;

 

import java.util.ArrayList;

import java.util.List;

 

public class Day18Example {

 

public static void main(String[] args) {

List<Apple> fruits = new ArrayList<>();

fruits.add(new Apple());

fruits.add(new Apple());

makeJuice(fruits);

}

public static void makeJuice(List<Apple> fruits) {

for(Apple a : fruits) {

a.makeJuice();

}

}

 

}

[/code]

真的是省了一些事...

 

1 2 3 4 5  下一篇»