P to P

          何以解憂,唯有學習!讓每一個人都能Open Source
          posts - 4, comments - 12, trackbacks - 0, articles - 11
          ??????應用try-finally,我們可以在異常滿天飛的程序里保證我們的關鍵資源被按時正確清理。一個最常見的應用就是jdbc的Connection, Statement, ResultSet等。

          但是,我最近驚奇地發現,不知道怎么正確清理資源的人大有人在,即使是一些java老手。

          看一個例子先:

          java代碼:?

          void f(){
          ? Connection conn = ...;
          ? Statement stmt = conn.createStatement();
          ? ResultSet rset = ...;
          ? ...
          }


          典型的jdbc程序。但是也是典型的光著屁股,其臭如蘭地走出廁所的典范。哎,你擦屁股了嗎?
          有的哥們振振有辭:我不用管,我的jdbc driver/我的應用服務器/garbage collector會處理的。
          這是典型的糊涂蛋邏輯。沒有close(),jdbc driver, 應用服務器怎么知道你是拉完了,還是光著屁股出去接個電話先?難不成這driver都智能地會算命了?
          garbage collector倒確實管得了。不過,garbage collector不一定運行啊。你要是有10G得內存,要是你的程序就用了10M,garbage collector說不定就一直睡大覺。而且,就算它管,也許等你光著屁股上班被警察抓起來之后才匆匆趕到,你等的起嗎?


          好,有人說,那我擦,我擦,我擦擦擦。行了吧?


          java代碼:?

          void f(){
          ? Connection conn = ...;
          ? Statement stmt = conn.createStatement();
          ? ResultSet rset = ...;
          ? rset.close();
          ? conn.close();
          ? ...
          }



          呵呵。我的傻哥們,你只擦了靠近后背的那三公分,剩下的嘛,別人看不見你就樂得省土塊兒了是么?

          按jdbc標準,ResultSet, Statement, Connection都要close(),也許有的driver會在Connection關閉的時候同時正確清理ResultSet, Statement,但是,并沒有一條規定讓所有的driver都這么做。
          另外,也許你的Connection是從一個池里面來的,它只是回到池中去,如果你不關閉Statement, ResultSet,下一個拿到這個Connection的人也許就倒霉了!
          做事要有始有終,既然開始擦了,就擦干凈點兒,行不?(那個,誰誰誰,借我個防毒面具先!)


          ok,有個講衛生的小傻子這樣擦:


          java代碼:?

          void f(){
          ? Connection conn = ...;
          ? Statement stmt = conn.createStatement();
          ? ResultSet rset = ...;
          ? rset.close();
          ? stmt.close();
          ? conn.close();
          ? ...
          }



          然后洋洋得意地說:我是好孩子,我天天擦屁屁。


          是啊,多聽話的孩子呀。可惜,某天,這孩子正坐在馬桶上美著呢,媽媽喊了嗓子:二傻子,吃飯啦。
          哦!吃飯。二傻子褲子都沒提就竄出來了,熏得媽媽一個跟頭。

          什么問題,傻子做事一根筋,不能打擾,一旦有異常情況出現,屁股就忘了擦了。



          所以,我這里鄭重提醒大家,請用"try-finally"!它獨有凹槽,防止側漏...(糟了,串臺了)

          是啊,java老手們都不是傻子,都知道用try-finally的,可是,別美,你現在就保不齊擦沒擦屁股呢!


          常見擦法:
          java代碼:?

          void f(){
          ? Connection conn = null;
          ? Statement stmt = null;
          ? ResultSet rset = null;
          ? try{
          ? ? conn = ...;
          ? ? stmt = ...;
          ? ? rset = ...;
          ? ? ...
          ? }
          ? finally{
          ? ? ? if(rset!=null)rset.close();
          ? ? ? if(stmt!=null)stmt.close();
          ? ? ? if(conn!=null)conn.close();

          ? }
          }



          嗯。怎么說呢。挺聰明的。都學會if(xxx!=null)這種傳說中條件判斷的上古絕學了。
          可惜,你屁股大,一張紙不夠,你用了第一張紙,滿意地看著它圓滿地完成了金燦燦的任務,再用第二張,靠,只太薄,破了,一手金燦燦地,象帶了個金戒指。你大怒,起,絕塵而去。于是也忘了第三張紙,
          哥們兒,close()是可以出異常的,你rset關了,stmt.close()出現了異常,但是conn就不管了?


          近日有位室外高人,據說是鬼谷子高徒,鑒于憐我世人,不擦屁股的實多的高尚情操,親手賺寫一本絕世擦功秘籍,其文美,其意高,除了擦不干凈之外,真可以說是稱霸擦林。

          java代碼:?


          void close(Connection conn){
          ? try{
          ? ? if(conn!=null) conn.close();
          ? }
          ? catch(Exception e){
          ? ? e.printStackTrace();
          ? }
          }
          void close(ResultSet rset){
          ? ...
          }
          void close(Statement rset){
          ? ...
          }
          void f(){
          ? Connection conn = null;
          ? Statement stmt = null;
          ? ResultSet rset = null;
          ? try{
          ? ? conn = ...;
          ? ? stmt = ...;
          ? ? rset = ...;
          ? ? ...
          ? }
          ? finally{
          ? ? ?close(rset);
          ? ? ?close(stmt);
          ? ? ?close(conn);

          ? }
          }



          哈,你們不能紙擦破了就不接著擦啊,甚至大而化之,不能擦股用具有了問題就半途而廢呀!

          具信,該高人以此法擦遍天下凡十數載,未有擦而無功者。

          可惜,高人卻忽視了,除了紙會出故障,甚至大而化之,一切擦具(如土塊兒,木條兒,手指)都可能出現故障,還有別的地方也會出故障地!
          除了Exception,還有Error啊,我的高人!如果close(rset)拋了一個Error,你的close(stmt), close(conn)不都歇菜了?

          后來,高人在《絕世武功補遺》里面解釋說:Error代表不可恢復錯誤,說明整個排泄大業都受阻了,所以根本不應該試圖對這種情況做任何處理,你也處理不了(自然也隱含此時你也根本無法擦屁股了的論斷)。任何試圖在這種情況下仍然固執擦屁股的做法都是倒行逆施,螳臂當車,必然被歷史的車輪所攆碎。

          此書一處,天下辟易。其革命性之深遠,難以估量。具有關方面評論,Sun這個公共廁所的try-finally這個工具的設定本身就是不合理的,應該被歷史車輪攆碎的,因為try-finally居然試圖在出現Error的時候去做一些事情!是可忍,孰不可忍?
          可以預見,try-finally將被sun徹底廢棄,并且向廣大公眾做公開道歉以檢討多年來的欺騙造成的惡劣影響。
          另外,公廁的構造也受到質疑,因為一旦有一個拉客在擦的時候某一步無可挽回地失敗(比如,太緊張,手一抖,紙掉到了坑里,又死活伸手撈不著),那么他就大搖大擺不再繼續擦,而如果碰巧此人剛吃了蘿卜,就會把整個廁所里的其它拉客都熏得無法繼續。(想想一個app server吧。你一個程序歇菜,樂得請病假不擦了,別人也跟著倒霉?)


          嘿嘿,那么,你擦了嗎?你肯定你擦了?擦干凈了?

          幸好,我們翻遍上古秘籍,最終在北京山頂洞人的失傳寶典《呼呼,擦!》中發現了一個據稱絕對干凈的擦法,那就是------------

          一下一下擦!

          具體操作辦法如下:

          java代碼:?


          void f(){
          ? finalConnection conn = ...;
          ? try{
          ? ? finalStatement stmt = ...;
          ? ? try{
          ? ? ? finalResultSet rset = ...;
          ? ? ? try{
          ? ? ? ? ...
          ? ? ? }
          ? ? ? finally{rset.close();}
          ? ? }
          ? ? finally{stmt.close();}
          ? }
          ? finally{conn.close();}
          }




          其訣竅就是,每建立一個需要清理的資源,就用一個try-finally來保證它可以被清理掉。

          如此,任何時候,你都是屁股干干靜靜地離開衛生間。


          哪。好多圣人門徒跟我說:這樣一下一下擦,姿勢非常不雅觀(看看那嵌套的try塊吧),有違古禮。我們反對!


          靠,你說孔丑兒古還是山頂洞人古??
          屁股還泛著味兒呢,還拽什么“雅”?

          而且,要是死要面子,也可以拉個簾子,擦的時候別讓人看見嘛。比如:

          java代碼:?


          interface ResultListener{
          ? void call(ResultSet rset);
          }
          class SqlReader{
          void read(ResultListener l){
          ? ? finalConnection conn = ...;
          ? ? try{
          ? ? ? finalStatement stmt = ...;
          ? ? ? try{
          ? ? ? ? finalResultSet rset = ...;
          ? ? ? ? try{
          ? ? ? ? ? l.call(rset);
          ? ? ? ? }
          ? ? ? ? finally{rset.close();}
          ? ? ? }
          ? ? ? finally{stmt.close();}
          ? ? }
          ? ? finally{conn.close();}
          ? }
          }



          這一下一下擦的動作都藏在SqlReader這個簾子里,你直接在ResultListener里面拉不就行了?

          那位高人說了,這太復雜,就為了擦個屁股不值。

          這個嘛,值不值的另說,你那個簡單,就是簡簡單單地擦不干凈屁股。要不您干脆別擦得了,更簡單呢還。反正您出門兒就愣說擦的是Chanel香水兒就是了。有啥比瞪眼兒說白話兒簡單?

          對了, 我還忘了一個條款:
          就是擦屁股的時候按順序擦。誰進廁所的,要讓人家出去。

          “什么狗屁規則?“那位問了。

          這個這個--,啊,你猜猜~~~?

          嗯,對了,是這樣的,上廁所都不著急,姍姍來遲,上課更不著急,更喜歡遲到了,對不對?而誰上課天天遲到早退還不擔心畢業?當然是太子黨了,是不?
          人家都太子黨了,你還不讓人家先出去?活膩味了你?(此處尾音要拉長,而且向上拐)


          反正啊,具體說,ResultSet最后創建,但是要先關。

          Statement其次。Connection最后。


          當然了,也許在你的環境下,次序錯了也沒出事情。但是,咱么吃軟飯的(吃軟件這口飯的)圖啥?不就圖個放心嗎?上廁所圖啥?不就圖個別讓太子黨抓去當兔子嗎?
          也許某個driver對次序不敏感,但是不好說哪天你換個環境就忽然她奶奶的敏感了呢?
          比如吧,你有connection pool, conn.close()把connection返回到connection。

          你要是先conn.close(),好嘛,connection先回到pool了,正好別的線程里面等著要connection,立馬這個connection又給分配出去了。
          這下齊了,你statement, resultset還沒關呢,那邊事故單位領導就找上門了。什么香油油的桌子,什么桐油炸丸子,全給你送來了。這不添堵嗎?

          好在,在我們《呼呼,擦!》寶典中記載的“一下一下擦”神功,老少咸宜,童叟無欺,有道是:法擦大法好,不如法擦冰箱好!

          跑題了。反正是,只要你一個try-finally對應一個資源,你就不可能在次序上出錯。自然而然的就是后入先出的堆棧結構。
          反觀別的擦法,就沒有這個效果,次序如何,全靠你自己掌握。弄錯了,系統也不告訴你。等著吃桐油炸丸子吧。

          這也是我們推廣一下一下擦的一個原因。


          上一次由ajoo于2005-6-09 周四, 下午12:45修改,總共修改了5次
          主站蜘蛛池模板: 卫辉市| 富阳市| 高邑县| 夏津县| 拉孜县| 克什克腾旗| 察雅县| 临汾市| 奉节县| 思南县| 陕西省| 微博| 海盐县| 宁安市| 射阳县| 阿拉善盟| 东海县| 承德市| 土默特右旗| 闸北区| 太原市| 洛扎县| 石城县| 甘孜| 双江| 康定县| 滨海县| 巴南区| 盘锦市| 珠海市| 五大连池市| 宝鸡市| 巴林右旗| 蓝田县| 乐东| 武清区| 嫩江县| 墨脱县| 石嘴山市| 射阳县| 磐安县|