|
2006年9月9日
何去何從?有點迷茫,是沉寂在溫柔之鄉還是去迎接狂風暴雨! 看來自己還是沒有自信!bs..........myself
2007最新騙局:
?????建行一同志轉送:?今天經過一棟大樓門口,門口有一提款機。有一個老伯,一直?
看著我走過他身邊,突然叫住我。他說他不識字,拿一張提款卡要我幫他在大樓門口?
的自動提款機取錢。我回答我無法幫你取,叫警衛幫你。結果,他就回答我說不用了?
,繼續找其他路人幫他取錢。朋友們要記住---取款機可是有攝影機耶。萬一他說我搶?
劫或是偷他的提款卡,甚至他的卡片是偷來的,幫他領錢會在提款機留下影像,絕對?
會讓你百口莫辯!我會警惕?!?是因為已有同事上當,目前仍官司纏身。顯然這是詐騙?
集團在找替身了!?請用力傳出去~~~?騙案真是層出不窮,一不小心就會踏入陷阱,真是?
令人防不勝防!
提醒各位朋友在外多小心!?
(2)一業主,家中突然斷電,看到窗戶外別人家里都有電,就出門查看自家電?
表箱,打開門就被刀子頂著了——持刀入室搶劫傷人家里突然斷電,不要貿然就開門?
查看,有貓眼的多觀察一會門外動靜,沒貓眼的也隔著門靜聽一段時間,沒有異常響?
動再開門。?
(3)各位女同胞們注意了!這是最新騙局?
女同胞請注意?男同胞請叫自己的朋友注意?新出的情況,女性朋友要特別注意啦:?
一位上班的小姐在下班回家的路上看到一個小孩子一直哭,很可憐?,然后就過去問那?
小朋友怎么了.小朋友就跟那個小姐說:?我迷路了,可以請你帶我回家嗎?然后拿一張?
紙條給她看,?說那是他家地址.然后她就笨笨的帶小孩子去了.一般人都有同情心,然后?
帶到那個所謂小孩子的家里以后,她一按鈴,門鈴像是有高壓電,就失去知覺了.隔天醒?
來就被脫光光在一間空屋里,身邊什么都沒有了,她甚至連犯人長啥樣子都沒看見.所以?
,現在人犯案都是利用同情心啊,如果遇到類似這種的,千萬別帶他去,要帶就帶他到派?
出所去好了,走丟的小孩放到派出所一定沒錯啦,請通知身邊所有女性,為了廣大女士?
的安全,看完后麻煩給轉發給所有人。?
(4)今天遇到討飯新招,大家注意提防了~~?
今天在家休息,有人按門鈴,開門一看,是個50來歲的老婦女,手里拿了2包喜糖,我?
還以為是鄰居來分喜糖的,結果一開口,聽得出不是本地人,她說什么這2包糖給我們?
的,圖個喜氣,要換一點錢給她,后面還說了一大堆不知道什么,我也沒聽清楚,感?
覺就是不對,嚇的馬上關門,暈!這年頭,還有這么討錢的一剛。?
(5)轉發:大家注意了!到自動取款機取錢時一定要倍加小心!!!!!?
昨晚在工行自動取款機取錢時,后面來了個老婦女,問我能不能取錢,還說?
什么取款機有個鍵可能壞了,旁邊不知什么時候來了個小女孩,一直想我身邊躋,我?
也沒在意,小孩子淘氣嘛,可是過分的是她竟然把手朝出鈔口放,準備拿我的錢了,?
我感覺不對勁了,立即把她推到一邊,等著把錢取出來。之后我想了一下,她們倆給?
我設了個套:老婦女負責和我瞎聊,吸引我的注意力,小女孩趁我不注意時搶走我的?
錢!如果我不防備的話,錢說不定就被搶走了,這樣的話,我就進套了:(一則我立?
即去追小女孩,去追回我的錢,可是誰又會相信一個小女孩能搶我一個大人的錢呢??
更可怕的是站在我后面的老婦女將會取光我卡中所有的錢,因為我的卡還在取款機里?
面;二則我不立即去追小女孩,等拿到卡再追,到那時小女孩就無影無蹤了,錢也就?
沒了啊:(她們真的很聰明,很可恥的!!!)?
這是我的親身經歷,希望大家以后取錢時一定要警惕起來,注意觀察周圍的所有人,?
并轉告周圍的家人、同事、朋友,讓壞蛋分子沒有可乘之機!!!!又出現騙局新招?
!!?
(6)我父母都退休在家。昨天上午,來一陌生中年人,說自己摩托車油開沒了,加油?
站太遠,摩托車又太重推不動,所以想問我父母要一個可樂瓶去買汽油,剛開口就說?
實在不行就出2、3元買一個空瓶好了。我母親就拿了個空瓶給他,別說他還真從口袋?
里掏出錢來,不過是幾張百元大鈔,還讓我父母找錢。我母親頓生警覺,說算了,不?
過是一個空瓶而已。他10元錢買下來,只不過還是那張百元大鈔。好在我母?
親尚未龍鐘,也不是那種愛貪小便宜的人?
女性朋友一定要認真看完,注意自我安全啊,現在萬惡的社會。。。。朋友發給我一?
篇報道,現轉給各位看看?,出門在外,千萬小心,小心千萬。。。?
(7)一對新婚夫婦到巴黎度蜜月。在巴黎,妻子在一間時尚服裝店試衣服?,身為丈夫?
就在試衣間外等候。但等候多時卻不見妻子走出來?,緊張的丈夫要求店員幫忙到里頭?
查看?,卻意外發現試衣間空無一人。丈夫以為妻子開玩笑作弄人?,要他緊張.于是回到?
酒店等她回來。幾小時后卻不見妻子的蹤影,才知事態嚴重。丈夫趕忙報警?,并到巴黎?
所有服裝店和醫院詢問妻子下落。三星期過去了,妻子猶如從人間蒸發,音訊全無,傷?
心的丈夫只能收拾包袱回到?**?。由于無法從絕望中振作,丈夫無心工作,甚至獨自生?
活?,決定把自己放逐,流浪到各地方。幾年后?,他心血來潮到巴厘島,在一破舊的屋子?
參觀一畸形秀?(?freak?show?)?。他見到一臟生銹的鐵籠里,有一女人四肢全無,身軀,?
包括臉部,猶如破布般殘破?,充滿疤痕。她在地上扭曲著?,并發出有如野獸般的呻吟聲?
。突然間男人驚恐地發出尖叫聲。他從那毫無人樣的女人臉上見到,他再熟悉不過,屬?
于他新婚不久就告失蹤的妻子臉上的紅色胎記。?
(8)另一版本則發生在上海。幾年前一女通知公安她的表妹在上海市集購物時無故失?
蹤,可是遍尋不著?,直到五年后一友人撞見這表妹在泰國曼谷街道上行乞。恐怖的是她?
不知何故沒了雙手雙腳,身子被鐵鏈綁在燈柱旁。?
(9)這是在某一對夫婦去香港游玩時發生的故事。一對夫妻不知不覺走入了全香港治?
安最壞的地區的一家精品店里???妻子對店里的衣服樣式十分喜歡?,隨后就進入試衣間?
試衣。可是,先生在外頭等了又等,卻不見妻子出來。由于實在是等太久了?,所以先生?
開門?進去找她,可是試衣間里早已空空如也。他吃驚地向店員詢問妻子到哪里去了,可?
是店員們卻好象是串通好了一樣,都說沒有看見,并堅持根本沒有象他妻子這樣的人來?
過店里。因此他只好請當地的警察協助搜索這家精品店?,可是卻一無所獲。后來他又?
一個人找了一段時間?,直到他的簽證到期。最后不得已他就在找不到妻子的情況下回?
國了。之后經過了一年?…?他向公司請了一段長假,再一次回到香港去找他的妻子。他?
帶著妻子的相片走遍香港的大街小巷,但這次仍是一點線索也沒有。終于假期就要結束?
了,他身心疲憊地開始考慮要回國的時候?,有一天無意間經過了一間珍奇小屋。小屋的?
看板上寫著?:?達磨(不倒翁)?雖然他對珍奇事物并不感興趣?,但由于連日疲勞他想?
讓自己改變一下心情?,?加上看板上寫著?達磨的文字也引起他的興趣。最后他決定?
進去瞧瞧。但是他不該進去的!因為珍奇小屋里面展出一件令他慘不忍睹的東?…?小屋?
里的舞臺上有一位手腳都被切斷的全裸女性被當成花瓶一樣擺在那里?!?這位女性的舌?
頭已經被拔掉了,不斷發出奇怪的呻吟聲。看到這么惡心的東西真令他恨不得馬上拔腿?
就跑?,但不知為什么他心里感受到一股奇怪的氣氛?,于是他又重新仔細看那女人的面?
孔?…?沒錯!這女人正是他一年前失蹤的妻子。后來,他向當地的黑道支付龐大的贖金?
換取妻子的剩下的軀干。但一切都太遲了,他可憐的妻子早就已經瘋了。現在她還住在?
國內某家醫院,繼續不斷地發出奇怪的呻吟聲?…?
(10)最近有人告訴我,他的朋友在晚上聽到門口有嬰兒在哭,不過當時已很晚了,?
而且她認為這件事很奇怪,于是她打電話給警察。警察告訴她∶?「無論如何,絕對不?
要開?門。」這位女士表示那聲音聽起來象是嬰兒爬到窗戶附近哭,她擔心嬰兒會爬到?
街上,被車子碾過。警察告訴她∶我們已派人前往,無論如何不能開門。警方認為這?
是一個連續殺人犯,利用嬰兒哭聲的錄音帶,誘使女性以為有人在外面遺棄嬰兒,她?
們出門察看。雖然尚未證實此事,但是警方已接到許多女性打電話來說,他們晚上獨?
自在家時,聽到門外有嬰兒的哭聲,請將這個消息傳給其他人,不要因為聽到嬰兒的?
哭聲而開門。?
請嚴肅看待這貼子!有這么離譜!小心為妙!!!?
以前聽說大活人現場失蹤,后來被賣到馬戲團、被賣器官什么的,只當是天方夜譚。?
結果真的看到發生在真實中的恐怖故事。?
(11)事情是同事群發郵件告知的。她的朋友,簡稱小a吧,上周和兩個女孩子,簡稱?
小b和小c,去逛羅湖商業城。羅湖商業城是深圳假貨集散地,龍蛇混雜,緊靠深圳火?
車站和香港的羅湖口岸,人流量非常大。話說小c內急,就去上衛生間,小a和小b在洗?
手間外面等。等了很久很久,還是不見小c出來,兩個人有點奇怪了。于是兩個人進去?
催她。誰知道進去一看,人影全無。兩個人倒豎一口冷氣,打手機也沒人接。一個大?
活人,難道就這么活不見人死不見尸的失蹤了?于是趕緊報警。?警察來了,問情事情?
經過以后,說了一句令人無比毛骨悚然的話,“你們有沒有看見其他可疑的人進去??
”,兩個人再三回憶,沒有。因為不可能帶著一個活生生的100多斤的人出來,而她們?
不注意。這時候小a突然想起來,其間有個清潔工打扮的人推著一輛清潔小車進去、接?
著又出來……?警察告訴他們,這種事情已經不是第一次發生,現在深圳警方初步懷疑?
一個犯罪團伙,有組織地在管理疏松的低檔商業城,利用人們,尤其是女性對清潔工?
沒有防范意識的心理,進行有組織地綁架、販賣人體器官犯罪。別忘了,羅湖商業城?
離香港和深圳火車站有多么地近。現在已經幾天過去了,那個可憐的小c姑娘,仍是活?
不見人死不見尸。我的同事說,小a,也就是我同事的朋友,仍在等小c的消息。但是?
很可能,也許如果幸運的話,活著的小c會被扔在哪個角落,只是失去了她的腎,但是?
,更有可能的是,也許再過幾天或者幾個月、幾年,小c的頭顱和軀體、四肢會在深圳?
的城鄉結合部的垃圾堆被人發現。如果看到這個發生在身邊的活生生的恐怖事件,請?
轉告身邊的女性親友,一定小心防范清潔工打扮的人,因為他/她很可能會趁你不注意?
把你敲暈,放進清潔車拉走,接下來等著你的是無比恐怖的活人分尸。?
遠程方法調用入門指南(Java RMI Tutorial)
遠程方法調用入門指南
Copyright ? 2005 Stephen Suen. All rights reserved.
Java 遠程方法調用(Remote Method Invocation, RMI)使得運行在一個 Java 虛擬機(Java Virtual Machine, JVM)的對象可以調用運行另一個 JVM 之上的其他對象的方法,從而提供了程序間進行遠程通訊的途徑。RMI 是 J2EE 的很多分布式技術的基礎,比如 RMI-IIOP 乃至 EJB。本文是 RMI 的一個入門指南,目的在于幫助讀者快速建立對 Java RMI 的一個感性認識,以便進行更深層次的學習。事實上,如果你了解 RMI 的目的在于更好的理解和學習 EJB,那么本文就再合適不過了。通過本文所了解的 RMI 的知識和技巧,應該足夠服務于這個目的了。
本文的最新版本將發布在程序員咖啡館網站上(建設中)。歡迎訂閱我們的郵件組,以獲得關于本文的正式發布及更新信息。
全文在保證完整性,且保留全部版權聲明(包括上述鏈接)的前提下可以在任意媒體轉載——須保留此標注。
我們知道遠程過程調用(Remote Procedure Call, RPC)可以用于一個進程調用另一個進程(很可能在另一個遠程主機上)中的過程,從而提供了過程的分布能力。Java 的 RMI 則在 RPC 的基礎上向前又邁進了一步,即提供分布式 對象間的通訊,允許我們獲得在遠程進程中的對象(稱為遠程對象)的引用(稱為遠程引用),進而通過引用調用遠程對象的方法,就好像該對象是與你的客戶端代碼同樣運行在本地進程中一樣。RMI 使用了術語"方法"(Method)強調了這種進步,即在分布式基礎上,充分支持面向對象的特性。
RMI 并不是 Java 中支持遠程方法調用的唯一選擇。在 RMI 基礎上發展而來的 RMI-IIOP(Java Remote Method Invocation over the Internet Inter-ORB Protocol),不但繼承了 RMI 的大部分優點,并且可以兼容于 CORBA。J2EE 和 EJB 都要求使用 RMI-IIOP 而不是 RMI。盡管如此,理解 RMI 將大大有助于 RMI-IIOP 的理解。所以,即便你的興趣在 RMI-IIOP 或者 EJB,相信本文也會對你很有幫助。另外,如果你現在就對 API 感興趣,那么可以告訴你,RMI 使用 java.rmi 包,而 RMI-IIOP 則既使用 java.rmi 也使用擴展的 javax.rmi 包。
本文的隨后內容將僅針對 Java RMI。
在學習 RMI 之前,我們需要了解一些基礎知識。首先需要了解所謂的分布式對象(Distributed Object)。分布式對象是指一個對象可以被遠程系統所調用。對于 Java 而言,即對象不僅可以被同一虛擬機中的其他客戶程序(Client)調用,也可以被運行于其他虛擬機中的客戶程序調用,甚至可以通過網絡被其他遠程主機之上的客戶程序調用。
下面的圖示說明了客戶程序是如何調用分布式對象的:
從圖上我們可以看到,分布式對象被調用的過程是這樣的:
-
客戶程序調用一個被稱為 Stub (有時譯作存根,為了不產生歧義,本文將使用其英文形式)的客戶端代理對象。該代理對象負責對客戶端隱藏網絡通訊的細節。Stub 知道如何通過網絡套接字(Socket)發送調用,包括如何將調用參數轉換為適當的形式以便傳輸等。
-
Stub 通過網絡將調用傳遞到服務器端,也就是分布對象一端的一個被稱為 Skeleton 的代理對象。同樣,該代理對象負責對分布式對象隱藏網絡通訊的細節。Skeleton 知道如何從網絡套接字(Socket)中接受調用,包括如何將調用參數從網絡傳輸形式轉換為 Java 形式等。
-
Skeleton 將調用傳遞給分布式對象。分布式對象執行相應的調用,之后將返回值傳遞給 Skeleton,進而傳遞到 Stub,最終返回給客戶程序。
這個場景基于一個基本的法則,即行為的定義和行為的具體實現相分離。如圖所示,客戶端代理對象 Stub 和分布式對象都實現了相同的接口,該接口稱為遠程接口(Remote Interface)。正是該接口定義了行為,而分布式對象本身則提供具體的實現。對于 Java RMI 而言,我們用接口(interface)定義行為,用類(class)定義實現。
RMI 的底層架構由三層構成:
-
首先是 Stub/Skeleton 層。該層提供了客戶程序和服務程序彼此交互的接口。
-
然后是遠程引用(Remote Reference)層。這一層相當于在其之上的 Stub/Skeleton 層和在其之下的傳輸協議層之前的中間件,負責處理遠程對象引用的創建和管理。
-
最后是傳輸協議(Transport Protocol) 層。該層提供了數據協議,用以通過線路傳輸客戶程序和遠程對象間的請求和應答。
這些層之間的交互可以參照下面的示意圖:
和其它分布式對象機制一樣,Java RMI 的客戶程序使用客戶端的 Stub 向遠程對象請求方法調用;服務器對象則通過服務器端的 Skeleton 接受請求。我們深入進去,來看看其中的一些細節。
注意: 事實上,在 Java 1.2 之后,RMI 不再需要 Skeleton 對象,而是通過 Java 的反射機制(Reflection)來完成對服務器端的遠程對象的調用。為了便于說明問題,本文以下內容仍然基于 Skeleton 來講解。
當客戶程序調用 Stub 時,Stub 負責將方法的參數轉換為序列化(Serialized)形式,我們使用一個特殊的術語,即編列(Marshal)來指代這個過程。編列的目的是將這些參數轉換為可移植的形式,從而可以通過網絡傳輸到遠程的服務對象一端。不幸的是,這個過程沒有想象中那么簡單。這里我們首先要理解一個經典的問題,即方法調用時,參數究竟是傳值還是傳引用呢?對于 Java RMI 來說,存在四種情況,我們將分別加以說明。
-
對于基本的原始類型(整型,字符型等等),將被自動的序列化,以傳值的方式編列。
-
對于 Java 的對象,如果該對象是可序列化的(實現了 java.io.Serializable 接口),則通過 Java 序列化機制自動地加以序列化,以傳值的方式編列。對象之中包含的原始類型以及所有被該對象引用,且沒有聲明為 transient 的對象也將自動的序列化。當然,這些被引用的對象也必須是可序列化的。
-
絕大多數內建的 Java 對象都是可序列化的。 對于不可序列化的 Java 對象(java.io.File 最典型),或者對象中包含對不可序列化,且沒有聲明為 transient 的其它對象的引用。則編列過程將向客戶程序拋出異常,而宣告失敗。
-
客戶程序可以調用遠程對象,沒有理由禁止調用參數本身也是遠程對象(實現了 java.rmi.Remote 接口的類的實例)。此時,RMI 采用一種模擬的傳引用方式(當然不是傳統意義的傳引用,因為本地對內存的引用到了遠程變得毫無意義),而不是將參數直接編列復制到遠程。這種情況下,交互的雙方發生的戲劇性變化值得我們注意。參數是遠程對象,意味著該參數對象可以遠程調用。當客戶程序指定遠程對象作為參數調用服務器端遠程對象的方法時,RMI 的運行時機制將向服務器端的遠程對象發送作為參數的遠程對象的一個 Stub 對象。這樣服務器端的遠程對象就可以回調(Callback)這個 Stub 對象的方法,進而調用在客戶端的遠程對象的對應方法。通過這種方法,服務器端的遠程對象就可以修改作為參數的客戶端遠程對象的內部狀態,這正是傳統意義的傳引用所具備的特性。是不是有點暈?這里的關鍵是要明白,在分布式環境中,所謂服務器和客戶端都是相對的。被請求的一方就是服務器,而發出請求的一方就是客戶端。
在調用參數的編列過程成功后,客戶端的遠程引用層從 Stub 那里獲得了編列后的參數以及對服務器端遠程對象的遠程引用(參見 java.rmi.server.RemoteRef API)。該層負責將客戶程序的請求依據底層的 RMI 數據傳輸協議轉換為傳輸層請求。在 RMI 中,有多種的可能的傳輸機制,比如點對點(Point-to-Point)以及廣播(Multicast)等。不過,在當前的 JMI 版本中只支持點對點協議,即遠程引用層將生成唯一的傳輸層請求,發往指定的唯一遠程對象(參見 java.rmi.server.UnicastRemoteObject API)。
在服務器端,服務器端的遠程引用層接收傳輸層請求,并將其轉換為對遠程對象的服務器端代理對象 Skeleton 的調用。Skeleton 對象負責將請求轉換為對實際的遠程對象的方法調用。這是通過與編列過程相對的反編列(Unmarshal)過程實現的。所有序列化的參數被轉換為 Java 形式,其中作為參數的遠程對象(實際上發送的是遠程引用)被轉換為服務器端本地的 Stub 對象。
如果方法調用有返回值或者拋出異常,則 Skeleton 負責編列返回值或者異常,通過服務器端的遠程引用層,經傳輸層傳遞給客戶端;相應地,客戶端的遠程引用層和 Stub 負責反編列并最終將結果返回給客戶程序。
整個過程中,可能最讓人迷惑的是遠程引用層。這里只要明白,本地的 Stub 對象是如何產生的,就不難理解遠程引用的意義所在了。遠程引用中包含了其所指向的遠程對象的信息,該遠程引用將用于構造作為本地代理對象的 Stub 對象。構造后,Stub 對象內部將維護該遠程引用。真正在網絡上傳輸的實際上就是這個遠程引用,而不是 Stub 對象。
在 RMI 的基本架構之上,RMI 提供服務與分布式應用程序的一些對象服務,包括對象的命名/注冊(Naming/Registry)服務,遠程對象激活(Activation)服務以及分布式垃圾收集(Distributed Garbage Collection, DGC)。作為入門指南,本文將指介紹其中的命名/注冊服務,因為它是實戰 RMI 所必備的。其它內容請讀者自行參考其它更加深入的資料。
在前一節中,如果你喜歡刨根問底,可能已經注意到,客戶端要調用遠程對象,是通過其代理對象 Stub 完成的,那么 Stub 最早是從哪里得來的呢?RMI 的命名/注冊服務正是解決這一問題的。當服務器端想向客戶端提供基于 RMI 的服務時,它需要將一個或多個遠程對象注冊到本地的 RMI 注冊表中(參見java.rmi.registry.Registry API)。每個對象在注冊時都被指定一個將來用于客戶程序引用該對象的名稱。客戶程序通過命名服務(參見 java.rmi.Naming API),指定類似 URL 的對象名稱就可以獲得指向遠程對象的遠程引用。在 Naming 中的 lookup() 方法找到遠程對象所在的主機后,它將檢索該主機上的 RMI 注冊表,并請求所需的遠程對象。如果注冊表發現被請求的遠程對象,它將生成一個對該遠程對象的遠程引用,并將其返回給客戶端,客戶端則基于遠程引用生成相應的 Stub 對象,并將引用傳遞給調用者。之后,雙方就可以按照我們前面講過的方式進行交互了。
注意: RMI 命名服務提供的 Naming 類并不是你的唯一選擇。RMI 的注冊表可以與其他命名服務綁定,比如 JNDI,這樣你就可以通過 JNDI 來訪問 RMI 的注冊表了。
理論離不開實踐,理解 RMI 的最好辦法就是通過例子。開發 RMI 的分布式對象的大體過程包括如下幾步:
-
定義遠程接口。這一步是通過擴展 java.rmi.Remote 接口,并定義所需的業務方法實現的。
-
定義遠程接口的實現類。即實現上一步所定義的接口,給出業務方法的具體實現邏輯。
-
編譯遠程接口和實現類,并通過 RMI 編譯器 rmic 基于實現類生成所需的 Stub 和 Skeleton 類。
RMI 中各個組件之間的關系如下面這個示意圖所示:
回憶我們上一節所講的,Stub 和 Skeleton 負責代理客戶和服務器之間的通訊。但我們并不需要自己生成它們,相反,RMI 的編譯器 rmic 可以幫我們基于遠程接口和實現類生成這些類。當客戶端對象通過命名服務向服務器端的 RMI 注冊表請求遠程對象時,RMI 將自動構造對應遠程對象的 Skeleton 實例對象,并通過 Skeleton 對象將遠程引用返回給客戶端。在客戶端,該遠程引用將用于構造 Stub 類的實例對象。之后,Stub 對象和 Skeleton 對象就可以代理客戶對象和遠程對象之間的交互了。
我們的例子展現了一個簡單的應用場景。服務器端部署了一個計算引擎,負責接受來自客戶端的計算任務,在服務器端執行計算任務,并將結果返回給客戶端。客戶端將發送并調用計算引擎的計算任務實際上是計算指定精度的 π 值。
定義遠程接口與非分布式應用中定義接口的方法沒有太多的區別。只要遵守下面兩個要求:
注意: 在 Java 1.2 之前,上面關于拋出異常的要求更嚴格,即必須拋出 java.rmi.RemoteExcption,不允許類似 java.io.IOException 這樣的超類。現在之所以放寬了這一要求,是希望可以使定義既可以用于遠程對象,也可以用于本地對象的接口變得容易一些(想想 EJB 中的本地接口和遠程接口)。當然,這并沒有使問題好多少,你還是必須聲明異常。不過,一種觀點認為這不是問題,強制聲明異常可以使開發人員保持清醒的頭腦,因為遠程對象和本地對象在調用時傳參的語意是不同的。本地對象是傳引用,而遠程對象主要是傳值,這意味對參數內部狀態的修改產生的結果是不同的。
對于第一個要求,java.rmi.Remote 接口實際上沒有任何方法,而只是用作標記接口。RMI 的運行環境依賴該接口判斷對象是否是遠程對象。第二個要求則是因為分布式應用可能發生任何問題,比如網絡問題等等。
例 1
列出了我們的遠程接口定義。該接口只有一個方法:executeTask() 用以執行指定的計算任務,并返回相應的結果。注意,我們用后綴 Remote 表明接口是遠程接口。
例 1. ComputeEngineRemote 遠程接口
package rmitutorial;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ComputeEngineRemote extends Remote {
public Object executeTask(Task task) throws RemoteException;
}
例 2
列出了計算任務接口的定義。該接口也只有一個方法:execute() 用以執行實際的計算邏輯,并返回結果。注意,該接口不是遠程接口,所以沒有擴展 java.rmi.Remote 接口;其方法也不必拋出 java.rmi.RemoteException 異常。但是,因為它將用作遠程方法的參數,所以擴展了 java.io.Serializable 接口。
例 2. Task 接口
package rmitutorial;
import java.io.Serializable;
public interface Task extends Serializable {
Object execute();
}
接下來,我們將實現前面定義的遠程接口。例 3給出了實現的源代碼。
例 3. ComputeEngine 實現
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 實現了之前定義的遠程接口,同時繼承自 java.rmi.server.UnicastRemoteObject 超類。UnicastRemoteObject 類是一個便捷類,它實現了我們前面所講的基于 TCP/IP 的點對點通訊機制。遠程對象都必須從該類擴展(除非你想自己實現幾乎所有 UnicastRemoteObject 的方法)。在我們的實現類的構造函數中,調用了超類的構造函數(當然,即使你不顯式的調用這個構建函數,它也一樣會被調用。這里這樣做,只是為了突出強調這種調用而已)。該構造函數的最重要的意義就是調用 UnicastRemoteObject 類的 exportObject() 方法。導出(Export)對象是指使遠程對象準備就緒,可以接受進來的調用的過程。而這個過程的最重要內容就是建立服務器套接字,監聽特定的端口,等待客戶端的調用請求。
為了讓客戶程序可以找到我們的遠程對象,就需要將我們的遠程對象注冊到 RMI 的注冊表。這個過程有時被稱為"引導"過程(Bootstrap)。我們將為此編寫一個獨立的引導程序負責創建和注冊遠程對象。例 4 給出了引導程序的源代碼。
例 4. 引導程序
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");
}
}
可以看到,我們首先創建了一個遠程對象(同時導出了該對象),之后將該對象綁定到 RMI 注冊表中。Naming 的 rebind() 方法接受一個 URL 形式的名字作綁定之用。其完整格式如下:
protocol://host:port/object
其中,協議(Protocol)默認為 rmi;主機名默認為 localhost;端口默認為 1099。注意,JDK 中提供的默認 Naming 實現只支持 rmi 協議。在我們的引導程序里面只給出了對象綁定的名字,而其它部分均使用缺省值。
例 5
給出了我們的客戶端程序。該程序接受兩個參數,分別是遠程對象所在的主機地址和希望獲得的 π 值的精度。
例 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;
}
}
編譯我們的示例程序和編譯其它非分布式的應用沒什么區別。只是編譯之后,需要使用 RMI 編譯器,即 rmic 生成所需 Stub 和 Skeleton 實現。使用 rmic 的方式是將我們的遠程對象的實現類(不是遠程接口)的全類名作為參數來運行 rmic 命令。參考下面的示例:
E:\classes\rmic rmitutorial.ComputeEngine
編譯之后將生成 rmitutorial.ComputeEngine_Skel 和 rmitutorial.ComputeEngine_Stub 兩個類。
遠程對象的引用通常是通過 RMI 的注冊表服務以及 java.rmi.Naming 接口獲得的。遠程對象需要導出(注冊)相應的遠程引用到注冊表服務,之后注冊表服務就可以監聽并服務于客戶端對遠程對象引用的請求。標準的 Sun Java SDK 提供了一個簡單的 RMI 注冊表服務程序,即 rmiregistry 用于監聽特定的端口,等待遠程對象的注冊,以及客戶端對這些遠程對象引用的檢索請求。
在運行我們的示例程序之前,首先要啟動 RMI 的注冊表服務。這個過程很簡單,只要直接運行 rmiregistry 命令即可。缺省的情況下,該服務將監聽 1099 端口。如果需要指定其它的監聽端口,可以在命令行指定希望監聽的端口(如果你指定了其它端口,需要修改示例程序以適應環境)。如果希望該程序在后臺運行,在 Unix 上可以以如下方式運行(當然,可以缺省端口參數):
$ rmiregistry 1099 &
在 Windows 操作系統中可以這樣運行:
C:\> start rmiregistry 1099
我們的 rmitutorial.Bootstrap 類將用于啟動遠程對象,并將其綁定在 RMI 注冊表中。運行該類后,遠程對象也將進入監聽狀態,等待來自客戶端的方法調用請求。
$ java rmitutorial.Bootstrap
ComputeEngine exported
ComputeEngine bound
啟動遠程對象后,打開另一個命令行窗口,運行客戶端。命令行的第一個參數為 RMI 注冊表的地址,第二個參數為期望的 π 值精度。參考下面的示例:
$ java rmitutorial.Client localhost 50
3.14159265358979323846264338327950288419716939937511
在演示示例程序時,我們實際上是在同一主機上運行的服務器和客戶端,并且無論是服務器和客戶端所需的類都在相同的類路徑上,可以同時被服務器和客戶端所訪問。這忽略了 Java RMI 的一個重要細節,即動態類裝載。因為 RMI 的特性(包括其它幾個特性)并不適用于 J2EE 的 RMI-IIOP 和 EJB 技術,所以,本文將不作詳細介紹,請讀者自行參考本文給出的參考資料。不過,為了讓好奇的讀者不至于過分失望,這里簡單介紹一下動態類裝載的基本思想。
RMI 運行時系統采用動態類裝載機制來裝載分布式應用所需的類。如果你可以直接訪問應用所涉及的所有包括服務器端客戶端在內的主機,并且可以把分布式應用所需的所有類都安裝在每個主機的 CLASSPATH 中(上面的示例就是極端情況,所有的東西都在本地主機),那么你完全不必關心 RMI 類裝載的細節。顯然,既然是分布式應用,情況往往正相反。對于 RMI 應用,客戶端需要裝載客戶端自身所需的類,將要調用的遠程對象的遠程接口類以及對應的 Stub 類;服務器端則要裝載遠程對象的實現類以及對應的 Skeleton 類(Java 1.2 之后不需要 Skeleton 類)。RMI 在處理遠程調用涉及的遠程引用,參數以及返回值時,可以將一個指定的 URL 編碼到流中。交互的另一端可以通過 該 URL 獲得處理這些對象所需的類文件。這一點類似于 Applet 中的 CODEBASE 的概念,交互的兩端通過 HTTP 服務器發布各自控制的類,允許交互的另一端動態下載這些類。以我們的示例為例,客戶端不必部署 ComputeEngine_Stub 的類文件,而可以通過服務器端的 HTTP 服務器獲得類文件。同樣,服務器端也不需要客戶端實現的定制任務 Pi 的類文件。
注意,這種動態類裝載將需要交互的兩端加載定制的安全管理器(參見 java.rmi.RMISecurityManager API),以及對應的策略文件。
-
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
正則表達式(轉載)
關鍵詞: 正則表達式 ?? 模式匹配 ?? Javascript ?? ??????????????????????????????????????
關鍵字:正則表達式 ?模式匹配 Javascript 摘要:收集一些常用的正則表達式。 正則表達式用于字符串處理,表單驗證等場合,實用高效,但用到時總是不太把握,以致往往要上網查一番。我將一些常用的表達式收藏在這里,作備忘之用。本貼隨時會更新。 匹配中文字符的正則表達式: [\u4e00-\u9fa5] 匹配雙字節字符(包括漢字在內):[^\x00-\xff] 應用:計算字符串的長度(一個雙字節字符長度計2,ASCII字符計1) String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;} 匹配空行的正則表達式:\n[\s| ]*\r 匹配HTML標記的正則表達式:/<(.*)>.*<\/\1>|<(.*) \/>/ 匹配首尾空格的正則表達式:(^\s*)|(\s*$) String.prototype.trim = function() { ??? return this.replace(/(^\s*)|(\s*$)/g, ""); } 利用正則表達式分解和轉換IP地址: 下面是利用正則表達式匹配IP地址,并將IP地址轉換成對應數值的Javascript程序: function IP2V(ip) { ?re=/(\d+)\.(\d+)\.(\d+)\.(\d+)/g? //匹配IP地址的正則表達式 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!") } } 不過上面的程序如果不用正則表達式,而直接用split函數來分解可能更簡單,程序如下: 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地址的正則表達式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 匹配網址URL的正則表達式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)? 利用正則表達式去除字串中重復的字符的算法程序:[注:此程序不正確,原因見本貼回復]
var s="abacabefgeeii" var s1=s.replace(/(.).*\1/g,"$1") var re=new RegExp("["+s1+"]","g") var s2=s.replace(re,"") alert(s1+s2)? //結果為:abcefgi
我原來在CSDN上發貼尋求一個表達式來實現去除重復字符的方法,最終沒有找到,這是我能想到的最簡單的實現方法。思路是使用后向引用取出包括重復的字符,再以重復的字符建立第二個表達式,取到不重復的字符,兩者串連。這個方法對于字符順序有要求的字符串可能不適用。 得用正則表達式從URL地址中提取文件名的javascript程序,如下結果為page1 s="http://www.9499.net/page1.htm" s=s.replace(/(.*\/){0,}([^\.]+).*/ig,"$2") alert(s) 利用正則表達式限制網頁表單里的文本框輸入內容: 用正則表達式限制只能輸入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))" 用正則表達式限制只能輸入全角字符:?onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))" 用正則表達式限制只能輸入數字:onkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))" 用正則表達式限制只能輸入數字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"
摘要: javascript小技巧
事件源對象
event.srcElement.tagName event.srcElement.type
捕獲釋放 event.srcElement.setCapture();? event... 閱讀全文
Java Excel是一開放源碼項目,通過它Java開發人員可以讀取Excel文件的內容、創建新的Excel文件、更新已經存在的Excel文件。使用該API非Windows操作系統也可以通過純Java應用來處理Excel數據表。因為是使用Java編寫的,所以我們在Web應用中可以通過JSP、Servlet來調用API實現對Excel數據表的訪問。
現在發布的穩定版本是V2.0,提供以下功能:
從Excel 95、97、2000等格式的文件中讀取數據; 讀取Excel公式(可以讀取Excel 97以后的公式); 生成Excel數據表(格式為Excel 97); 支持字體、數字、日期的格式化; 支持單元格的陰影操作,以及顏色操作; 修改已經存在的數據表; 現在還不支持以下功能,但不久就會提供了:
不能夠讀取圖表信息; 可以讀,但是不能生成公式,任何類型公式最后的計算值都可以讀出; 應用示例
1 從Excel文件讀取數據表
Java Excel API既可以從本地文件系統的一個文件(.xls),也可以從輸入流中讀取Excel數據表。讀取Excel數據表的第一步是創建Workbook(術語:工作薄),下面的代碼片段舉例說明了應該如何操作:(完整代碼見ExcelReading.java)
import java.io.*; import jxl.*; … … … … try { //構建Workbook對象, 只讀Workbook對象 //直接從本地文件創建Workbook //從輸入流創建Workbook ?? InputStream is = new FileInputStream(sourcefile); ?? jxl.Workbook rwb = Workbook.getWorkbook(is); } catch (Exception e) { e.printStackTrace(); }
一旦創建了Workbook,我們就可以通過它來訪問Excel Sheet(術語:工作表)。參考下面的代碼片段:
//獲取第一張Sheet表 Sheet rs = rwb.getSheet(0);
我們既可能通過Sheet的名稱來訪問它,也可以通過下標來訪問它。如果通過下標來訪問的話,要注意的一點是下標從0開始,就像數組一樣。
一旦得到了Sheet,我們就可以通過它來訪問Excel Cell(術語:單元格)。參考下面的代碼片段:
//獲取第一行,第一列的值 Cell c00 = rs.getCell(0, 0); String strc00 = c00.getContents();
//獲取第一行,第二列的值 Cell c10 = rs.getCell(1, 0); String strc10 = c10.getContents();
//獲取第二行,第二列的值 Cell c11 = rs.getCell(1, 1); String strc11 = c11.getContents();
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType()); System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType()); System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
如果僅僅是取得Cell的值,我們可以方便地通過getContents()方法,它可以將任何類型的Cell值都作為一個字符串返回。示例代碼中Cell(0, 0)是文本型,Cell(1, 0)是數字型,Cell(1,1)是日期型,通過getContents(),三種類型的返回值都是字符型。
如果有需要知道Cell內容的確切類型,API也提供了一系列的方法。參考下面的代碼片段:
String strc00 = null; double strc10 = 0.00; Date strc11 = null;
Cell c00 = rs.getCell(0, 0); Cell c10 = rs.getCell(1, 0); Cell c11 = rs.getCell(1, 1);
if(c00.getType() == CellType.LABEL) { LabelCell labelc00 = (LabelCell)c00; strc00 = labelc00.getString(); } if(c10.getType() == CellType.NUMBER) { NmberCell numc10 = (NumberCell)c10; strc10 = numc10.getValue(); } if(c11.getType() == CellType.DATE) { DateCell datec11 = (DateCell)c11; strc11 = datec11.getDate(); }
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType()); System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType()); System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
在得到Cell對象后,通過getType()方法可以獲得該單元格的類型,然后與API提供的基本類型相匹配,強制轉換成相應的類型,最后調用相應的取值方法getXXX(),就可以得到確定類型的值。API提供了以下基本類型,與Excel的數據格式相對應,如下圖所示:
每種類型的具體意義,請參見Java Excel API Document。
當你完成對Excel電子表格數據的處理后,一定要使用close()方法來關閉先前創建的對象,以釋放讀取數據表的過程中所占用的內存空間,在讀取大量數據時顯得尤為重要。參考如下代碼片段:
//操作完成時,關閉對象,釋放占用的內存空間 rwb.close();
Java Excel API提供了許多訪問Excel數據表的方法,在這里我只簡要地介紹幾個常用的方法,其它的方法請參考附錄中的Java Excel API Document。
Workbook類提供的方法
1. int getNumberOfSheets() 獲得工作薄(Workbook)中工作表(Sheet)的個數,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); int sheets = rwb.getNumberOfSheets();
2. Sheet[] getSheets() 返回工作薄(Workbook)中工作表(Sheet)對象數組,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); Sheet[] sheets = rwb.getSheets();
3. String getVersion() 返回正在使用的API的版本號,好像是沒什么太大的作用。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); String apiVersion = rwb.getVersion();
Sheet接口提供的方法
1) String getName() 獲取Sheet的名稱,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); String sheetName = rs.getName();
2) int getColumns() 獲取Sheet表中所包含的總列數,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); int rsColumns = rs.getColumns();
3) Cell[] getColumn(int column) 獲取某一列的所有單元格,返回的是單元格對象數組,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell[] cell = rs.getColumn(0);
4) int getRows() 獲取Sheet表中所包含的總行數,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); int rsRows = rs.getRows();
5) Cell[] getRow(int row) 獲取某一行的所有單元格,返回的是單元格對象數組,示例子:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell[] cell = rs.getRow(0);
6) Cell getCell(int column, int row) 獲取指定單元格的對象引用,需要注意的是它的兩個參數,第一個是列數,第二個是行數,這與通常的行、列組合有些不同。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell cell = rs.getCell(0, 0);
2 生成新的Excel工作薄
下面的代碼主要是向大家介紹如何生成簡單的Excel工作表,在這里單元格的內容是不帶任何修飾的(如:字體,顏色等等),所有的內容都作為字符串寫入。(完整代碼見ExcelWriting.java)
與讀取Excel工作表相似,首先要使用Workbook類的工廠方法創建一個可寫入的工作薄(Workbook)對象,這里要注意的是,只能通過API提供的工廠方法來創建Workbook,而不能使用WritableWorkbook的構造函數,因為類WritableWorkbook的構造函數為protected類型。示例代碼片段如下:
import java.io.*; import jxl.*; import jxl.write.*; … … … … try { //構建Workbook對象, 只讀Workbook對象 //Method 1:創建可寫入的Excel工作薄 ?? jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
//Method 2:將WritableWorkbook直接寫入到輸出流 /* ?? OutputStream os = new FileOutputStream(targetfile); ?? jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os); */ } catch (Exception e) { e.printStackTrace(); }
API提供了兩種方式來處理可寫入的輸出流,一種是直接生成本地文件,如果文件名不帶全路徑的話,缺省的文件會定位在當前目錄,如果文件名帶有全路徑的話,則生成的Excel文件則會定位在相應的目錄;另外一種是將Excel對象直接寫入到輸出流,例如:用戶通過瀏覽器來訪問Web服務器,如果HTTP頭設置正確的話,瀏覽器自動調用客戶端的Excel應用程序,來顯示動態生成的Excel電子表格。
接下來就是要創建工作表,創建工作表的方法與創建工作薄的方法幾乎一樣,同樣是通過工廠模式方法獲得相應的對象,該方法需要兩個參數,一個是工作表的名稱,另一個是工作表在工作薄中的位置,參考下面的代碼片段:
//創建Excel工作表 jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
"這鍋也支好了,材料也準備齊全了,可以開始下鍋了!",現在要做的只是實例化API所提供的Excel基本數據類型,并將它們添加到工作表中就可以了,參考下面的代碼片段:
//1.添加Label對象 jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell"); ws.addCell(labelC);
//添加帶有字型Formatting的對象 jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true); jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf); jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF); ws.addCell(labelCF);
//添加帶有字體顏色Formatting的對象 jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false, Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED); jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc); jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC); ws.addCell(labelCF);
//2.添加Number對象 jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926); ws.addCell(labelN);
//添加帶有formatting的Number對象 jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##"); jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf); jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN); ws.addCell(labelNF);
//3.添加Boolean對象 jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false); ws.addCell(labelB);
//4.添加DateTime對象 jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date()); ws.addCell(labelDT);
//添加帶有formatting的DateFormat對象 jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss"); jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df); jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF); ws.addCell(labelDTF);
這里有兩點大家要引起大家的注意。第一點,在構造單元格時,單元格在工作表中的位置就已經確定了。一旦創建后,單元格的位置是不能夠變更的,盡管單元格的內容是可以改變的。第二點,單元格的定位是按照下面這樣的規律(column, row),而且下標都是從0開始,例如,A1被存儲在(0, 0),B1被存儲在(1, 0)。
最后,不要忘記關閉打開的Excel工作薄對象,以釋放占用的內存,參見下面的代碼片段:
//寫入Exel工作表 wwb.write();
//關閉Excel工作薄對象 wwb.close();
這可能與讀取Excel文件的操作有少少不同,在關閉Excel對象之前,你必須要先調用write()方法,因為先前的操作都是存儲在緩存中的,所以要通過該方法將操作的內容保存在文件中。如果你先關閉了Excel對象,那么只能得到一張空的工作薄了。
3 拷貝、更新Excel工作薄
接下來簡要介紹一下如何更新一個已經存在的工作薄,主要是下面二步操作,第一步是構造只讀的Excel工作薄,第二步是利用已經創建的Excel工作薄創建新的可寫入的Excel工作薄,參考下面的代碼片段:(完整代碼見ExcelModifying.java)
//創建只讀的Excel工作薄的對象 jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));
//創建可寫入的Excel工作薄對象 jxl.write.WritableWorkbook??wwb = Workbook.createWorkbook(new File(targetfile), rw); ?????????? //讀取第一張工作表 jxl.write.WritableSheet ws = wwb.getSheet(0);
//獲得第一個單元格對象 jxl.write.WritableCell wc = ws.getWritableCell(0, 0); ?????????? //判斷單元格的類型, 做出相應的轉化 if(wc.getType() == CellType.LABEL) { Label l = (Label)wc; ?? l.setString("The value has been modified."); }
//寫入Excel對象 wwb.write();
//關閉可寫入的Excel對象 wwb.close();
//關閉只讀的Excel對象 rw.close();
之所以使用這種方式構建Excel對象,完全是因為效率的原因,因為上面的示例才是API的主要應用。為了提高性能,在讀取工作表時,與數據相關的一些輸出信息,所有的格式信息,如:字體、顏色等等,是不被處理的,因為我們的目的是獲得行數據的值,既使沒有了修飾,也不會對行數據的值產生什么影響。唯一的不利之處就是,在內存中會同時保存兩個同樣的工作表,這樣當工作表體積比較大時,會占用相當大的內存,但現在好像內存的大小并不是什么關鍵因素了。
一旦獲得了可寫入的工作表對象,我們就可以對單元格對象進行更新的操作了,在這里我們不必調用API提供的add()方法,因為單元格已經于工作表當中,所以我們只需要調用相應的setXXX()方法,就可以完成更新的操作了。
盡單元格原有的格式化修飾是不能去掉的,我們還是可以將新的單元格修飾加上去,以使單元格的內容以不同的形式表現。
新生成的工作表對象是可寫入的,我們除了更新原有的單元格外,還可以添加新的單元格到工作表中,這與示例2的操作是完全一樣的。
最后,不要忘記調用write()方法,將更新的內容寫入到文件中,然后關閉工作薄對象,這里有兩個工作薄對象要關閉,一個是只讀的,另外一個是可寫入的。
以上摘自IBM網站
使用JXL讀取Excel表格,拷貝、更新Excel工作薄
|
xymiser 原創? (參與分:41669,專家分:2761)?? 發表:2006-01-18 22:11 ??版本:1.0 ??閱讀:1666次 |
|
/** *?<p>讀取Excel表格,拷貝、更新Excel工作薄?</p> *?<p>Description:?可以讀取Excel文件的內容,更新Excel工作薄 *?</p> *?<p>Copyright:?Copyright?(c)?Corparation?2005</p> *?<p>程序開發環境為eclipse</p> *?@author?Walker *?@version?1.0 */ package?cn.com.yitong.xls;
import?java.io.File; import?java.io.FileInputStream; import?java.io.InputStream; import?java.util.Vector;
import?cn.com.yitong.ChartImg; import?cn.com.yitong.VireObj; import?cn.com.yitong.platform.log.YTLogger;
import?jxl.CellType; import?jxl.Workbook; import?jxl.format.CellFormat; import?jxl.format.Colour; import?jxl.format.UnderlineStyle; import?jxl.write.Formula; import?jxl.write.Label; import?jxl.write.Number; import?jxl.write.WritableCell; import?jxl.write.WritableCellFormat; import?jxl.write.WritableFont; import?jxl.write.WritableImage; import?jxl.write.WritableSheet; import?jxl.write.WritableWorkbook; import?jxl.write.WriteException; import?jxl.write.biff.RowsExceededException;
public?class?XLSDemo { ????private?static?final?int?TITLE_LENGTH?=?7; ????private?static?final?int?SHEET_WIDTH?=?32; ????private?static?final?int?SHEET_HEIGHT?=?116; ???? ????/** ?????*?創建Excel ?????*/ ????private?void?makeXls() ????{ ????????Workbook?workbook?=?null; ????????try ????????{ ????????????//?構建Workbook對象,?只讀Workbook對象 ????????????//?直接從本地文件創建Workbook,?從輸入流創建Workbook ????????????InputStream?ins?=?new?FileInputStream("D:/Workspace/testproj/source.xls"); ????????????workbook?=?Workbook.getWorkbook(ins);
????????????//?利用已經創建的Excel工作薄創建新的可寫入的Excel工作薄 ????????????File?outFile?=?new?File("D:/Workspace/testproj/test.xls"); ????????????WritableWorkbook?wwb?=?Workbook.createWorkbook(outFile,?workbook); ????????????//?讀取第一張工作表 ????????????WritableSheet?dataSheet?=?wwb.getSheet(0); ????????????//??設置凍結單元格 ????????????dataSheet.getSettings().setVerticalFreeze(7); ????????????dataSheet.getSettings().setHorizontalFreeze(2); ???????????? ????????????//?測試模擬數據 ????????????Vector?vecData?=?new?Vector(); ????????????for(int?i?=?0;?i?<?50;?i?++) ????????????{ ????????????????VireObj?obj?=?new?VireObj(); ????????????????obj.setOrgNo("00"?+?i?+?"0"); ????????????????obj.setOrgName("機構"?+?(i?+?1)); ????????????????obj.setOpenAcc((int)(100?*?Math.random())); ????????????????obj.setDestoryAcc((int)(10?*?Math.random())); ????????????????obj.setTotalAcc((int)(500?*?Math.random())); ????????????????obj.setMonthInCount((int)(500?*?Math.random())); ????????????????obj.setMonthInMoney(500?*?Math.random()); ????????????????obj.setMonthOutCount((int)(500?*?Math.random())); ????????????????obj.setMonthOutMoney(500?*?Math.random()); ???????????????? ????????????????vecData.add(obj); ????????????}???????????? ????????????//?插入數據 ????????????insertData(wwb,?dataSheet,?vecData);???????????? ????????????//?插入模擬圖像數據 ????????????Vector?vecImg?=?new?Vector(); ????????????for(int?i?=?0;?i?<?3;?i?++) ????????????{ ????????????????ChartImg?img?=?new?ChartImg(); ????????????????img.setImgTitle("圖像"?+?(i?+?1)); ????????????????img.setImgName("D:/Workspace/testproj/images/barchart.png"); ????????????????vecImg.add(img); ????????????} ????????????//?插入圖表 ????????????insertImgsheet(wwb,?vecImg); ????????????//寫入Excel對象 ????????????wwb.write(); ????????????wwb.close(); ????????}?catch?(Exception?e) ????????{ ????????????YTLogger.logDebug(e); ????????}?finally ????????{ ????????????//?操作完成時,關閉對象,釋放占用的內存空間 ????????????workbook.close(); ????????} ????} ???? ????/** ?????*?插入數據 ?????*?@param?wwb?WritableWorkbook?:?工作簿 ?????*?@param?dataSheet?WritableSheet?:?工作表 ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?void?insertData(WritableWorkbook?wwb,?WritableSheet?dataSheet,?Vector?vecData)?throws?RowsExceededException,?WriteException ????{ ????????//?獲得標題單元格對象???????? ????????modiStrCell(dataSheet,?2,?0,?"工商銀行江蘇省分行?個人網上銀行業務種類/開銷戶明細報表(2005-12)",?null); ????????//?修改數據單元格數據 ????????for(int?i?=?0;?i?<?vecData.size();?i?++) ????????{ ????????????VireObj?obj?=?(VireObj)vecData.get(i); ????????????modiStrCell(dataSheet,?0,?TITLE_LENGTH?+?i,?obj.getOrgNo(),?null); ????????????modiStrCell(dataSheet,?1,?TITLE_LENGTH?+?i,?obj.getOrgName(),?null); ????????????modiNumCell(dataSheet,?2,?TITLE_LENGTH?+?i,?obj.getOpenAcc(),?null); ????????????modiNumCell(dataSheet,?3,?TITLE_LENGTH?+?i,?obj.getDestoryAcc(),?null); ????????????modiNumCell(dataSheet,?4,?TITLE_LENGTH?+?i,?obj.getTotalAcc(),?null); ????????????modiNumCell(dataSheet,?5,?TITLE_LENGTH?+?i,?obj.getMonthInCount(),?null); ????????????modiNumCell(dataSheet,?6,?TITLE_LENGTH?+?i,?obj.getTotalInMoney(),?null); ????????????modiNumCell(dataSheet,?7,?TITLE_LENGTH?+?i,?obj.getMonthOutCount(),?null); ????????????modiNumCell(dataSheet,?8,?TITLE_LENGTH?+?i,?obj.getMonthOutMoney(),?null); ????????}???? ????????//?刪除空行 ????????for?(int?j?=?vecData.size()?+?TITLE_LENGTH;?j?<?SHEET_HEIGHT;?j++) ????????{ ????????????dataSheet.removeRow(vecData.size()?+?TITLE_LENGTH); ????????}???????? ????????//?插入公式 ????????for(int?i?=?2;?i?<?SHEET_WIDTH;?i?++) ????????{ ????????????modiFormulaCell(dataSheet,?i,?vecData.size()?+?TITLE_LENGTH,?8,?vecData.size()?+?TITLE_LENGTH,?null); ????????}???????? ????}
????/** ?????*?修改字符單元格的值 ?????*?@param?dataSheet?WritableSheet?:?工作表 ?????*?@param?col?int?:?列 ?????*?@param?row?int?:?行 ?????*?@param?str?String?:?字符 ?????*?@param?format?CellFormat?:?單元格的樣式 ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?void?modiStrCell(WritableSheet?dataSheet,?int?col,?int?row,?String?str,?CellFormat?format)?throws?RowsExceededException,?WriteException ????{ ????????//?獲得單元格對象 ????????WritableCell?cell?=?dataSheet.getWritableCell(col,?row); ????????//?判斷單元格的類型,?做出相應的轉化 ????????if?(cell.getType()?==?CellType.EMPTY) ????????{ ????????????Label?lbl?=?new?Label(col,?row,?str); ????????????if(null?!=?format) ????????????{ ????????????????lbl.setCellFormat(format); ????????????}?else ????????????{ ????????????????lbl.setCellFormat(cell.getCellFormat()); ????????????} ????????????dataSheet.addCell(lbl); ????????}?else?if?(cell.getType()?==?CellType.LABEL) ????????{ ????????????Label?lbl?=?(Label)cell; ????????????lbl.setString(str); ????????}?else?if?(cell.getType()?==?CellType.NUMBER) ????????{ ????????????//?數字單元格修改 ????????????Number?n1?=?(Number)cell; ????????????n1.setValue(42.05); ????????} ????} ???? ????/** ?????*?修改數字單元格的值 ?????*?@param?dataSheet?WritableSheet?:?工作表 ?????*?@param?col?int?:?列 ?????*?@param?row?int?:?行 ?????*?@param?num?double?:?數值 ?????*?@param?format?CellFormat?:?單元格的樣式 ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?void?modiNumCell(WritableSheet?dataSheet,?int?col,?int?row,?double?num,?CellFormat?format)?throws?RowsExceededException,?WriteException ????{ ????????//?獲得單元格對象 ????????WritableCell?cell?=?dataSheet.getWritableCell(col,?row); ????????//?判斷單元格的類型,?做出相應的轉化 ????????if?(cell.getType()?==?CellType.EMPTY) ????????{ ????????????Number?lbl?=?new?Number(col,?row,?num); ????????????if(null?!=?format) ????????????{ ????????????????lbl.setCellFormat(format); ????????????}?else ????????????{ ????????????????lbl.setCellFormat(cell.getCellFormat()); ????????????} ????????????dataSheet.addCell(lbl); ????????}?else?if?(cell.getType()?==?CellType.NUMBER) ????????{ ????????????//?數字單元格修改 ????????????Number?lbl?=?(Number)cell; ????????????lbl.setValue(num); ????????}?else?if?(cell.getType()?==?CellType.LABEL) ????????{ ????????????Label?lbl?=?(Label)cell; ????????????lbl.setString(String.valueOf(num)); ????????} ????} ???? ????/** ?????*?修改公式單元格的值 ?????*?@param?dataSheet?WritableSheet?:?工作表 ?????*?@param?col?int?:?列 ?????*?@param?row?int?:?行 ?????*?@param?startPos?int?:?開始位置 ?????*?@param?endPos?int?:?結束位置 ?????*?@param?format ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?void?modiFormulaCell(WritableSheet?dataSheet,?int?col,?int?row,?int?startPos,?int?endPos,?CellFormat?format)?throws?RowsExceededException,?WriteException ????{ ????????String?f?=?getFormula(col,?row,?startPos,?endPos); ????????//?插入公式(只支持插入,不支持修改) ????????WritableCell?cell?=?dataSheet.getWritableCell(col,?row); ????????if?(cell.getType()?==?CellType.EMPTY) ????????{???????????????????? ????????????//?公式單元格 ????????????Formula?lbl?=?new?Formula(col,?row,?f); ????????????if(null?!=?format) ????????????{ ????????????????lbl.setCellFormat(format); ????????????}?else ????????????{ ????????????????lbl.setCellFormat(cell.getCellFormat()); ????????????} ????????????dataSheet.addCell(lbl); ????????}?else?if?(cell.getType()?==?CellType.STRING_FORMULA) ????????{ ????????????YTLogger.logWarn("Formula?modify?not?supported!"); ????????} ????} ???? ????/** ?????*?得到公式 ?????*?@param?col?int?:?列 ?????*?@param?row?int?:?行 ?????*?@param?startPos?int?:?開始位置 ?????*?@param?endPos?int?:?結束位置 ?????*?@return?String ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?String?getFormula(int?col,?int?row,?int?startPos,?int?endPos) ????????????throws?RowsExceededException,?WriteException ????{ ????????char?base?=?'A'; ????????char?c1?=?base; ????????StringBuffer?formula?=?new?StringBuffer(128); ????????//?組裝公式 ????????formula.append("SUM("); ????????if?(col?<=?25) ????????{ ????????????c1?=?(char)?(col?%?26?+?base); ????????????formula.append(c1).append(startPos).append(":") ???????????????????.append(c1).append(endPos).append(")"); ????????}?else?if?(col?>?25) ????????{ ????????????char?c2?=?(char)?((col?-?26)?/?26?+?base); ????????????c1?=?(char)?((col?-?26)?%?26?+?base); ????????????formula.append(c2).append(c1).append(startPos).append(":") ???????????????????.append(c2).append(c1).append(endPos).append(")"); ????????}
????????return?formula.toString(); ????} ???? ????/** ?????*?插入圖表工作表 ?????*?@param?wwb?WritableWorkbook?:?工作簿 ?????*?@param?vecImg?Vector?:?圖像鏈表 ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?void?insertImgsheet(WritableWorkbook?wwb,?Vector?vecImg) ????????????throws?RowsExceededException,?WriteException ????{ ????????//?插入圖像 ????????WritableSheet?imgSheet; ????????if((wwb.getSheets()).length?<?2) ????????{ ????????????imgSheet?=?wwb.createSheet("圖表",?1); ????????}?else ????????{ ????????????imgSheet?=?wwb.getSheet(1); ????????} ???????? ????????for?(int?i?=?0;?i?<?vecImg.size();?i++) ????????{ ????????????ChartImg?chart?=?(ChartImg)?vecImg.get(i); ????????????//?插入圖像標題 ????????????Label?lbl?=?new?Label(0,?2?+?20?*?i,?chart.getImgTitle()); ????????????WritableFont?font?=?new?WritableFont(WritableFont.ARIAL, ????????????????????WritableFont.DEFAULT_POINT_SIZE,?WritableFont.NO_BOLD,?false, ????????????????????UnderlineStyle.NO_UNDERLINE,?Colour.DARK_BLUE2); ????????????WritableCellFormat?background?=?new?WritableCellFormat(font); ????????????background.setWrap(true); ????????????background.setBackground(Colour.GRAY_25); ????????????imgSheet.mergeCells(0,?2?+?20?*?i,?9,?2?+?20?*?i); ????????????lbl.setCellFormat(background); ????????????imgSheet.addCell(lbl); ????????????//?插入圖像單元格 ????????????insertImgCell(imgSheet,?2,?4?+?20?*?i,?8,?15,?chart.getImgName()); ????????} ????}
????/** ?????*?插入圖像到單元格(圖像格式只支持png) ?????*?@param?dataSheet?WritableSheet?:?工作表 ?????*?@param?col?int?:?列 ?????*?@param?row?int?:?行 ?????*?@param?width?int?:?寬 ?????*?@param?height?int?:?高 ?????*?@param?imgName?String?:?圖像的全路徑 ?????*?@throws?RowsExceededException ?????*?@throws?WriteException ?????*/ ????private?void?insertImgCell(WritableSheet?dataSheet,?int?col,?int?row,?int?width, ????????????int?height,?String?imgName)?throws?RowsExceededException,?WriteException ????{ ????????File?imgFile?=?new?File(imgName); ????????WritableImage?img?=?new?WritableImage(col,?row,?width,?height,?imgFile); ????????dataSheet.addImage(img); ????} ???? ????/** ?????*?測試 ?????*?@param?args ?????*/ ????public?static?void?main(String[]?args) ????{ ????????XLSDemo?demo?=?new?XLSDemo(); ????????demo.makeXls(); ????} } |
|
jxl不錯,簡單易用
import jxl.*; import jxl.write.*; import java.io.*; import java.io.File.*; import java.util.*;
public class excel { public static void main(String[] args) {
String targetfile = "c:/out.xls";//輸出的excel文件名 String worksheet = "List";//輸出的excel文件工作表名 String[] title = {"ID","NAME","DESCRIB"};//excel工作表的標題
WritableWorkbook workbook; try { //創建可寫入的Excel工作薄,運行生成的文件在tomcat/bin下 //workbook = Workbook.createWorkbook(new File("output.xls")); System.out.println("begin");
OutputStream os=new FileOutputStream(targetfile); workbook=Workbook.createWorkbook(os);
WritableSheet sheet = workbook.createSheet(worksheet, 0); //添加第一個工作表 //WritableSheet sheet1 = workbook.createSheet("MySheet1", 1); //可添加第二個工作 /* jxl.write.Label label = new jxl.write.Label(0, 2, "A label record"); //put a label in cell A3, Label(column,row) sheet.addCell(label); */
jxl.write.Label label; for (int i=0; i<title.length; i++) { //Label(列號,行號 ,內容 ) label = new jxl.write.Label(i, 0, title[i]); //put the title in row1 sheet.addCell(label); }
//下列添加的對字體等的設置均調試通過,可作參考用
//添加數字 jxl.write.Number number = new jxl.write.Number(3, 4, 3.14159); //put the number 3.14159 in cell D5 sheet.addCell(number);
//添加帶有字型Formatting的對象 jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES,10,WritableFont.BOLD,true); jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf); jxl.write.Label labelCF = new jxl.write.Label(4,4,"文本",wcfF); sheet.addCell(labelCF);
//添加帶有字體顏色,帶背景顏色 Formatting的對象 jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL,10,WritableFont.BOLD,false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.RED); jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc); wcfFC.setBackground(jxl.format.Colour.BLUE); jxl.write.Label labelCFC = new jxl.write.Label(1,5,"帶顏色",wcfFC); sheet.addCell(labelCFC);
//添加帶有formatting的Number對象 jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##"); jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf); jxl.write.Number labelNF = new jxl.write.Number(1,1,3.1415926,wcfN); sheet.addCell(labelNF);
//3.添加Boolean對象 jxl.write.Boolean labelB = new jxl.write.Boolean(0,2,false); sheet.addCell(labelB);
//4.添加DateTime對象 jxl.write.DateTime labelDT = new jxl.write.DateTime(0,3,new java.util.Date()); sheet.addCell(labelDT);
//添加帶有formatting的DateFormat對象 jxl.write.DateFormat df = new jxl.write.DateFormat("ddMMyyyyhh:mm:ss"); jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df); jxl.write.DateTime labelDTF = new jxl.write.DateTime(1,3,new java.util.Date(),wcfDF); sheet.addCell(labelDTF);
//和賓單元格 //sheet.mergeCells(int col1,int row1,int col2,int row2);//左上角到右下角 sheet.mergeCells(4,5,8,10);//左上角到右下角 wfc = new jxl.write.WritableFont(WritableFont.ARIAL,40,WritableFont.BOLD,false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.GREEN); jxl.write.WritableCellFormat wchB = new jxl.write.WritableCellFormat(wfc); wchB.setAlignment(jxl.format.Alignment.CENTRE); labelCFC = new jxl.write.Label(4,5,"單元合并",wchB); sheet.addCell(labelCFC); //
//設置邊框 jxl.write.WritableCellFormat wcsB = new jxl.write.WritableCellFormat(); wcsB.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THICK); labelCFC = new jxl.write.Label(0,6,"邊框設置",wcsB); sheet.addCell(labelCFC); workbook.write(); workbook.close(); }catch(Exception e) { e.printStackTrace(); } System.out.println("end"); Runtime r=Runtime.getRuntime(); Process p=null; //String cmd[]={"notepad","exec.java"}; String cmd[]={"C:\\Program Files\\Microsoft Office\\Office\\EXCEL.EXE","out.xls"}; try{ p=r.exec(cmd); } catch(Exception e){ System.out.println("error executing: "+cmd[0]); }
} }
|