一輩子的程序員?

          愛你一生不變-芳芳!
          posts - 27, comments - 15, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          2006年9月11日

              何去何從?有點(diǎn)迷茫,是沉寂在溫柔之鄉(xiāng)還是去迎接狂風(fēng)暴雨!
          看來自己還是沒有自信!bs..........myself

          posted @ 2007-08-15 14:32 boddi 閱讀(589) | 評論 (3)編輯 收藏

          2007最新騙局:

          ?????建行一同志轉(zhuǎn)送:?今天經(jīng)過一棟大樓門口,門口有一提款機(jī)。有一個(gè)老伯,一直?

          看著我走過他身邊,突然叫住我。他說他不識字,拿一張?zhí)峥羁ㄒ規(guī)退诖髽情T口?

          的自動提款機(jī)取錢。我回答我無法幫你取,叫警衛(wèi)幫你。結(jié)果,他就回答我說不用了?

          ,繼續(xù)找其他路人幫他取錢。朋友們要記住---取款機(jī)可是有攝影機(jī)耶。萬一他說我搶?

          劫或是偷他的提款卡,甚至他的卡片是偷來的,幫他領(lǐng)錢會在提款機(jī)留下影像,絕對?

          會讓你百口莫辯!我會警惕?!?是因?yàn)橐延型律袭?dāng),目前仍官司纏身。顯然這是詐騙?

          集團(tuán)在找替身了!?請用力傳出去~~~?騙案真是層出不窮,一不小心就會踏入陷阱,真是?

          令人防不勝防!

          提醒各位朋友在外多小心!?

          (2)一業(yè)主,家中突然斷電,看到窗戶外別人家里都有電,就出門查看自家電?

          表箱,打開門就被刀子頂著了——持刀入室搶劫傷人家里突然斷電,不要貿(mào)然就開門?

          查看,有貓眼的多觀察一會門外動靜,沒貓眼的也隔著門靜聽一段時(shí)間,沒有異常響?

          動再開門。?

          (3)各位女同胞們注意了!這是最新騙局?

          女同胞請注意?男同胞請叫自己的朋友注意?新出的情況,女性朋友要特別注意啦:?

          一位上班的小姐在下班回家的路上看到一個(gè)小孩子一直哭,很可憐?,然后就過去問那?

          小朋友怎么了.小朋友就跟那個(gè)小姐說:?我迷路了,可以請你帶我回家嗎?然后拿一張?

          紙條給她看,?說那是他家地址.然后她就笨笨的帶小孩子去了.一般人都有同情心,然后?

          帶到那個(gè)所謂小孩子的家里以后,她一按鈴,門鈴像是有高壓電,就失去知覺了.隔天醒?

          來就被脫光光在一間空屋里,身邊什么都沒有了,她甚至連犯人長啥樣子都沒看見.所以?

          ,現(xiàn)在人犯案都是利用同情心啊,如果遇到類似這種的,千萬別帶他去,要帶就帶他到派?

          出所去好了,走丟的小孩放到派出所一定沒錯(cuò)啦,請通知身邊所有女性,為了廣大女士?

          的安全,看完后麻煩給轉(zhuǎn)發(fā)給所有人。?

          (4)今天遇到討飯新招,大家注意提防了~~?

          今天在家休息,有人按門鈴,開門一看,是個(gè)50來歲的老婦女,手里拿了2包喜糖,我?

          還以為是鄰居來分喜糖的,結(jié)果一開口,聽得出不是本地人,她說什么這2包糖給我們?

          的,圖個(gè)喜氣,要換一點(diǎn)錢給她,后面還說了一大堆不知道什么,我也沒聽清楚,感?

          覺就是不對,嚇的馬上關(guān)門,暈!這年頭,還有這么討錢的一剛。?

          (5)轉(zhuǎn)發(fā):大家注意了!到自動取款機(jī)取錢時(shí)一定要倍加小心!!!!!?

          昨晚在工行自動取款機(jī)取錢時(shí),后面來了個(gè)老婦女,問我能不能取錢,還說?

          什么取款機(jī)有個(gè)鍵可能壞了,旁邊不知什么時(shí)候來了個(gè)小女孩,一直想我身邊躋,我?

          也沒在意,小孩子淘氣嘛,可是過分的是她竟然把手朝出鈔口放,準(zhǔn)備拿我的錢了,?

          我感覺不對勁了,立即把她推到一邊,等著把錢取出來。之后我想了一下,她們倆給?

          我設(shè)了個(gè)套:老婦女負(fù)責(zé)和我瞎聊,吸引我的注意力,小女孩趁我不注意時(shí)搶走我的?

          錢!如果我不防備的話,錢說不定就被搶走了,這樣的話,我就進(jìn)套了:(一則我立?

          即去追小女孩,去追回我的錢,可是誰又會相信一個(gè)小女孩能搶我一個(gè)大人的錢呢??

          更可怕的是站在我后面的老婦女將會取光我卡中所有的錢,因?yàn)槲业目ㄟ€在取款機(jī)里?

          面;二則我不立即去追小女孩,等拿到卡再追,到那時(shí)小女孩就無影無蹤了,錢也就?

          沒了啊:(她們真的很聰明,很可恥的!!!)?

          這是我的親身經(jīng)歷,希望大家以后取錢時(shí)一定要警惕起來,注意觀察周圍的所有人,?

          并轉(zhuǎn)告周圍的家人、同事、朋友,讓壞蛋分子沒有可乘之機(jī)!!!!又出現(xiàn)騙局新招?

          !!?

          (6)我父母都退休在家。昨天上午,來一陌生中年人,說自己摩托車油開沒了,加油?

          站太遠(yuǎn),摩托車又太重推不動,所以想問我父母要一個(gè)可樂瓶去買汽油,剛開口就說?

          實(shí)在不行就出2、3元買一個(gè)空瓶好了。我母親就拿了個(gè)空瓶給他,別說他還真從口袋?

          里掏出錢來,不過是幾張百元大鈔,還讓我父母找錢。我母親頓生警覺,說算了,不?

          過是一個(gè)空瓶而已。他10元錢買下來,只不過還是那張百元大鈔。好在我母?

          親尚未龍鐘,也不是那種愛貪小便宜的人?

          女性朋友一定要認(rèn)真看完,注意自我安全啊,現(xiàn)在萬惡的社會。。。。朋友發(fā)給我一?

          篇報(bào)道,現(xiàn)轉(zhuǎn)給各位看看?,出門在外,千萬小心,小心千萬。。。?

          (7)一對新婚夫婦到巴黎度蜜月。在巴黎,妻子在一間時(shí)尚服裝店試衣服?,身為丈夫?

          就在試衣間外等候。但等候多時(shí)卻不見妻子走出來?,緊張的丈夫要求店員幫忙到里頭?

          查看?,卻意外發(fā)現(xiàn)試衣間空無一人。丈夫以為妻子開玩笑作弄人?,要他緊張.于是回到?

          酒店等她回來。幾小時(shí)后卻不見妻子的蹤影,才知事態(tài)嚴(yán)重。丈夫趕忙報(bào)警?,并到巴黎?

          所有服裝店和醫(yī)院詢問妻子下落。三星期過去了,妻子猶如從人間蒸發(fā),音訊全無,傷?

          心的丈夫只能收拾包袱回到?**?。由于無法從絕望中振作,丈夫無心工作,甚至獨(dú)自生?

          活?,決定把自己放逐,流浪到各地方。幾年后?,他心血來潮到巴厘島,在一破舊的屋子?

          參觀一畸形秀?(?freak?show?)?。他見到一臟生銹的鐵籠里,有一女人四肢全無,身軀,?

          包括臉部,猶如破布般殘破?,充滿疤痕。她在地上扭曲著?,并發(fā)出有如野獸般的呻吟聲?

          。突然間男人驚恐地發(fā)出尖叫聲。他從那毫無人樣的女人臉上見到,他再熟悉不過,屬?

          于他新婚不久就告失蹤的妻子臉上的紅色胎記。?

          (8)另一版本則發(fā)生在上海。幾年前一女通知公安她的表妹在上海市集購物時(shí)無故失?

          蹤,可是遍尋不著?,直到五年后一友人撞見這表妹在泰國曼谷街道上行乞。恐怖的是她?

          不知何故沒了雙手雙腳,身子被鐵鏈綁在燈柱旁。?

          (9)這是在某一對夫婦去香港游玩時(shí)發(fā)生的故事。一對夫妻不知不覺走入了全香港治?

          安最壞的地區(qū)的一家精品店里???妻子對店里的衣服樣式十分喜歡?,隨后就進(jìn)入試衣間?

          試衣。可是,先生在外頭等了又等,卻不見妻子出來。由于實(shí)在是等太久了?,所以先生?

          開門?進(jìn)去找她,可是試衣間里早已空空如也。他吃驚地向店員詢問妻子到哪里去了,可?

          是店員們卻好象是串通好了一樣,都說沒有看見,并堅(jiān)持根本沒有象他妻子這樣的人來?

          過店里。因此他只好請當(dāng)?shù)氐木靺f(xié)助搜索這家精品店?,可是卻一無所獲。后來他又?

          一個(gè)人找了一段時(shí)間?,直到他的簽證到期。最后不得已他就在找不到妻子的情況下回?

          國了。之后經(jīng)過了一年?…?他向公司請了一段長假,再一次回到香港去找他的妻子。他?

          帶著妻子的相片走遍香港的大街小巷,但這次仍是一點(diǎn)線索也沒有。終于假期就要結(jié)束?

          了,他身心疲憊地開始考慮要回國的時(shí)候?,有一天無意間經(jīng)過了一間珍奇小屋。小屋的?

          看板上寫著?:?達(dá)磨(不倒翁)?雖然他對珍奇事物并不感興趣?,但由于連日疲勞他想?

          讓自己改變一下心情?,?加上看板上寫著?達(dá)磨的文字也引起他的興趣。最后他決定?

          進(jìn)去瞧瞧。但是他不該進(jìn)去的!因?yàn)檎淦嫘∥堇锩嬲钩鲆患钏麘K不忍睹的東?…?小屋?

          里的舞臺上有一位手腳都被切斷的全裸女性被當(dāng)成花瓶一樣擺在那里?!?這位女性的舌?

          頭已經(jīng)被拔掉了,不斷發(fā)出奇怪的呻吟聲。看到這么惡心的東西真令他恨不得馬上拔腿?

          就跑?,但不知為什么他心里感受到一股奇怪的氣氛?,于是他又重新仔細(xì)看那女人的面?

          孔?…?沒錯(cuò)!這女人正是他一年前失蹤的妻子。后來,他向當(dāng)?shù)氐暮诘乐Ц洱嫶蟮内H金?

          換取妻子的剩下的軀干。但一切都太遲了,他可憐的妻子早就已經(jīng)瘋了。現(xiàn)在她還住在?

          國內(nèi)某家醫(yī)院,繼續(xù)不斷地發(fā)出奇怪的呻吟聲?…?

          (10)最近有人告訴我,他的朋友在晚上聽到門口有嬰兒在哭,不過當(dāng)時(shí)已很晚了,?

          而且她認(rèn)為這件事很奇怪,于是她打電話給警察。警察告訴她∶?「無論如何,絕對不?

          要開?門。」這位女士表示那聲音聽起來象是嬰兒爬到窗戶附近哭,她擔(dān)心嬰兒會爬到?

          街上,被車子碾過。警察告訴她∶我們已派人前往,無論如何不能開門。警方認(rèn)為這?

          是一個(gè)連續(xù)殺人犯,利用嬰兒哭聲的錄音帶,誘使女性以為有人在外面遺棄嬰兒,她?

          們出門察看。雖然尚未證實(shí)此事,但是警方已接到許多女性打電話來說,他們晚上獨(dú)?

          自在家時(shí),聽到門外有嬰兒的哭聲,請將這個(gè)消息傳給其他人,不要因?yàn)槁牭綃雰旱?

          哭聲而開門。?

          請嚴(yán)肅看待這貼子!有這么離譜!小心為妙!!!?

          以前聽說大活人現(xiàn)場失蹤,后來被賣到馬戲團(tuán)、被賣器官什么的,只當(dāng)是天方夜譚。?

          結(jié)果真的看到發(fā)生在真實(shí)中的恐怖故事。?

          (11)事情是同事群發(fā)郵件告知的。她的朋友,簡稱小a吧,上周和兩個(gè)女孩子,簡稱?

          小b和小c,去逛羅湖商業(yè)城。羅湖商業(yè)城是深圳假貨集散地,龍蛇混雜,緊靠深圳火?

          車站和香港的羅湖口岸,人流量非常大。話說小c內(nèi)急,就去上衛(wèi)生間,小a和小b在洗?

          手間外面等。等了很久很久,還是不見小c出來,兩個(gè)人有點(diǎn)奇怪了。于是兩個(gè)人進(jìn)去?

          催她。誰知道進(jìn)去一看,人影全無。兩個(gè)人倒豎一口冷氣,打手機(jī)也沒人接。一個(gè)大?

          活人,難道就這么活不見人死不見尸的失蹤了?于是趕緊報(bào)警。?警察來了,問情事情?

          經(jīng)過以后,說了一句令人無比毛骨悚然的話,“你們有沒有看見其他可疑的人進(jìn)去??

          ”,兩個(gè)人再三回憶,沒有。因?yàn)椴豢赡軒е粋€(gè)活生生的100多斤的人出來,而她們?

          不注意。這時(shí)候小a突然想起來,其間有個(gè)清潔工打扮的人推著一輛清潔小車進(jìn)去、接?

          著又出來……?警察告訴他們,這種事情已經(jīng)不是第一次發(fā)生,現(xiàn)在深圳警方初步懷疑?

          一個(gè)犯罪團(tuán)伙,有組織地在管理疏松的低檔商業(yè)城,利用人們,尤其是女性對清潔工?

          沒有防范意識的心理,進(jìn)行有組織地綁架、販賣人體器官犯罪。別忘了,羅湖商業(yè)城?

          離香港和深圳火車站有多么地近。現(xiàn)在已經(jīng)幾天過去了,那個(gè)可憐的小c姑娘,仍是活?

          不見人死不見尸。我的同事說,小a,也就是我同事的朋友,仍在等小c的消息。但是?

          很可能,也許如果幸運(yùn)的話,活著的小c會被扔在哪個(gè)角落,只是失去了她的腎,但是?

          ,更有可能的是,也許再過幾天或者幾個(gè)月、幾年,小c的頭顱和軀體、四肢會在深圳?

          的城鄉(xiāng)結(jié)合部的垃圾堆被人發(fā)現(xiàn)。如果看到這個(gè)發(fā)生在身邊的活生生的恐怖事件,請?

          轉(zhuǎn)告身邊的女性親友,一定小心防范清潔工打扮的人,因?yàn)樗?她很可能會趁你不注意?

          把你敲暈,放進(jìn)清潔車?yán)撸酉聛淼戎愕氖菬o比恐怖的活人分尸。?

          posted @ 2007-03-05 17:11 boddi 閱讀(420) | 評論 (2)編輯 收藏

          遠(yuǎn)程方法調(diào)用入門指南(Java RMI Tutorial)


          Java R MI Tutorial

          遠(yuǎn)程方法調(diào)用入門指南

          Copyright ? 2005 Stephen Suen. All rights reserved.

          Java 遠(yuǎn)程方法調(diào)用(Remote Method Invocation, RMI)使得運(yùn)行在一個(gè) Java 虛擬機(jī)(Java Virtual Machine, JVM)的對象可以調(diào)用運(yùn)行另一個(gè) JVM 之上的其他對象的方法,從而提供了程序間進(jìn)行遠(yuǎn)程通訊的途徑。RMI 是 J2EE 的很多分布式技術(shù)的基礎(chǔ),比如 RMI-IIOP 乃至 EJB。本文是 RMI 的一個(gè)入門指南,目的在于幫助讀者快速建立對 Java RMI 的一個(gè)感性認(rèn)識,以便進(jìn)行更深層次的學(xué)習(xí)。事實(shí)上,如果你了解 RMI 的目的在于更好的理解和學(xué)習(xí) EJB,那么本文就再合適不過了。通過本文所了解的 RMI 的知識和技巧,應(yīng)該足夠服務(wù)于這個(gè)目的了。

          本文的最新版本將發(fā)布在程序員咖啡館網(wǎng)站上(建設(shè)中)。歡迎訂閱我們的郵件組,以獲得關(guān)于本文的正式發(fā)布及更新信息。

          全文在保證完整性,且保留全部版權(quán)聲明(包括上述鏈接)的前提下可以在任意媒體轉(zhuǎn)載——須保留此標(biāo)注。


          1. 簡介

          我們知道遠(yuǎn)程過程調(diào)用(Remote Procedure Call, RPC)可以用于一個(gè)進(jìn)程調(diào)用另一個(gè)進(jìn)程(很可能在另一個(gè)遠(yuǎn)程主機(jī)上)中的過程,從而提供了過程的分布能力。Java 的 RMI 則在 RPC 的基礎(chǔ)上向前又邁進(jìn)了一步,即提供分布式 對象間的通訊,允許我們獲得在遠(yuǎn)程進(jìn)程中的對象(稱為遠(yuǎn)程對象)的引用(稱為遠(yuǎn)程引用),進(jìn)而通過引用調(diào)用遠(yuǎn)程對象的方法,就好像該對象是與你的客戶端代碼同樣運(yùn)行在本地進(jìn)程中一樣。RMI 使用了術(shù)語"方法"(Method)強(qiáng)調(diào)了這種進(jìn)步,即在分布式基礎(chǔ)上,充分支持面向?qū)ο蟮奶匦浴?/p>

          RMI 并不是 Java 中支持遠(yuǎn)程方法調(diào)用的唯一選擇。在 RMI 基礎(chǔ)上發(fā)展而來的 RMI-IIOP(Java Remote Method Invocation over the Internet Inter-ORB Protocol),不但繼承了 RMI 的大部分優(yōu)點(diǎn),并且可以兼容于 CORBA。J2EE 和 EJB 都要求使用 RMI-IIOP 而不是 RMI。盡管如此,理解 RMI 將大大有助于 RMI-IIOP 的理解。所以,即便你的興趣在 RMI-IIOP 或者 EJB,相信本文也會對你很有幫助。另外,如果你現(xiàn)在就對 API 感興趣,那么可以告訴你,RMI 使用 java.rmi 包,而 RMI-IIOP 則既使用 java.rmi 也使用擴(kuò)展的 javax.rmi 包。

          本文的隨后內(nèi)容將僅針對 Java RMI。

          2. 分布式對象

          在學(xué)習(xí) RMI 之前,我們需要了解一些基礎(chǔ)知識。首先需要了解所謂的分布式對象(Distributed Object)。分布式對象是指一個(gè)對象可以被遠(yuǎn)程系統(tǒng)所調(diào)用。對于 Java 而言,即對象不僅可以被同一虛擬機(jī)中的其他客戶程序(Client)調(diào)用,也可以被運(yùn)行于其他虛擬機(jī)中的客戶程序調(diào)用,甚至可以通過網(wǎng)絡(luò)被其他遠(yuǎn)程主機(jī)之上的客戶程序調(diào)用。

          下面的圖示說明了客戶程序是如何調(diào)用分布式對象的:

          從圖上我們可以看到,分布式對象被調(diào)用的過程是這樣的:

          1. 客戶程序調(diào)用一個(gè)被稱為 Stub (有時(shí)譯作存根,為了不產(chǎn)生歧義,本文將使用其英文形式)的客戶端代理對象。該代理對象負(fù)責(zé)對客戶端隱藏網(wǎng)絡(luò)通訊的細(xì)節(jié)。Stub 知道如何通過網(wǎng)絡(luò)套接字(Socket)發(fā)送調(diào)用,包括如何將調(diào)用參數(shù)轉(zhuǎn)換為適當(dāng)?shù)男问揭员銈鬏數(shù)取?/p>

          2. Stub 通過網(wǎng)絡(luò)將調(diào)用傳遞到服務(wù)器端,也就是分布對象一端的一個(gè)被稱為 Skeleton 的代理對象。同樣,該代理對象負(fù)責(zé)對分布式對象隱藏網(wǎng)絡(luò)通訊的細(xì)節(jié)。Skeleton 知道如何從網(wǎng)絡(luò)套接字(Socket)中接受調(diào)用,包括如何將調(diào)用參數(shù)從網(wǎng)絡(luò)傳輸形式轉(zhuǎn)換為 Java 形式等。

          3. Skeleton 將調(diào)用傳遞給分布式對象。分布式對象執(zhí)行相應(yīng)的調(diào)用,之后將返回值傳遞給 Skeleton,進(jìn)而傳遞到 Stub,最終返回給客戶程序。

          這個(gè)場景基于一個(gè)基本的法則,即行為的定義和行為的具體實(shí)現(xiàn)相分離。如圖所示,客戶端代理對象 Stub 和分布式對象都實(shí)現(xiàn)了相同的接口,該接口稱為遠(yuǎn)程接口(Remote Interface)。正是該接口定義了行為,而分布式對象本身則提供具體的實(shí)現(xiàn)。對于 Java RMI 而言,我們用接口(interface)定義行為,用類(class)定義實(shí)現(xiàn)。

          3. RMI 架構(gòu)

          RMI 的底層架構(gòu)由三層構(gòu)成:

          • 首先是 Stub/Skeleton 層。該層提供了客戶程序和服務(wù)程序彼此交互的接口。

          • 然后是遠(yuǎn)程引用(Remote Reference)層。這一層相當(dāng)于在其之上的 Stub/Skeleton 層和在其之下的傳輸協(xié)議層之前的中間件,負(fù)責(zé)處理遠(yuǎn)程對象引用的創(chuàng)建和管理。

          • 最后是傳輸協(xié)議(Transport Protocol) 層。該層提供了數(shù)據(jù)協(xié)議,用以通過線路傳輸客戶程序和遠(yuǎn)程對象間的請求和應(yīng)答。

          這些層之間的交互可以參照下面的示意圖:

          和其它分布式對象機(jī)制一樣,Java RMI 的客戶程序使用客戶端的 Stub 向遠(yuǎn)程對象請求方法調(diào)用;服務(wù)器對象則通過服務(wù)器端的 Skeleton 接受請求。我們深入進(jìn)去,來看看其中的一些細(xì)節(jié)。

          注意: 事實(shí)上,在 Java 1.2 之后,RMI 不再需要 Skeleton 對象,而是通過 Java 的反射機(jī)制(Reflection)來完成對服務(wù)器端的遠(yuǎn)程對象的調(diào)用。為了便于說明問題,本文以下內(nèi)容仍然基于 Skeleton 來講解。

          當(dāng)客戶程序調(diào)用 Stub 時(shí),Stub 負(fù)責(zé)將方法的參數(shù)轉(zhuǎn)換為序列化(Serialized)形式,我們使用一個(gè)特殊的術(shù)語,即編列(Marshal)來指代這個(gè)過程。編列的目的是將這些參數(shù)轉(zhuǎn)換為可移植的形式,從而可以通過網(wǎng)絡(luò)傳輸?shù)竭h(yuǎn)程的服務(wù)對象一端。不幸的是,這個(gè)過程沒有想象中那么簡單。這里我們首先要理解一個(gè)經(jīng)典的問題,即方法調(diào)用時(shí),參數(shù)究竟是傳值還是傳引用呢?對于 Java RMI 來說,存在四種情況,我們將分別加以說明。

          • 對于基本的原始類型(整型,字符型等等),將被自動的序列化,以傳值的方式編列。

          • 對于 Java 的對象,如果該對象是可序列化的(實(shí)現(xiàn)了 java.io.Serializable 接口),則通過 Java 序列化機(jī)制自動地加以序列化,以傳值的方式編列。對象之中包含的原始類型以及所有被該對象引用,且沒有聲明為 transient 的對象也將自動的序列化。當(dāng)然,這些被引用的對象也必須是可序列化的。

          • 絕大多數(shù)內(nèi)建的 Java 對象都是可序列化的。 對于不可序列化的 Java 對象(java.io.File 最典型),或者對象中包含對不可序列化,且沒有聲明為 transient 的其它對象的引用。則編列過程將向客戶程序拋出異常,而宣告失敗。

          • 客戶程序可以調(diào)用遠(yuǎn)程對象,沒有理由禁止調(diào)用參數(shù)本身也是遠(yuǎn)程對象(實(shí)現(xiàn)了 java.rmi.Remote 接口的類的實(shí)例)。此時(shí),RMI 采用一種模擬的傳引用方式(當(dāng)然不是傳統(tǒng)意義的傳引用,因?yàn)楸镜貙?nèi)存的引用到了遠(yuǎn)程變得毫無意義),而不是將參數(shù)直接編列復(fù)制到遠(yuǎn)程。這種情況下,交互的雙方發(fā)生的戲劇性變化值得我們注意。參數(shù)是遠(yuǎn)程對象,意味著該參數(shù)對象可以遠(yuǎn)程調(diào)用。當(dāng)客戶程序指定遠(yuǎn)程對象作為參數(shù)調(diào)用服務(wù)器端遠(yuǎn)程對象的方法時(shí),RMI 的運(yùn)行時(shí)機(jī)制將向服務(wù)器端的遠(yuǎn)程對象發(fā)送作為參數(shù)的遠(yuǎn)程對象的一個(gè) Stub 對象。這樣服務(wù)器端的遠(yuǎn)程對象就可以回調(diào)(Callback)這個(gè) Stub 對象的方法,進(jìn)而調(diào)用在客戶端的遠(yuǎn)程對象的對應(yīng)方法。通過這種方法,服務(wù)器端的遠(yuǎn)程對象就可以修改作為參數(shù)的客戶端遠(yuǎn)程對象的內(nèi)部狀態(tài),這正是傳統(tǒng)意義的傳引用所具備的特性。是不是有點(diǎn)暈?這里的關(guān)鍵是要明白,在分布式環(huán)境中,所謂服務(wù)器和客戶端都是相對的。被請求的一方就是服務(wù)器,而發(fā)出請求的一方就是客戶端。

          在調(diào)用參數(shù)的編列過程成功后,客戶端的遠(yuǎn)程引用層從 Stub 那里獲得了編列后的參數(shù)以及對服務(wù)器端遠(yuǎn)程對象的遠(yuǎn)程引用(參見 java.rmi.server.RemoteRef API)。該層負(fù)責(zé)將客戶程序的請求依據(jù)底層的 RMI 數(shù)據(jù)傳輸協(xié)議轉(zhuǎn)換為傳輸層請求。在 RMI 中,有多種的可能的傳輸機(jī)制,比如點(diǎn)對點(diǎn)(Point-to-Point)以及廣播(Multicast)等。不過,在當(dāng)前的 JMI 版本中只支持點(diǎn)對點(diǎn)協(xié)議,即遠(yuǎn)程引用層將生成唯一的傳輸層請求,發(fā)往指定的唯一遠(yuǎn)程對象(參見 java.rmi.server.UnicastRemoteObject API)。

          在服務(wù)器端,服務(wù)器端的遠(yuǎn)程引用層接收傳輸層請求,并將其轉(zhuǎn)換為對遠(yuǎn)程對象的服務(wù)器端代理對象 Skeleton 的調(diào)用。Skeleton 對象負(fù)責(zé)將請求轉(zhuǎn)換為對實(shí)際的遠(yuǎn)程對象的方法調(diào)用。這是通過與編列過程相對的反編列(Unmarshal)過程實(shí)現(xiàn)的。所有序列化的參數(shù)被轉(zhuǎn)換為 Java 形式,其中作為參數(shù)的遠(yuǎn)程對象(實(shí)際上發(fā)送的是遠(yuǎn)程引用)被轉(zhuǎn)換為服務(wù)器端本地的 Stub 對象。

          如果方法調(diào)用有返回值或者拋出異常,則 Skeleton 負(fù)責(zé)編列返回值或者異常,通過服務(wù)器端的遠(yuǎn)程引用層,經(jīng)傳輸層傳遞給客戶端;相應(yīng)地,客戶端的遠(yuǎn)程引用層和 Stub 負(fù)責(zé)反編列并最終將結(jié)果返回給客戶程序。

          整個(gè)過程中,可能最讓人迷惑的是遠(yuǎn)程引用層。這里只要明白,本地的 Stub 對象是如何產(chǎn)生的,就不難理解遠(yuǎn)程引用的意義所在了。遠(yuǎn)程引用中包含了其所指向的遠(yuǎn)程對象的信息,該遠(yuǎn)程引用將用于構(gòu)造作為本地代理對象的 Stub 對象。構(gòu)造后,Stub 對象內(nèi)部將維護(hù)該遠(yuǎn)程引用。真正在網(wǎng)絡(luò)上傳輸?shù)膶?shí)際上就是這個(gè)遠(yuǎn)程引用,而不是 Stub 對象。

          4. RMI 對象服務(wù)

          在 RMI 的基本架構(gòu)之上,RMI 提供服務(wù)與分布式應(yīng)用程序的一些對象服務(wù),包括對象的命名/注冊(Naming/Registry)服務(wù),遠(yuǎn)程對象激活(Activation)服務(wù)以及分布式垃圾收集(Distributed Garbage Collection, DGC)。作為入門指南,本文將指介紹其中的命名/注冊服務(wù),因?yàn)樗菍?shí)戰(zhàn) RMI 所必備的。其它內(nèi)容請讀者自行參考其它更加深入的資料。

          在前一節(jié)中,如果你喜歡刨根問底,可能已經(jīng)注意到,客戶端要調(diào)用遠(yuǎn)程對象,是通過其代理對象 Stub 完成的,那么 Stub 最早是從哪里得來的呢?RMI 的命名/注冊服務(wù)正是解決這一問題的。當(dāng)服務(wù)器端想向客戶端提供基于 RMI 的服務(wù)時(shí),它需要將一個(gè)或多個(gè)遠(yuǎn)程對象注冊到本地的 RMI 注冊表中(參見java.rmi.registry.Registry API)。每個(gè)對象在注冊時(shí)都被指定一個(gè)將來用于客戶程序引用該對象的名稱。客戶程序通過命名服務(wù)(參見 java.rmi.Naming API),指定類似 URL 的對象名稱就可以獲得指向遠(yuǎn)程對象的遠(yuǎn)程引用。在 Naming 中的 lookup() 方法找到遠(yuǎn)程對象所在的主機(jī)后,它將檢索該主機(jī)上的 RMI 注冊表,并請求所需的遠(yuǎn)程對象。如果注冊表發(fā)現(xiàn)被請求的遠(yuǎn)程對象,它將生成一個(gè)對該遠(yuǎn)程對象的遠(yuǎn)程引用,并將其返回給客戶端,客戶端則基于遠(yuǎn)程引用生成相應(yīng)的 Stub 對象,并將引用傳遞給調(diào)用者。之后,雙方就可以按照我們前面講過的方式進(jìn)行交互了。

          注意: RMI 命名服務(wù)提供的 Naming 類并不是你的唯一選擇。RMI 的注冊表可以與其他命名服務(wù)綁定,比如 JNDI,這樣你就可以通過 JNDI 來訪問 RMI 的注冊表了。

          5. 實(shí)戰(zhàn) RMI

          理論離不開實(shí)踐,理解 RMI 的最好辦法就是通過例子。開發(fā) RMI 的分布式對象的大體過程包括如下幾步:

          1. 定義遠(yuǎn)程接口。這一步是通過擴(kuò)展 java.rmi.Remote 接口,并定義所需的業(yè)務(wù)方法實(shí)現(xiàn)的。

          2. 定義遠(yuǎn)程接口的實(shí)現(xiàn)類。即實(shí)現(xiàn)上一步所定義的接口,給出業(yè)務(wù)方法的具體實(shí)現(xiàn)邏輯。

          3. 編譯遠(yuǎn)程接口和實(shí)現(xiàn)類,并通過 RMI 編譯器 rmic 基于實(shí)現(xiàn)類生成所需的 Stub 和 Skeleton 類。

          RMI 中各個(gè)組件之間的關(guān)系如下面這個(gè)示意圖所示:

          回憶我們上一節(jié)所講的,Stub 和 Skeleton 負(fù)責(zé)代理客戶和服務(wù)器之間的通訊。但我們并不需要自己生成它們,相反,RMI 的編譯器 rmic 可以幫我們基于遠(yuǎn)程接口和實(shí)現(xiàn)類生成這些類。當(dāng)客戶端對象通過命名服務(wù)向服務(wù)器端的 RMI 注冊表請求遠(yuǎn)程對象時(shí),RMI 將自動構(gòu)造對應(yīng)遠(yuǎn)程對象的 Skeleton 實(shí)例對象,并通過 Skeleton 對象將遠(yuǎn)程引用返回給客戶端。在客戶端,該遠(yuǎn)程引用將用于構(gòu)造 Stub 類的實(shí)例對象。之后,Stub 對象和 Skeleton 對象就可以代理客戶對象和遠(yuǎn)程對象之間的交互了。

          我們的例子展現(xiàn)了一個(gè)簡單的應(yīng)用場景。服務(wù)器端部署了一個(gè)計(jì)算引擎,負(fù)責(zé)接受來自客戶端的計(jì)算任務(wù),在服務(wù)器端執(zhí)行計(jì)算任務(wù),并將結(jié)果返回給客戶端。客戶端將發(fā)送并調(diào)用計(jì)算引擎的計(jì)算任務(wù)實(shí)際上是計(jì)算指定精度的 π 值。

          重要: 本文的例子改編自 The Java? Tutorial Trail:RMI。所有權(quán)利屬于相應(yīng)的所有人。

          6. 定義遠(yuǎn)程接口

          定義遠(yuǎn)程接口與非分布式應(yīng)用中定義接口的方法沒有太多的區(qū)別。只要遵守下面兩個(gè)要求:

          • 遠(yuǎn)程接口必須直接或者間接的擴(kuò)展自 java.rmi.Remote 接口。遠(yuǎn)程接口還可以在擴(kuò)展該接口的基礎(chǔ)上,同時(shí)擴(kuò)展其它接口,只要被擴(kuò)展的接口的所有方法與遠(yuǎn)程接口的所有方法一樣滿足下一個(gè)要求。

          • 在遠(yuǎn)程接口或者其超接口(Super-interface)中聲明的方法必須滿足下列對遠(yuǎn)程方法的要求:

            • 遠(yuǎn)程方法必須聲明拋出 java.rmi.RemoteException 異常,或者該異常的超類(Superclass),比如 java.io.IOException 或者 java.lang.Exception 異常。在此基礎(chǔ)上,遠(yuǎn)程方法可以聲明拋出應(yīng)用特定的其它異常。

            • 在遠(yuǎn)程方法聲明中,作為參數(shù)或者返回值的遠(yuǎn)程對象,或者包含在其它非遠(yuǎn)程對象中的遠(yuǎn)程對象,必須聲明為其對應(yīng)的遠(yuǎn)程接口,而不是實(shí)際的實(shí)現(xiàn)類。

          注意: 在 Java 1.2 之前,上面關(guān)于拋出異常的要求更嚴(yán)格,即必須拋出 java.rmi.RemoteExcption,不允許類似 java.io.IOException 這樣的超類。現(xiàn)在之所以放寬了這一要求,是希望可以使定義既可以用于遠(yuǎn)程對象,也可以用于本地對象的接口變得容易一些(想想 EJB 中的本地接口和遠(yuǎn)程接口)。當(dāng)然,這并沒有使問題好多少,你還是必須聲明異常。不過,一種觀點(diǎn)認(rèn)為這不是問題,強(qiáng)制聲明異常可以使開發(fā)人員保持清醒的頭腦,因?yàn)檫h(yuǎn)程對象和本地對象在調(diào)用時(shí)傳參的語意是不同的。本地對象是傳引用,而遠(yuǎn)程對象主要是傳值,這意味對參數(shù)內(nèi)部狀態(tài)的修改產(chǎn)生的結(jié)果是不同的。

          對于第一個(gè)要求,java.rmi.Remote 接口實(shí)際上沒有任何方法,而只是用作標(biāo)記接口。RMI 的運(yùn)行環(huán)境依賴該接口判斷對象是否是遠(yuǎn)程對象。第二個(gè)要求則是因?yàn)榉植际綉?yīng)用可能發(fā)生任何問題,比如網(wǎng)絡(luò)問題等等。

          例 1 列出了我們的遠(yuǎn)程接口定義。該接口只有一個(gè)方法:executeTask() 用以執(zhí)行指定的計(jì)算任務(wù),并返回相應(yīng)的結(jié)果。注意,我們用后綴 Remote 表明接口是遠(yuǎn)程接口。

          例 1. ComputeEngineRemote 遠(yuǎn)程接口

          package rmitutorial;
          
          import java.rmi.Remote;
          import java.rmi.RemoteException;
          
          public interface ComputeEngineRemote extends Remote {
              public Object executeTask(Task task) throws RemoteException;    
          }

          例 2 列出了計(jì)算任務(wù)接口的定義。該接口也只有一個(gè)方法:execute() 用以執(zhí)行實(shí)際的計(jì)算邏輯,并返回結(jié)果。注意,該接口不是遠(yuǎn)程接口,所以沒有擴(kuò)展 java.rmi.Remote 接口;其方法也不必拋出 java.rmi.RemoteException 異常。但是,因?yàn)樗鼘⒂米鬟h(yuǎn)程方法的參數(shù),所以擴(kuò)展了 java.io.Serializable 接口。

          例 2. Task 接口

          package rmitutorial;
          
          import java.io.Serializable;
          
          public interface Task extends Serializable {
              Object execute();
          }

          7. 實(shí)現(xiàn)遠(yuǎn)程接口

          接下來,我們將實(shí)現(xiàn)前面定義的遠(yuǎn)程接口。例 3給出了實(shí)現(xiàn)的源代碼。

          例 3. ComputeEngine 實(shí)現(xiàn)

          package rmitutorial;
          
          import java.rmi.RemoteException;
          import java.rmi.server.UnicastRemoteObject;
          
          public class ComputeEngine extends UnicastRemoteObject 
                  implements ComputeEngineRemote {
              
              public ComputeEngine() throws RemoteException {
                  super();
              }
              
              public Object executeTask(Task task) throws RemoteException {
                  return task.execute();
              }
          }

          ComputeEngine 實(shí)現(xiàn)了之前定義的遠(yuǎn)程接口,同時(shí)繼承自 java.rmi.server.UnicastRemoteObject 超類。UnicastRemoteObject 類是一個(gè)便捷類,它實(shí)現(xiàn)了我們前面所講的基于 TCP/IP 的點(diǎn)對點(diǎn)通訊機(jī)制。遠(yuǎn)程對象都必須從該類擴(kuò)展(除非你想自己實(shí)現(xiàn)幾乎所有 UnicastRemoteObject 的方法)。在我們的實(shí)現(xiàn)類的構(gòu)造函數(shù)中,調(diào)用了超類的構(gòu)造函數(shù)(當(dāng)然,即使你不顯式的調(diào)用這個(gè)構(gòu)建函數(shù),它也一樣會被調(diào)用。這里這樣做,只是為了突出強(qiáng)調(diào)這種調(diào)用而已)。該構(gòu)造函數(shù)的最重要的意義就是調(diào)用 UnicastRemoteObject 類的 exportObject() 方法。導(dǎo)出(Export)對象是指使遠(yuǎn)程對象準(zhǔn)備就緒,可以接受進(jìn)來的調(diào)用的過程。而這個(gè)過程的最重要內(nèi)容就是建立服務(wù)器套接字,監(jiān)聽特定的端口,等待客戶端的調(diào)用請求。

          8. 引導(dǎo)程序

          為了讓客戶程序可以找到我們的遠(yuǎn)程對象,就需要將我們的遠(yuǎn)程對象注冊到 RMI 的注冊表。這個(gè)過程有時(shí)被稱為"引導(dǎo)"過程(Bootstrap)。我們將為此編寫一個(gè)獨(dú)立的引導(dǎo)程序負(fù)責(zé)創(chuàng)建和注冊遠(yuǎn)程對象。例 4 給出了引導(dǎo)程序的源代碼。

          例 4. 引導(dǎo)程序

          package rmitutorial;
          
          import java.rmi.Naming;
          import java.rmi.RMISecurityManager;
          
          public class Bootstrap {
              
              public static void main(String[] args) throws Exception {
                  String name = "ComputeEngine";
                  
                  ComputeEngine engine = new ComputeEngine();
                  System.out.println("ComputerEngine exported");
                  
                  Naming.rebind(name, engine);
                  System.out.println("ComputeEngine bound");
              }
          }
          

          可以看到,我們首先創(chuàng)建了一個(gè)遠(yuǎn)程對象(同時(shí)導(dǎo)出了該對象),之后將該對象綁定到 RMI 注冊表中。Namingrebind() 方法接受一個(gè) URL 形式的名字作綁定之用。其完整格式如下:

          protocol://host:port/object

          其中,協(xié)議(Protocol)默認(rèn)為 rmi;主機(jī)名默認(rèn)為 localhost;端口默認(rèn)為 1099。注意,JDK 中提供的默認(rèn) Naming 實(shí)現(xiàn)只支持 rmi 協(xié)議。在我們的引導(dǎo)程序里面只給出了對象綁定的名字,而其它部分均使用缺省值。

          9. 客戶端程序

          例 5 給出了我們的客戶端程序。該程序接受兩個(gè)參數(shù),分別是遠(yuǎn)程對象所在的主機(jī)地址和希望獲得的 π 值的精度。

          例 5. Client.java

          package rmitutorial;
          
          import java.math.BigDecimal;
          import java.rmi.Naming;
          
          public class Client {
              public static void main(String args[]) throws Exception {
                      String name = "rmi://" + args[0] + "/ComputeEngine";
                      ComputeEngineRemote engineRemote = 
                              (ComputeEngineRemote)Naming.lookup(name);
                      Pi task = new Pi(Integer.parseInt(args[1]));
                      BigDecimal pi = (BigDecimal)(engineRemote.executeTask(task));
                      System.out.println(pi);
              }
          }

          例 6. Pi.java

          package rmitutorial;
          
          import java.math.*;
          
          public class Pi implements Task {
              
              private static final BigDecimal ZERO =
                      BigDecimal.valueOf(0);
              private static final BigDecimal  ONE =
                      BigDecimal.valueOf(1);
              private static final BigDecimal FOUR =
                      BigDecimal.valueOf(4);
              
              private static final int roundingMode =
                      BigDecimal.ROUND_HALF_EVEN;
              
              private int digits;
              
              public Pi(int digits) {
                  this.digits = digits;
              }
              
              public Object execute() {
                  return computePi(digits);
              }
              
              public static BigDecimal computePi(int digits) {
                  int scale = digits + 5;
                  BigDecimal arctan1_5 = arctan(5, scale);
                  BigDecimal arctan1_239 = arctan(239, scale);
                  BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                          arctan1_239).multiply(FOUR);
                  return pi.setScale(digits,
                          BigDecimal.ROUND_HALF_UP);
              }
          
              public static BigDecimal arctan(int inverseX,
                      int scale) {
                  BigDecimal result, numer, term;
                  BigDecimal invX = BigDecimal.valueOf(inverseX);
                  BigDecimal invX2 =
                          BigDecimal.valueOf(inverseX * inverseX);
                  
                  numer = ONE.divide(invX, scale, roundingMode);
                  
                  result = numer;
                  int i = 1;
                  do {
                      numer =
                              numer.divide(invX2, scale, roundingMode);
                      int denom = 2 * i + 1;
                      term =
                              numer.divide(BigDecimal.valueOf(denom),
                              scale, roundingMode);
                      if ((i % 2) != 0) {
                          result = result.subtract(term);
                      } else {
                          result = result.add(term);
                      }
                      i++;
                  } while (term.compareTo(ZERO) != 0);
                  return result;
              }
          }

          10. 編譯示例程序

          編譯我們的示例程序和編譯其它非分布式的應(yīng)用沒什么區(qū)別。只是編譯之后,需要使用 RMI 編譯器,即 rmic 生成所需 Stub 和 Skeleton 實(shí)現(xiàn)。使用 rmic 的方式是將我們的遠(yuǎn)程對象的實(shí)現(xiàn)類(不是遠(yuǎn)程接口)的全類名作為參數(shù)來運(yùn)行 rmic 命令。參考下面的示例:

          E:\classes\rmic rmitutorial.ComputeEngine

          編譯之后將生成 rmitutorial.ComputeEngine_Skelrmitutorial.ComputeEngine_Stub 兩個(gè)類。

          11. 運(yùn)行示例程序

          遠(yuǎn)程對象的引用通常是通過 RMI 的注冊表服務(wù)以及 java.rmi.Naming 接口獲得的。遠(yuǎn)程對象需要導(dǎo)出(注冊)相應(yīng)的遠(yuǎn)程引用到注冊表服務(wù),之后注冊表服務(wù)就可以監(jiān)聽并服務(wù)于客戶端對遠(yuǎn)程對象引用的請求。標(biāo)準(zhǔn)的 Sun Java SDK 提供了一個(gè)簡單的 RMI 注冊表服務(wù)程序,即 rmiregistry 用于監(jiān)聽特定的端口,等待遠(yuǎn)程對象的注冊,以及客戶端對這些遠(yuǎn)程對象引用的檢索請求。

          在運(yùn)行我們的示例程序之前,首先要啟動 RMI 的注冊表服務(wù)。這個(gè)過程很簡單,只要直接運(yùn)行 rmiregistry 命令即可。缺省的情況下,該服務(wù)將監(jiān)聽 1099 端口。如果需要指定其它的監(jiān)聽端口,可以在命令行指定希望監(jiān)聽的端口(如果你指定了其它端口,需要修改示例程序以適應(yīng)環(huán)境)。如果希望該程序在后臺運(yùn)行,在 Unix 上可以以如下方式運(yùn)行(當(dāng)然,可以缺省端口參數(shù)):

          $ rmiregistry 1099 &

          在 Windows 操作系統(tǒng)中可以這樣運(yùn)行:

          C:\> start rmiregistry 1099

          我們的 rmitutorial.Bootstrap 類將用于啟動遠(yuǎn)程對象,并將其綁定在 RMI 注冊表中。運(yùn)行該類后,遠(yuǎn)程對象也將進(jìn)入監(jiān)聽狀態(tài),等待來自客戶端的方法調(diào)用請求。

          $ java rmitutorial.Bootstrap
          ComputeEngine exported
          ComputeEngine bound

          啟動遠(yuǎn)程對象后,打開另一個(gè)命令行窗口,運(yùn)行客戶端。命令行的第一個(gè)參數(shù)為 RMI 注冊表的地址,第二個(gè)參數(shù)為期望的 π 值精度。參考下面的示例:

          $ java rmitutorial.Client localhost 50
          3.14159265358979323846264338327950288419716939937511

          12. 其它信息

          在演示示例程序時(shí),我們實(shí)際上是在同一主機(jī)上運(yùn)行的服務(wù)器和客戶端,并且無論是服務(wù)器和客戶端所需的類都在相同的類路徑上,可以同時(shí)被服務(wù)器和客戶端所訪問。這忽略了 Java RMI 的一個(gè)重要細(xì)節(jié),即動態(tài)類裝載。因?yàn)?RMI 的特性(包括其它幾個(gè)特性)并不適用于 J2EE 的 RMI-IIOP 和 EJB 技術(shù),所以,本文將不作詳細(xì)介紹,請讀者自行參考本文給出的參考資料。不過,為了讓好奇的讀者不至于過分失望,這里簡單介紹一下動態(tài)類裝載的基本思想。

          RMI 運(yùn)行時(shí)系統(tǒng)采用動態(tài)類裝載機(jī)制來裝載分布式應(yīng)用所需的類。如果你可以直接訪問應(yīng)用所涉及的所有包括服務(wù)器端客戶端在內(nèi)的主機(jī),并且可以把分布式應(yīng)用所需的所有類都安裝在每個(gè)主機(jī)的 CLASSPATH 中(上面的示例就是極端情況,所有的東西都在本地主機(jī)),那么你完全不必關(guān)心 RMI 類裝載的細(xì)節(jié)。顯然,既然是分布式應(yīng)用,情況往往正相反。對于 RMI 應(yīng)用,客戶端需要裝載客戶端自身所需的類,將要調(diào)用的遠(yuǎn)程對象的遠(yuǎn)程接口類以及對應(yīng)的 Stub 類;服務(wù)器端則要裝載遠(yuǎn)程對象的實(shí)現(xiàn)類以及對應(yīng)的 Skeleton 類(Java 1.2 之后不需要 Skeleton 類)。RMI 在處理遠(yuǎn)程調(diào)用涉及的遠(yuǎn)程引用,參數(shù)以及返回值時(shí),可以將一個(gè)指定的 URL 編碼到流中。交互的另一端可以通過 該 URL 獲得處理這些對象所需的類文件。這一點(diǎn)類似于 Applet 中的 CODEBASE 的概念,交互的兩端通過 HTTP 服務(wù)器發(fā)布各自控制的類,允許交互的另一端動態(tài)下載這些類。以我們的示例為例,客戶端不必部署 ComputeEngine_Stub 的類文件,而可以通過服務(wù)器端的 HTTP 服務(wù)器獲得類文件。同樣,服務(wù)器端也不需要客戶端實(shí)現(xiàn)的定制任務(wù) Pi 的類文件。

          注意,這種動態(tài)類裝載將需要交互的兩端加載定制的安全管理器(參見 java.rmi.RMISecurityManager API),以及對應(yīng)的策略文件。

          13. 參考資料

          • The Java? Tutorial Trail:RMI

          • David Flanagan, Jim Farley, William Crawford and Kris Magnusson, 1999, ISBN 1-56592-483-5E, O'Reilly, Java? Enterprise in a Nutshell

          • Ed Roman, Scott Ambler and Tyler Jewell 2002, ISBN 0-471-41711-4, John Wiley &Sons, Inc., Matering Enterprise JavaBeans? , Second Edition

          posted @ 2006-10-11 09:51 boddi 閱讀(2123) | 評論 (1)編輯 收藏

          正則表達(dá)式(轉(zhuǎn)載)

          關(guān)鍵詞正則表達(dá)式 ?? 模式匹配 ?? Javascript ?? ??????????????????????????????????????

          關(guān)鍵字:正則表達(dá)式 ?模式匹配 Javascript

          摘要:收集一些常用的正則表達(dá)式。

          正則表達(dá)式用于字符串處理,表單驗(yàn)證等場合,實(shí)用高效,但用到時(shí)總是不太把握,以致往往要上網(wǎng)查一番。我將一些常用的表達(dá)式收藏在這里,作備忘之用。本貼隨時(shí)會更新。

          匹配中文字符的正則表達(dá)式: [\u4e00-\u9fa5]

          匹配雙字節(jié)字符(包括漢字在內(nèi)):[^\x00-\xff]

          應(yīng)用:計(jì)算字符串的長度(一個(gè)雙字節(jié)字符長度計(jì)2,ASCII字符計(jì)1)

          String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;}

          匹配空行的正則表達(dá)式:\n[\s| ]*\r

          匹配HTML標(biāo)記的正則表達(dá)式:/<(.*)>.*<\/\1>|<(.*) \/>/

          匹配首尾空格的正則表達(dá)式:(^\s*)|(\s*$)

          String.prototype.trim = function()
          {
          ??? return this.replace(/(^\s*)|(\s*$)/g, "");
          }

          利用正則表達(dá)式分解和轉(zhuǎn)換IP地址:

          下面是利用正則表達(dá)式匹配IP地址,并將IP地址轉(zhuǎn)換成對應(yīng)數(shù)值的Javascript程序:

          function IP2V(ip)
          {
          ?re=/(\d+)\.(\d+)\.(\d+)\.(\d+)/g? //匹配IP地址的正則表達(dá)式
          if(re.test(ip))
          {
          return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
          }
          else
          {
          ?throw new Error("Not a valid IP address!")
          }
          }

          不過上面的程序如果不用正則表達(dá)式,而直接用split函數(shù)來分解可能更簡單,程序如下:

          var ip="10.100.20.168"
          ip=ip.split(".")
          alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

          匹配Email地址的正則表達(dá)式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

          匹配網(wǎng)址URL的正則表達(dá)式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

          利用正則表達(dá)式去除字串中重復(fù)的字符的算法程序:[注:此程序不正確,原因見本貼回復(fù)]

          var s="abacabefgeeii"
          var s1=s.replace(/(.).*\1/g,"$1")
          var re=new RegExp("["+s1+"]","g")
          var s2=s.replace(re,"")
          alert(s1+s2)? //結(jié)果為:abcefgi

          我原來在CSDN上發(fā)貼尋求一個(gè)表達(dá)式來實(shí)現(xiàn)去除重復(fù)字符的方法,最終沒有找到,這是我能想到的最簡單的實(shí)現(xiàn)方法。思路是使用后向引用取出包括重復(fù)的字符,再以重復(fù)的字符建立第二個(gè)表達(dá)式,取到不重復(fù)的字符,兩者串連。這個(gè)方法對于字符順序有要求的字符串可能不適用。

          得用正則表達(dá)式從URL地址中提取文件名的javascript程序,如下結(jié)果為page1

          s="http://www.9499.net/page1.htm"
          s=s.replace(/(.*\/){0,}([^\.]+).*/ig,"$2")
          alert(s)

          利用正則表達(dá)式限制網(wǎng)頁表單里的文本框輸入內(nèi)容:

          用正則表達(dá)式限制只能輸入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))"

          用正則表達(dá)式限制只能輸入全角字符:?onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))"

          用正則表達(dá)式限制只能輸入數(shù)字:onkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

          用正則表達(dá)式限制只能輸入數(shù)字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

          posted @ 2006-09-13 16:28 boddi 閱讀(364) | 評論 (0)編輯 收藏

               摘要: javascript小技巧 事件源對象 event.srcElement.tagName event.srcElement.type 捕獲釋放 event.srcElement.setCapture();? event...  閱讀全文

          posted @ 2006-09-11 13:35 boddi 閱讀(791) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 霸州市| 祁阳县| 布尔津县| 潜山县| 石楼县| 灌阳县| 敦煌市| 大同市| 青铜峡市| 福安市| 高密市| 兴城市| 蒙自县| 墨竹工卡县| 临武县| 望江县| 体育| 临澧县| 舒城县| 永善县| 哈尔滨市| 崇礼县| 琼海市| 盘锦市| 军事| 罗山县| 鲜城| 莱州市| 嘉义县| 富锦市| 板桥市| 克东县| 介休市| 淮安市| 涞源县| 泾源县| 宜宾市| 青岛市| 沛县| 河津市| 延川县|