2006年8月21日 #
(原)Java 專業(yè)人士必備的書(shū)籍和網(wǎng)站列表您必備的參考資料 ![]() |
![]() |
|
級(jí)別: 初級(jí)
Roy Miller
(roy@roywmiller.com), 創(chuàng)始人兼總裁, The Other Road, LLC
2007 年 1 月 15 日
對(duì)于 Java? 語(yǔ)言開(kāi)發(fā)人員來(lái)說(shuō),信息過(guò)量是一個(gè)真正的問(wèn)題。每個(gè)新入行的程序員都要面臨一個(gè)令人畏縮的挑戰(zhàn):要進(jìn)入的行業(yè)是一個(gè)具有海量知識(shí)的行業(yè)。要了解的東西簡(jiǎn)直太多了。對(duì)于有經(jīng)驗(yàn)的老手來(lái)說(shuō),情況只有些微好轉(zhuǎn)。知識(shí)量總在增大,僅僅跟上進(jìn)度就是一個(gè)挑戰(zhàn)。如果有一份專業(yè)人士必備的書(shū)籍和網(wǎng)站列表該有多好!本文就是這個(gè)列表。它包含了每個(gè)專業(yè)的 Java 語(yǔ)言程序員在書(shū)架或?yàn)g覽器書(shū)簽中必備的最重要的書(shū)籍和網(wǎng)站。
這些都是您書(shū)架上必備的書(shū)和應(yīng)該經(jīng)常使用的 Web 鏈接。時(shí)間是一項(xiàng)重要的資源,本文幫您回避那些分心的事情,把時(shí)間專注于最有益于您作為Java 語(yǔ)言程序員職業(yè)生涯的信息源。盡管有多少程序員就有多少他們最喜歡的參考資料,但本文收集的這些都是優(yōu)中選優(yōu),來(lái)源于我書(shū)架上的私家珍藏和許多 Java 專家的推薦。
我考慮了兩種組織這份參考資料列表的方法。我本可以通過(guò)主題領(lǐng)域來(lái)組織,這也許很有幫助,但主題列表很快就會(huì)變得不實(shí)用。相反,我選擇了另一種方法:通過(guò)類型來(lái)組織,即書(shū)籍和 Web 站點(diǎn)。
總的來(lái)講,有經(jīng)驗(yàn)的老手們用 Web 站點(diǎn)來(lái)跟蹤行業(yè)的走勢(shì)。書(shū)籍、文章和論文有助于跟上潮流,但它們總體上更適合于基礎(chǔ)學(xué)習(xí)。極富創(chuàng)造性的書(shū)籍偶爾會(huì)撼動(dòng)一兩個(gè)基礎(chǔ)性的東西。這樣的書(shū)也在本列表之列。
需要提出的一點(diǎn)警告是,專注于 Java 語(yǔ)言的書(shū)籍和 Web 站點(diǎn)數(shù)量巨大。您鐘愛(ài)的未必在這份列表里。那并不意味著它們不好。它們只是不在這份列表里而已。可能是因?yàn)槲疫€不知道它們。也可能是因?yàn)槲也徽J(rèn)為它們能夠算得上是重要資源。不包含一些參考資料是一個(gè)評(píng)判問(wèn)題,但如果不這樣的話,您也許就要花幾小時(shí)來(lái)拖動(dòng)滾動(dòng)條,還要花上成千上萬(wàn)美元來(lái)買書(shū)。如果您作為一個(gè)專業(yè)的 Java 程序員,有一些常用的優(yōu)秀參考資料,一定要讓我知道這些資料。這份列表一直都在更新中,您提出的那些也許就會(huì)被收錄進(jìn)去。
每個(gè)程序員都會(huì)有一些由于經(jīng)常被當(dāng)作專業(yè)資料參閱而磨壞的書(shū)。下列書(shū)籍應(yīng)該是 Java 語(yǔ)言程序員的書(shū)架上必備的。書(shū)很貴,所以我有意將這份列表弄得很短,僅限于重要書(shū)籍。
Thinking in Java (Bruce Eckel)
Thinking in Java, 3rd edition
(Bruce Eckel; Prentice Hall PTR,2002 年)
Java 編程思想:第3版 (陳昊鵬 等譯; 機(jī)械工業(yè)出版社,2005 年)
Eckel 的書(shū)對(duì)于學(xué)習(xí)如何在 Java 語(yǔ)言環(huán)境中使用好面向?qū)ο蠹夹g(shù)極其實(shí)用。書(shū)中大量的代碼樣例解釋了他所介紹的概念。文字出自一個(gè)并不認(rèn)為 Java 技術(shù)總是正確答案的人,所以相當(dāng)?shù)貙?shí)用。Eckel 具有多種語(yǔ)言的大量經(jīng)驗(yàn),還有用面向?qū)ο蠓绞竭M(jìn)行思考的扎實(shí)技能。本書(shū)將這些技能放到實(shí)用的 Java 語(yǔ)言環(huán)境中。他還在寫(xiě)一本新書(shū),名為 Thinking in Enterprise Java。
Effective Java: Programming Language Guide
(Joshua Bloch; Addison-Wesley,2001 年)
Effective Java 中文版 (潘愛(ài)民 譯; 機(jī)械工業(yè)出版社,2003 年)
本書(shū)是理解優(yōu)秀 Java 程序設(shè)計(jì)原則的最佳書(shū)籍。大多數(shù)材料從其他的 “學(xué)習(xí) Java ” 的書(shū)中根本找不到。例如,Bloch 書(shū)中關(guān)于覆蓋 equals()
這一章是我讀過(guò)的最好的參考資料之一。他也在書(shū)中包括了很實(shí)用的建議:用接口替代抽象類和靈活使用異常。Bloch 是 Sun 公司 Java 平臺(tái)庫(kù)的架構(gòu)師,所以他透徹地了解這門(mén)語(yǔ)言。事實(shí)上,他編寫(xiě)了該語(yǔ)言中大量有用的庫(kù)。本書(shū)必讀!
The Java Programming Language (Ken Arnold, James Gosling, David Holmes)
The Java Programming Language
(Ken Arnold,James Gosling,David Holmes; Addison-Wesley,2000 年)
Java 編程語(yǔ)言(第 3 版) (虞萬(wàn)榮 等譯,中國(guó)電力出版社,2003 年)
這也許是能弄到的最好的 Java 入門(mén)讀物。它并不是一個(gè)標(biāo)準(zhǔn)規(guī)范,而是一本介紹每門(mén)語(yǔ)言特性的可讀書(shū)籍。這本書(shū)在嚴(yán)謹(jǐn)性和教育性方面權(quán)衡得很好,能夠讓懂編程的人迅速被 Java 語(yǔ)言(和其豐富的類庫(kù))所吸引。
Concurrent Programming in Java: Design Principles and Patterns (Doug Lea)
Concurrent Programming in Java: Design Principles and Patterns, 2nd edition
(Doug Lea; Addison-Wesley,1999 年)
Java 并發(fā)編程—設(shè)計(jì)原則與模式(第二版) (趙涌 等譯,中國(guó)電力出版社,2004 年)
不是每個(gè)開(kāi)發(fā)人員都需要如此細(xì)致地了解并發(fā)性,也不是每個(gè)工程師都能達(dá)到本書(shū)的水準(zhǔn),但卻沒(méi)有比本書(shū)更好的關(guān)于并發(fā)性編程的概述了。如果您對(duì)此感興趣,請(qǐng)從這里開(kāi)始。Lea 是 SUNY 的一名專業(yè)程序員,他的和并發(fā)性有關(guān)的作品和想法都包含在了 JDK 5.0 規(guī)范(引自 JSR166)中,所以您大可放心,他所說(shuō)的關(guān)于有效使用 Java 語(yǔ)言的建議是值得一聽(tīng)的。他是一個(gè)很善于溝通的人。
Expert One-On-One J2EE Design and Development (Rod Johnson)
Expert One-On-One J2EE Design and Development
(Rod Johnson)
WROX: J2EE 設(shè)計(jì)開(kāi)發(fā)編程指南 (魏海萍 譯,電子工業(yè)出版社,2003 年)
對(duì)于剛接觸 J2EE 的人來(lái)說(shuō),這是唯一的一本如實(shí)反映這項(xiàng)技術(shù)的書(shū)。本書(shū)收錄了多年的成功經(jīng)驗(yàn)和失敗經(jīng)驗(yàn),不同于其他許多作者,Johnson 樂(lè)于將失敗的經(jīng)驗(yàn)公諸于眾。J2EE 常常都被過(guò)度使用。Johnson 的書(shū)能幫您避免這一點(diǎn)。
Refactoring (Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts)
Refactoring: Improving the Design of Existing Code
(Martin Fowler,Kent Beck,John Brant,William Opdyke,Don Roberts; Addison-Wesley,1999 年)
重構(gòu):改善既有代碼的設(shè)計(jì)(中文版) (侯捷 等譯,中國(guó)電力出版社 ,2003 年)
Fowler 寫(xiě)了幾本現(xiàn)已出版的最流行的編程書(shū),包括 Analysis Patterns。他的關(guān)于重構(gòu) 的書(shū)是這一主題的基本書(shū)籍。重構(gòu)代碼是被程序員忽略的訓(xùn)練,但卻是程序員最直觀的想法。重構(gòu)是在不改變代碼結(jié)果的前提下改進(jìn)現(xiàn)有代碼的設(shè)計(jì)。這是保持代碼整潔的最佳方式,用這種方法設(shè)計(jì)的代碼總是很容易修改。什么時(shí)候進(jìn)行重構(gòu)呢?當(dāng)代碼“散發(fā)出味道”時(shí)。Fowler 的書(shū)里滿是 Java 語(yǔ)言代碼的例子。許多 Java 語(yǔ)言集成開(kāi)發(fā)環(huán)境(IDE)(包括了 IBM 的 Eclipse)都將 Fowler 的重構(gòu)包含了進(jìn)去,每一個(gè)都使用他的重構(gòu)名命名,所以熟悉如extract method 等重構(gòu)方法還是很值得的。
Design Patterns (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)
Design Patterns: Elements of Reusable Object Oriented Software
(Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides; Addison-Wesley,1997 年)
設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ) (李英軍 等譯,機(jī)械工業(yè)出版社 ,2005 年)
這是一本在專業(yè)程序員圈子里更為有名的書(shū),基于作者共同的綽號(hào),這本書(shū)被認(rèn)為是 “四人幫(GOF)之書(shū)”。模式是思考和解決普通編程問(wèn)題時(shí)可以重用的方式。學(xué)習(xí)模式是一門(mén)學(xué)科。使用好模式(或知道什么時(shí)候不 使用模式)是一項(xiàng)技能。忽略模式則是錯(cuò)誤的。書(shū)中所有的例子都以 C++ 表示,但 Java 語(yǔ)言是從那里誕生的,讓 Java 語(yǔ)言程序員由此聯(lián)系到如何在 Java 語(yǔ)言中實(shí)現(xiàn)這些模式相對(duì)簡(jiǎn)單一些。熟悉模式并了解如何使用好模式使編程更加簡(jiǎn)單。這使得和其他程序員交流也更簡(jiǎn)單,因?yàn)樵卺槍?duì)通用問(wèn)題的通用解決方案中,模式是描述解決方案中彼此協(xié)作的大量相關(guān)編程概念的快捷方式。一些更為通用的方式,如工廠方法 則是普便存在的,甚至存在于 Java 語(yǔ)言本身。關(guān)于明智使用模式的這個(gè)主題,也可以閱讀 Joshua Kerievsky 的 Refactoring to Patterns,該書(shū)稱可以讓代碼來(lái)告訴您何時(shí)實(shí)現(xiàn)模式。
Patterns of Enterprise Application Architecture (Martin Fowler)
Patterns of Enterprise Application Architecture
(Martin Fowler; Addison-Wesley,2002 年)
企業(yè)應(yīng)用架構(gòu)模式 (王懷民 等譯,機(jī)械工業(yè)出版社 ,2004 年)
比起小型、一次性項(xiàng)目來(lái)說(shuō),企業(yè)開(kāi)發(fā)當(dāng)然代表了更大的挑戰(zhàn)。那并不意味著企業(yè)開(kāi)發(fā)帶來(lái)的所有挑戰(zhàn)都是新挑戰(zhàn)。事實(shí)上有些時(shí)候,這項(xiàng)開(kāi)發(fā)已經(jīng) 是以前完成過(guò)的了。Fowler 做了很多個(gè)這樣的項(xiàng)目。他的書(shū)提到了一些通用解決方案,并提供了關(guān)于使用、折中和可選方案的指導(dǎo)。Fowler 在書(shū)中包含了一些熟悉的模式,如模型視圖控制器(MVC),他也提供了一些您也許不了解的模式,如處理 Web 站點(diǎn)上特定頁(yè)面請(qǐng)求或行為請(qǐng)求的 Page Controller 模式。正如您對(duì)待大多數(shù)模式一樣,一旦您讀過(guò)許多模式,您就會(huì)認(rèn)為 “我已經(jīng)知道那個(gè)模式了” 。也許是這樣,但有一個(gè)用來(lái)引用模式的通用表達(dá)方式還是很有幫助的。在有多個(gè)組件(由不同人開(kāi)發(fā))的大型項(xiàng)目中,該類引用是一項(xiàng)很好的幫助。
UML Distilled: A Brief Guide to the Standard Object Modeling Language
(Martin Fowler; Addison-Wesley 2003 年)
UML精粹:標(biāo)準(zhǔn)對(duì)象語(yǔ)言簡(jiǎn)明指南(第3版) (徐家福 譯,清華大學(xué)出版社 ,2005 年)
對(duì)于專業(yè)的程序員來(lái)說(shuō),UML 是一門(mén)很重要的通用可視化溝通語(yǔ)言,但是它被過(guò)度使用和草率地濫用了。您無(wú)需對(duì)使用 UML 溝通了解太多。Martin 對(duì) UML 的提煉為您提供了最核心的東西。事實(shí)上,前后的封頁(yè)提供了常規(guī)基礎(chǔ)上可能使用到的所有東西。該書(shū)中 UML 例子的代碼都是 Java 代碼。
Test-Driven Development: By Example (Kent Beck)
Test-Driven Development: By Example
(Kent Beck; Addison-Wesley 2002 年)
測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(中文版) (崔凱 譯,中國(guó)電力出版社 ,2004 年)
測(cè)試優(yōu)先編程將使編程發(fā)生革命性變化,能助您成為更好的程序員。在寫(xiě)代碼之前編寫(xiě)測(cè)試開(kāi)始很難,但卻是一項(xiàng)威力強(qiáng)大的技能。通過(guò)優(yōu)先編寫(xiě)測(cè)試,可使代碼更加簡(jiǎn)單,并確保從一開(kāi)始它就能工作(Beck 實(shí)踐著他提倡的測(cè)試優(yōu)先,與人合寫(xiě)了 JUnit,這是 Java 語(yǔ)言最流行的測(cè)試框架)。Beck 的書(shū)是權(quán)威的參考資料,擴(kuò)展了的 Money 例子也用 Java 語(yǔ)言寫(xiě)成。Beck 詳述了如何用測(cè)試優(yōu)先進(jìn)行 思考(這也許是許多程序員首先遇到的障礙)。
The Pragmatic Programmer: From Journeyman to Master (Andy Hunt and Dave Thomas)
The Pragmatic Programmer: From Journeyman to Master
(Andrew Hunt 和 David Thomas; Addison-Wesley 1999 年)
程序員修煉之道——從小工到專家 (馬維達(dá) 譯,電子工業(yè)出版社 ,2004 年)
做一個(gè)純粹的面向?qū)ο箝_(kāi)發(fā)人員有其優(yōu)勢(shì)所在。在當(dāng)今復(fù)雜的社會(huì)中,作為 Java 語(yǔ)言開(kāi)發(fā)人員,為完成任務(wù)常要妥協(xié)。Hunt 和 Thomas 探討了如何不將真正重要的東西妥協(xié)掉而完成任務(wù)。這不是一本關(guān)于 Java 語(yǔ)言的書(shū),而是 Java 語(yǔ)言開(kāi)發(fā)人員重要的思想讀物。例如,我認(rèn)為沒(méi)從“要解決問(wèn)題,而不是推卸責(zé)任”這句忠言中受益的程序員,不能像個(gè)自豪的藝術(shù)家一樣在他的杰作上簽上大名。
Peopleware: Productive Projects and Teams (Tom DeMarco and Timothy Lister)
Peopleware: Productive Projects and Teams
(Tom DeMarco,Timothy Lister; Dorset House,1999 年)
人件(第2版) (UMLChina 翻譯組 譯,清華大學(xué)出版社 ,2003 年)
這份列表中的其他所有書(shū)籍都至少和技術(shù)有些相關(guān)。這本書(shū)卻不是。在所有技術(shù)行話和首字母縮略詞的海洋中,有時(shí)軟件開(kāi)發(fā)人員和經(jīng)理們會(huì)忘記:是人 制造了軟件。DeMarco 和 Lister 向我們提醒了這一事實(shí),也向我們提醒了形成這一區(qū)別的原因。這不是一本關(guān)于一門(mén)特定編程語(yǔ)言的書(shū)籍,但卻是每個(gè) Java 語(yǔ)言程序員都應(yīng)該讀的書(shū)。關(guān)于 “累死程序員如何讓經(jīng)理們適得其反” 還有許多其他的好書(shū),但這是最好的一本。
![]() ![]() |
![]()
|
Web 站點(diǎn)的數(shù)目浩如煙海,如果您想要消化其中的內(nèi)容,窮畢生之力也難以全部訪問(wèn)。包含 Java 語(yǔ)言某方面內(nèi)容的詳盡的網(wǎng)站列表會(huì)大得離譜。下列站點(diǎn)都是可靠、真實(shí)的。
Sun 的 Java 語(yǔ)言站點(diǎn)
這是 Sun 的 Java 語(yǔ)言主站。作為 Java 語(yǔ)言開(kāi)發(fā)人員,您會(huì)發(fā)現(xiàn)自己頻繁地訪問(wèn)此站點(diǎn)。下列鏈接特別重要,特別是對(duì)新入行的 Java 語(yǔ)言開(kāi)發(fā)人員:
-
New to Java Center
New to Java Center
New to Java Center 存放了許多循序漸進(jìn)的 Java 技術(shù)資源鏈接。如果您剛接觸這門(mén)語(yǔ)言,這是一個(gè)好的起點(diǎn)。 -
教程和代碼庫(kù)
Java Tutorial
這里有大名鼎鼎的 Java Tutorial,以及關(guān)于 Java 語(yǔ)言各個(gè)方面(例如 Collection)的其他教程。
IBM 的 developerWorks
推銷自己也許有些厚臉皮,但 developerWorks 是一項(xiàng)巨大的資源,收錄了大量 Java 語(yǔ)言工具和技術(shù)的教程和文章。其內(nèi)容從初學(xué)者指南到學(xué)習(xí)這門(mén)語(yǔ)言到高級(jí)并發(fā)性技術(shù)。可以根據(jù)主題搜索內(nèi)容,然后根據(jù)類型瀏覽。
Apache Software Foundation
Apache 站點(diǎn)是許多可重用庫(kù)(通用領(lǐng)域)和工具的主頁(yè),這些庫(kù)和工具幫助 Java 開(kāi)發(fā)人員進(jìn)行開(kāi)發(fā)。這里的內(nèi)容全都是開(kāi)放源碼,所以盡管下載想要的吧!許多極其流行的 Java 語(yǔ)言庫(kù)和工具(如 Struts、Ant 和 Tomcat)都始于 Apache 項(xiàng)目。Jakarta 專區(qū)匯聚了大多數(shù)新興的 Java 語(yǔ)言材料。
Eclipse
有幾個(gè)好的 Java 語(yǔ)言集成開(kāi)發(fā)環(huán)境(IDE)。Eclipse(來(lái)自 IBM)是最新的 IDE 之一,它很快成為 Java 語(yǔ)言開(kāi)發(fā)的首要 IDE。它完全是開(kāi)源的,這意味著它是免費(fèi)的。該站包含了學(xué)習(xí)如何有效使用 Eclipse 的各種參考資料。這里還有關(guān)于 Standard Widget Toolkit(SWT)的信息,SWT 是相對(duì)于 Swing 來(lái)說(shuō)更加輕量級(jí)的選擇。
Eclipse 插件中心
和 Eclipse 插件
Eclipse 基于插件架構(gòu)。事實(shí)上,插件是 Eclipse 的 Java 語(yǔ)言開(kāi)發(fā)組件。但有差不多上千個(gè)插件,從 Web 開(kāi)發(fā)的插件到在 Eclipse 環(huán)境中玩游戲的插件。這兩個(gè)站點(diǎn)分類列出了大多數(shù)插件,可以進(jìn)行搜索。它們是很棒的資源。如果您想在 Eclipse 開(kāi)發(fā)環(huán)境中弄點(diǎn)新東西,幸運(yùn)的話有某個(gè)插件可能已經(jīng)實(shí)現(xiàn),從這兩個(gè)站點(diǎn)能找到想要的插件。這兩個(gè)站點(diǎn)都允許評(píng)論插件,這樣您就可以知道哪些插件好,哪些值得一試。
JUnit.org
Junit 是 Java 語(yǔ)言中一個(gè)基本的單元測(cè)試框架。該站點(diǎn)包含了 Junit 最新最棒的版本,外加大量有關(guān)測(cè)試(Java 語(yǔ)言或者其他語(yǔ)言的)各個(gè)層面上(針對(duì)桌面應(yīng)用程序、Web 應(yīng)用程序、J2EE 應(yīng)用程序等)的其他資源。如果您想找測(cè)試資源,這里就是最佳起點(diǎn)。
TheServerSide.com
如果您要(或?qū)⒁?從事服務(wù)器端 Java 語(yǔ)言的開(kāi)發(fā),此站點(diǎn)是一處舉足輕重的資源。您可以到這里找到有關(guān) JBoss、J2EE、LDAP、Struts 和大量其他主題的文章,并且都是完全可檢索的。這些文章不僅僅是簡(jiǎn)單描述 Java 語(yǔ)言的特征或者支持的庫(kù)。它們更進(jìn)一步地描述了庫(kù)的新奇用法(如使用 Jakarta Velocity 作為規(guī)則引擎,而不是模板引擎)。它們也提供了有關(guān) Java 語(yǔ)言現(xiàn)狀的連續(xù)評(píng)論(當(dāng)前的一篇文章是由 Tim Bray 所寫(xiě)的 Java is boring )。該站點(diǎn)更好的通用功能之一是對(duì) Java 語(yǔ)言工具和產(chǎn)品(應(yīng)用服務(wù)器等)的矩陣式比較。
Bruce Eckel's MindView, Inc.
Eckel 寫(xiě)了幾本 “用 …… 進(jìn)行思考” 的書(shū),內(nèi)容關(guān)于 Java 語(yǔ)言、Python 和 C++ ,當(dāng)我學(xué)習(xí) Java 語(yǔ)言時(shí),他的 Thinking in Java 對(duì)我尤其有幫助。它很實(shí)用并切中要害,在“在 Java 語(yǔ)言環(huán)境中如何面向?qū)ο笏伎肌狈矫婢哂凶孔R(shí)。您可以從此站點(diǎn)免費(fèi)下載他所有書(shū)籍的電子版。他也寫(xiě)了許多好文章,并且他把這些文章的鏈接都放到了這里(包括關(guān)于 Jython、Java 和 .NET 比較等內(nèi)容的文章)。
ONJava.com
O'Reilley 歷年來(lái)出版了一些有關(guān)編程語(yǔ)言和工具的優(yōu)秀書(shū)籍。他們的專注于 Java 語(yǔ)言的網(wǎng)站也不錯(cuò)。它有些有關(guān)各種 Java 語(yǔ)言工具(如 JDOM 和 Hibernate)、Java 平臺(tái)(如 J2SE 和 J2EE)不同領(lǐng)域不同部分的文章。全部都可以被檢索到。他們有優(yōu)秀的文章和教程。該站點(diǎn)按主題排列。例如有 Java 和 XML、Java Security、Wireless Java 和 Java SysAdmin。該站點(diǎn)也有到 O'Reilley Learning Lab 的鏈接,在那里您能獲得在線參考資料(Java 語(yǔ)言相關(guān)和其他的)。那些不是免費(fèi)的,但是許多都面向大學(xué)認(rèn)證。因此您可以以一種很方便的方式來(lái)學(xué)習(xí)技能,并得到一些認(rèn)證。
java.net 社區(qū)
java.net 社區(qū)有多個(gè)“社區(qū)”,有特定于主題的論壇和文章。例如 Java Desktop 社區(qū)有各類與 Java 語(yǔ)言桌面開(kāi)發(fā)相關(guān)的資料。Java Patterns 社區(qū)作為一個(gè)門(mén)戶,也許對(duì)提供 Java 語(yǔ)言的模式資源相當(dāng)感興趣。還有一個(gè) Java User Groups (JUG) 社區(qū),在那里能找到有關(guān)創(chuàng)建、加入和管理一個(gè) JUG 的信息。
![]() ![]() |
![]()
|
任何 “好的”、“關(guān)鍵性的” 或者 “重要的” 參考資料列表都注定是不完整的,本文的列表也未能例外。 Java 語(yǔ)言的書(shū)籍?dāng)?shù)目眾多,當(dāng)然,萬(wàn)維網(wǎng)也很龐大。除本文所列的參考資料之外,還有很多用于學(xué)習(xí) Java 語(yǔ)言的參考資料。但如果您擁有了這里所提到的所有書(shū)籍、網(wǎng)站、文章或者教程,您應(yīng)當(dāng)已經(jīng)擁有了一個(gè)使您良好開(kāi)端并助您登堂入室的實(shí)用寶庫(kù)。
最后,要成為一個(gè)能力日增和高效的 Java 語(yǔ)言開(kāi)發(fā)人員,方法就是用它工作,動(dòng)手來(lái)嘗試。如果有一個(gè)教程詳細(xì)介紹了所需創(chuàng)建的軟件的每一部分,您很可能并沒(méi)得到多少好處。有時(shí),您可能得走自己的路。在成功地嘗試了一些新的東西之后,您可能想要寫(xiě)一篇文章、教程或者一本書(shū)來(lái)分享您所學(xué)到的。
- 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文 。
- 在 developerWorks Java 技術(shù)專區(qū) 查找更多 Java 技術(shù)參考資料。
- 訪問(wèn) Java 技術(shù)新手入門(mén) 站點(diǎn),獲取最新的參考資料,幫您開(kāi)始使用 Java 編程。
- 通過(guò)參與 developerWorks blog 加入 developerWorks 社區(qū)。
-
瀏覽
關(guān)于這些主題和其他技術(shù)主題的書(shū)籍。
![]() |
||
|
![]() |
Roy Miller 是一名獨(dú)立軟件開(kāi)發(fā)培訓(xùn)師、程序員兼作家,他在充滿挑戰(zhàn)、快節(jié)奏的咨詢公司里從事了十多年軟件開(kāi)發(fā)和項(xiàng)目管理工作。他最初在 Andersen Consulting(現(xiàn)在是 Accenture)公司工作,在那里,他管理團(tuán)隊(duì)實(shí)現(xiàn)了許多系統(tǒng),從主機(jī)記帳系統(tǒng)到星形模式數(shù)據(jù)集市。最近三年來(lái),他在北卡羅來(lái)納州 Holly Springs 的 RoleModel Software, Inc. 公司工作,在那里他專業(yè)地運(yùn)用著 Java 技術(shù),并擔(dān)任開(kāi)發(fā)人員兼 Extreme Programming (XP) 培訓(xùn)師。他與人合著了 Addison-Wesley XP 系列的 Extreme Programming Applied: Playing to Win 一書(shū),最近他寫(xiě)了 Managing Software for Growth: Without Fear, Control and the Manufacturing Mindset 一書(shū),來(lái)幫助經(jīng)理和管理層理解:像 XP 這樣的敏捷構(gòu)建方法為什么比傳統(tǒng)的方法更有效。2003 年,他創(chuàng)辦了自己的公司:The Other Road,該公司幫助其他公司了解如何向 XP 和被他稱為 Extreme Business (XB) 的方法轉(zhuǎn)換。 |
JasperReport是一個(gè)強(qiáng)大的開(kāi)源報(bào)表工具,它可以傳送豐富的報(bào)表內(nèi)容到顯示器、打印機(jī)或者PDF、HTML、XLS、CSV、XML文件。它完全使用Java編寫(xiě),可以在各種Java應(yīng)用中用來(lái)創(chuàng)建動(dòng)態(tài)報(bào)表內(nèi)容。它的主要目標(biāo)是用簡(jiǎn)單靈活的方法幫助創(chuàng)建便于打印的分頁(yè)文檔。
JasperReport根據(jù)一個(gè)xml報(bào)表設(shè)計(jì)文件來(lái)組織從JDBC獲得的關(guān)系數(shù)據(jù)庫(kù)數(shù)據(jù)。要用數(shù)據(jù)填充報(bào)表,首先必須編譯報(bào)表。編譯xml的報(bào)表設(shè)計(jì)文件是用JasperManager類的compileReport()方法完成的。
通過(guò)編譯,報(bào)表設(shè)計(jì)被加載到一個(gè)報(bào)表設(shè)計(jì)對(duì)象(net.sf.jasperreports.engine.JasperReport類的實(shí)例)中并被序列化然后保存。在應(yīng)用程序用數(shù)據(jù)填充報(bào)表時(shí)使用該序列化文件。實(shí)際上,報(bào)表編譯完成了報(bào)表設(shè)計(jì)中所有的java表達(dá)式的編譯。很多檢查工作在編譯期間進(jìn)行以確保報(bào)表設(shè)計(jì)的完整性,編譯后的文件是待填充的報(bào)表,以方便應(yīng)用程序用各種數(shù)據(jù)集來(lái)產(chǎn)生不同的報(bào)表文檔。
要填充報(bào)表,可以使用JasperManager類的fillReportXXX()方法。這些方法接受一個(gè)參數(shù)代表報(bào)表設(shè)計(jì)——可以是一個(gè)JasperDesign對(duì)象,也可以是一個(gè)存放該類對(duì)象的文件名——還有一個(gè)獲得填充報(bào)表數(shù)據(jù)的JDBC連接。報(bào)表填充的結(jié)果是一個(gè)表示待打印文檔的對(duì)象(net.sf.jasperreports.engine.JasperPrint類的實(shí)例),可以被序列化保存以后繼續(xù)使用,或者傳送給打印機(jī)、顯示器,或者導(dǎo)出成PDF、HTML、XLS、CSV或者XML文件。
2. 報(bào)表設(shè)計(jì)
一個(gè)報(bào)表設(shè)計(jì)表示一個(gè)模版用來(lái)被JasperReport引擎填充數(shù)據(jù)并傳送到屏幕、打印機(jī)或者Web。數(shù)據(jù)庫(kù)的數(shù)據(jù)根據(jù)報(bào)表設(shè)計(jì)被組織來(lái)填充報(bào)表以得到待打印的分頁(yè)文檔。報(bào)表設(shè)計(jì)都保存到一個(gè)特定結(jié)構(gòu)的一個(gè)XML文件中,文件結(jié)構(gòu)定義在一個(gè)JasperReport引擎可以識(shí)別的DTD文件中。然后這些xml文件會(huì)被編譯以準(zhǔn)備報(bào)表填充操作。
創(chuàng)建一個(gè)報(bào)表設(shè)計(jì)(模版),必須按照如下結(jié)構(gòu)編輯一個(gè)xml文件:
<?xml version="1.0"?>
<!DOCTYPE jasperReport
PUBLIC "-//JasperReports//DTD Report Design//EN"
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport name="name_of_the_report" ... >
...
</jasperReport>
3. 報(bào)表參數(shù)
報(bào)表參數(shù)是傳遞給報(bào)表填充操作的對(duì)象的引用,為報(bào)表引擎?zhèn)鬟f它無(wú)法在數(shù)據(jù)源中找到的數(shù)據(jù)是非常有用的。例如,我們可以將登陸執(zhí)行報(bào)表填充操作的用戶名傳給引擎,這樣我們可以在報(bào)表上顯示制表人或者動(dòng)態(tài)改變報(bào)表的標(biāo)題。
一個(gè)使用報(bào)表參數(shù)的重要作用是完成報(bào)表的動(dòng)態(tài)查詢語(yǔ)句,以使報(bào)表獲得的數(shù)據(jù)更加符合要求,這些參數(shù)就像報(bào)表數(shù)據(jù)的過(guò)濾器。
在報(bào)表中聲明參數(shù)非常簡(jiǎn)單,只需要指定名稱和類型(java類):
<parameter name="ReportTitle" class="java.lang.String"/>
<parameter name="MaxOrderID" class="java.lang.Integer"/>
<parameter name="SummaryImage" class="java.awt.Image"/>
可以用兩種方法在查詢語(yǔ)句中使用報(bào)表參數(shù):
1. 就像通常在java.sql.PreparedStatement中使用參數(shù)一樣:
SELECT * FROM Orders WHERE OrderID <= $P{MaxOrderID} ORDER BY ShipCountry
2. 有時(shí)需要用參數(shù)來(lái)動(dòng)態(tài)改變SQL查詢的部分語(yǔ)句或者將整個(gè)SQL語(yǔ)句作為參數(shù)傳給報(bào)表,在這種情況下,語(yǔ)法有一點(diǎn)不同,如下:
SELECT * FROM Orders ORDER BY $P!{OrderByClause}
還有一些報(bào)表內(nèi)建的系統(tǒng)參數(shù)可以直接在表達(dá)式中使用:
REPORT_PARAMETERS_MAP
REPORT_CONNECTION
REPORT_DATA_SOURCE
REPORT_SCRIPTLET
4. 數(shù)據(jù)源
JasperReport只是各種類型的數(shù)據(jù)源,并提供一個(gè)JRDataSource的接口。該有一個(gè)缺省的實(shí)現(xiàn)類(JRResultSetDataSource class)包裝了ResultSet對(duì)象,允許使用任何通過(guò)JDBC連接的數(shù)據(jù)庫(kù)。使用JDBC數(shù)據(jù)源時(shí),即可以通過(guò)將數(shù)據(jù)庫(kù)連接傳給報(bào)表填充引擎并在報(bào)表定義中指定一個(gè)SQL查詢語(yǔ)句(參考dtd定義中的<queryString>元素)來(lái)提供數(shù)據(jù),也可以直接用ResultSet作參數(shù)生成JRResultSetDataSource對(duì)象來(lái)提供數(shù)據(jù)。
對(duì)于其他的數(shù)據(jù)源,也不會(huì)太麻煩,只需要實(shí)現(xiàn)JRDataSource接口來(lái)創(chuàng)建自己的數(shù)據(jù)源類。
5. 字段
報(bào)表字段提供了唯一映射數(shù)據(jù)源中數(shù)據(jù)到報(bào)表數(shù)據(jù)的方式。如果數(shù)據(jù)源是ResultSet對(duì)象,報(bào)表字段必須對(duì)應(yīng)ResultSet對(duì)象中的列,就是說(shuō)報(bào)表字段必須和對(duì)應(yīng)的列有相同的名字和匹配的類型。
例如,我們要?jiǎng)?chuàng)建的報(bào)表需要用Employees表的數(shù)據(jù),該表結(jié)構(gòu)如下:
Column Name Datatype Length
--------------------------------------
EmployeeID int 4
LastName varchar 20
FirstName varchar 10
HireDate datetime 8
我們可以在報(bào)表設(shè)計(jì)文件中定義如下的字段:
<field name="EmployeeID" class="java.lang.Integer"/>
<field name="LastName" class="java.lang.String"/>
<field name="FirstName" class="java.lang.String"/>
<field name="HireDate" class="java.util.Date"/>
如果我們生命一個(gè)報(bào)表字段在ResultSet中沒(méi)有對(duì)應(yīng)的列,則會(huì)在運(yùn)行時(shí)拋出異常。當(dāng)然ResultSet中的列沒(méi)有被聲明為報(bào)表字段不會(huì)影響報(bào)表的數(shù)據(jù)填充,但是他們?nèi)匀皇强梢栽L問(wèn)的。
6. 表達(dá)式
表達(dá)式是JasperReport的一個(gè)很強(qiáng)大有用的特性。用表達(dá)式可以:聲明報(bào)表變量來(lái)完成各種計(jì)算,為數(shù)據(jù)分組,指定報(bào)表文本字段內(nèi)容或?qū)ζ渌麍?bào)表對(duì)象的顯示進(jìn)行更靈活的定制。基本上,所有的報(bào)表表達(dá)式都是Java表達(dá)式,并且可以引用報(bào)表字段和報(bào)表變量。
在報(bào)表設(shè)計(jì)的xml文件中有諸多定義表達(dá)式的元素:<variableExpression>, <initialValueExpression>, <groupExpression>, <printWhenExpression>, <imageExpression> 和<textFieldExpression>。
要在在表達(dá)式中引用報(bào)表字段,字段名必須寫(xiě)在$F{和}符號(hào)之間。例如,如果我們要在一個(gè)文本域中連接兩個(gè)字段,我們可以像下面定義表達(dá)式:
<textFieldExpression>
$F{FirstName} + " " + $F{LastName}
</textFieldExpression>
表達(dá)式可以更復(fù)雜:
<textFieldExpression>
$F{FirstName} + " " + $F{LastName} + " was hired on " +
(new SimpleDateFormat("MM/dd/yyyy")).format($F{HireDate}) + "."
</textFieldExpression>
要在表達(dá)式中引用一個(gè)變量,必須將變量名寫(xiě)在$V{和}符號(hào)之間,如下:
<textFieldExpression>
"Total quantity : " + $V{QuantitySum} + " kg."
</textFieldExpression>
對(duì)于報(bào)表參數(shù)也是同樣的語(yǔ)法,只不過(guò)參數(shù)名必須寫(xiě)在$P{和}符號(hào)之間:
<textFieldExpression>
"Max Order ID is : " + $P{MaxOrderID}
</textFieldExpression>
7. 變量
報(bào)表變量是在表達(dá)式之前構(gòu)建的專用對(duì)象。變量只聲明一次,而可以在整個(gè)報(bào)表設(shè)計(jì)中重復(fù)使用,并在對(duì)應(yīng)的表達(dá)式中完成大量的計(jì)算,從而簡(jiǎn)化了報(bào)表設(shè)計(jì)。在表達(dá)式中,一個(gè)變量可以引用其它變量,但是被引用變量必須在引用變量之前聲明。所以變量的聲明順序?qū)?bào)表設(shè)計(jì)也是很重要的。
變量還可以聲明來(lái)完成引擎內(nèi)建計(jì)算的求值,如:count、sum、average、lowest、highest、variance等等。一個(gè)完成Quantity字段sum計(jì)算的變量定義如下:
<variable name="QuantitySum"
class="java.lang.Double" calculation="Sum">
<variableExpression>$F{Quantity}</variableExpression>
</variable>
我們還可以通過(guò)制定初始化級(jí)別來(lái)改變計(jì)算過(guò)程,默認(rèn)的級(jí)別是Report就是變量?jī)H在報(bào)表開(kāi)始處初始化一次,一直到報(bào)表結(jié)束完成計(jì)算。我們可以選擇更低的級(jí)別讓變量在每個(gè)Page、Column或者Group級(jí)別重新初始化。假如我們想計(jì)算計(jì)算每頁(yè)的總數(shù),變量聲明如下:
<variable name="QuantitySum" class="java.lang.Double"
resetType="Page" calculation="Sum">
<variableExpression>$F{Quantity}</variableExpression>
<initialValueExpression>new Double(0) </initialValueExpression>
</variable>
變量將在每一頁(yè)的開(kāi)始處被初始化為0。
引擎還提供了如下的內(nèi)建變量可以在表達(dá)式中直接使用:
PAGE_NUMBER
COLUMN_NUMBER
REPORT_COUNT
PAGE_COUNT
COLUMN_COUNT
GroupName_COUNT
8. 報(bào)表區(qū)域
在創(chuàng)建報(bào)表模板時(shí),我們需要定義報(bào)表區(qū)域的內(nèi)容和風(fēng)格。一個(gè)完全的報(bào)表模板包括如下幾個(gè)區(qū)域:<title>, <pageHeader>, <columnHeader>, <groupHeader>, <detail>, <groupFooter>, <columnFoter>, <pageFooter>, <summary>。區(qū)域是報(bào)表的重要組成部分,它有指定的高度和寬度,并且可以容納直線、矩形、圖片或者文本域等報(bào)表對(duì)象。我們用<band>標(biāo)簽在報(bào)表模板xml文件中定義報(bào)表區(qū)域的內(nèi)容和風(fēng)格。下面是一個(gè)PageHeader區(qū)域的定義,它僅僅包含一條直線和一個(gè)靜態(tài)文本:
<pageHeader>
<band height="30">
<rectangle>
<reportElement x="0" y="0" width="555" height="25"/>
<graphicElement/>
</rectangle>
<staticText>
<reportElement x="0" y="0" width="555" height="25"/>
<textElement textAlignment="Center">
<font fontName="Helvetica" size="18"/>
</textElement>
<text>Northwind Order List</text>
</staticText>
</band>
</pageHeader>
9. 分組
組表示一種分組組織數(shù)據(jù)的方式。填充報(bào)表數(shù)據(jù)時(shí),JasperReport引擎計(jì)算所有定義的分組表達(dá)式檢查是否出現(xiàn)組邊界(表達(dá)式的值改變),如果遇到組邊界則將<groupFooter>和<groupHeader>報(bào)表區(qū)域加入報(bào)表。
報(bào)表可以包含任意多的分組,組在報(bào)表中的聲明順序很重要,因?yàn)榻M之間相互包含。一個(gè)組包含其后聲明組依此類推,一個(gè)大的組遇到邊界,所有的子組都將被重新初始化。一個(gè)報(bào)表組跟其數(shù)據(jù)分組表達(dá)式一起定義,同時(shí)還需要定義兩個(gè)報(bào)表分組區(qū)域:分組頭區(qū)域和分組尾區(qū)域。
關(guān)于分組的詳細(xì)信息參考分組的報(bào)表示例。
10. 字體和Unicode支持
現(xiàn)在你可以用任何語(yǔ)言來(lái)創(chuàng)建報(bào)表。<font>元素的新屬性允許在Java字體和PDF字體間映射。PDF使用特定的字體集使得以前的JasperReport版本沒(méi)有辦法使用它們。新的屬性使用戶可以指定什么PDF字體用來(lái)顯示不同的字符集(pdfFontName屬性),什么編碼類型(pdfEncoding屬性)和是否將字體嵌入PDF文檔(isPdfEmbedded)。
為了簡(jiǎn)化字體集的使用,增加了一個(gè)新屬性<reportFont>。報(bào)表字體是報(bào)表級(jí)別的字體定義用來(lái)作為報(bào)表中其他顯示對(duì)象的默認(rèn)字體。因?yàn)閷?duì)國(guó)際字符集的支持不知為何被綁定到iText庫(kù),你可以在iText documentation.文當(dāng)中找到更多關(guān)于如何用不同的語(yǔ)言不同的字符集創(chuàng)建PDF文檔的信息。
11. Scriptlets
所有的報(bào)表顯示數(shù)據(jù)來(lái)自報(bào)表變量和報(bào)表字段,這些數(shù)據(jù)可以用報(bào)表變量和表達(dá)式來(lái)處理。
有時(shí)候報(bào)表需要對(duì)變量進(jìn)行特殊處理,一些變量可能在報(bào)表的某個(gè)事件中(報(bào)表開(kāi)始、換頁(yè)或者換列)被重新初始化,而且,變量在每次從數(shù)據(jù)源中獲得數(shù)據(jù)時(shí)(每一行)都被計(jì)算。而僅僅用簡(jiǎn)單變量表達(dá)式無(wú)法實(shí)現(xiàn)所有的復(fù)雜功能,這時(shí)就要使用Scriptlet。
因?yàn)镾criptlet主要和報(bào)表變量一起工作,完全控制scriptlet的執(zhí)行時(shí)機(jī)非常重要。JasperReport允許根據(jù)報(bào)表事件定制Java編碼BEFORE或者AFTER:Report、Page、Column和Group的初始化來(lái)執(zhí)行Scriptlet。
要使用Scriptlet,開(kāi)發(fā)者只需要通過(guò)繼承net.sf.jasperreports.engine.JRAbstractScriptlet或者net.sf.jasperreports.engine.JRDefaultScriptlet來(lái)創(chuàng)建Scritplet類。該定制的Scriptlet類會(huì)被指定為<jasperReport>的scritpletClass屬性的值。創(chuàng)建Scriptlet時(shí)開(kāi)發(fā)這需要實(shí)現(xiàn)或者重載如beforeReportInit(), afterReportInit(), beforePageInit(), afterPageInit(), beforeGroupInit(), afterGroupInit(),等方法。這些方法將在填充數(shù)據(jù)時(shí)被引擎在適當(dāng)?shù)臅r(shí)候調(diào)用。
有一個(gè)叫做REPORT_SCRIPTLET的默認(rèn)報(bào)表參數(shù)表示對(duì)報(bào)表引擎在填充數(shù)據(jù)時(shí)實(shí)例化的Scriptlet對(duì)象的引用。它可以在整個(gè)報(bào)表的任何表達(dá)式中使用來(lái)調(diào)用Scriptlet的特定方法使整個(gè)報(bào)表機(jī)制更加靈活。
12. 子報(bào)表
子報(bào)表是報(bào)表工具的重要特性,它允許創(chuàng)建更復(fù)雜的報(bào)表并簡(jiǎn)化設(shè)計(jì)工作。自報(bào)表在創(chuàng)建主從報(bào)表時(shí)特別有用。
如何在JSP頁(yè)面中訪問(wèn)web.xml中的初始化參數(shù)?
例如,如果你有:
<context-param> <param-name>productId</param-name>如何從java代碼中訪問(wèn)web.xml 中的初始化參數(shù)?
你可以使用externalContext的 getInitParameter 方法得到他們.例如 如果你的參數(shù)如下:<context-param> <param-name>connectionString</param-name> <param-value>jdbc:oracle:thin:scott/tiger@cartman:1521:O901DB</param-value> </context-param>你可以使用下面代碼訪問(wèn)connectionString :
FacesContext?fc?=?FacesContext.getCurrentInstance();
String?connection?=?fc.getExternalContext().getInitParameter("connectionString");?
場(chǎng)景:當(dāng)用戶點(diǎn)擊按鈕,調(diào)用JS函數(shù),該函數(shù)隱藏頁(yè)面并且顯示"請(qǐng)等待..."div.你可以使用CSS來(lái)自定義外觀:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <f:loadBundle basename="demo.bundle.Messages" var="Message"/> <html> <head> <title>Input Name Page</title> <script> function gowait() { document.getElementById("main").style.visibility="hidden"; document.getElementById("wait").style.visibility="visible"; } </script> </head> <body bgcolor="white"> <f:view> <div id="main"> <h1><h:outputText value="#{Message.inputname_header}"/></h1> <h:messages style="color: red"/> <h:form id="helloForm"> <h:outputText value="#{Message.prompt}"/> <h:inputText id="userName" value="#{GetNameBean.userName}" required="true"> <f:validateLength minimum="2" maximum="20"/> </h:inputText> <h:commandButton onclick="gowait()" id="submit" action="#{GetNameBean.action}" value="Say Hello" /> </h:form> </div> <div id="wait" style="visibility:hidden; position: absolute; top: 0; left: 0"> <table width="100%" height ="300px"> <tr> <td align="center" valign="middle"> <h2>Please, wait...</h2> </td> </tr> </table> </div> </f:view> </body> </html>
如果你想有一個(gè)動(dòng)畫(huà)gif圖片在"請(qǐng)等待..."中,當(dāng)表單提交后該圖片應(yīng)該從新加載.因此,再一次指定圖片的id,并且添加經(jīng)過(guò)一段時(shí)間延時(shí)后重新加載的代碼.下面是個(gè)例子:
<script> function gowait() { document.getElementById("main").style.visibility="hidden"; document.getElementById("wait").style.visibility="visible"; window.setTimeout('showProgress()', 500); } function showProgress(){ var wg = document.getElementById("waitgif"); wg.src=wg.src; } </script> .... .... .... <img id="waitgif" src="animated.gif">
添加下面的代碼到backing bean的action listener中:
public?void?viewPdf(ActionEvent?event)?{ |
使用backing bean來(lái)添加UIComponents 到頁(yè)面中?
jsp1.jsp:
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <f:view> <head> <title>jsp1</title> <link rel="stylesheet" type="text/css" href="./style.css" title="Style"/> </head> <body bgcolor="#ffffff"> TESTING... <h:form id="form1"> <h:panelGrid id="panelgridtest" binding="#{jsp1Bean.component}"/> </h:form> </body> </f:view> </html>
?
Jsp1Bean.java:
package?test; |
?
faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config> <navigation-rule> <from-view-id>/jsp1</from-view-id> <navigation-case> <from-action>submit</from-action> <to-view-id>/jsp1</to-view-id> <redirect/> </navigation-case> </navigation-rule> <managed-bean> <managed-bean-name>jsp1Bean</managed-bean-name> <managed-bean-class>test.Jsp1Bean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>
如何在BackBean中使用SelectMany 對(duì)象?
This page was made fast and I hope don't be a nuisance to someboby
首先我們定義backbean來(lái)管理我們的 selectMany "list" :
<managed-bean> <managed-bean-name>Manager</managed-bean-name> <managed-bean-class>demo.Manager</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-bean>
現(xiàn)在Manager類應(yīng)該有個(gè) SelectMany object來(lái)管理信息;
?
package?demo; |
?
JSP頁(yè)面的代碼如下:?
<h:selectManyMenu binding="#{Manager.list}">
如果你想使用CheckBox or a ListBox僅僅在JSF文件中該變標(biāo)簽就可以了.如 <h:selectManyCheckBox> by either <h:selectManyListbox> or <h:selectManyCheckbox>, it is great!!
public?void? beforePhase ( PhaseEvent?arg0 )?
{
if ( arg0.getPhaseId () ==?PhaseId.RENDER_RESPONSE )
{
?? FacesContext?facesContext?=?arg0.getFacesContext () ;
?? ViewHandler?vh??=?facesContext.getApplication () .getViewHandler () ;
?? UIViewRoot?newRoot?=?vh.createView ( facesContext,? "/yourNew.jsp" ) ;
?? facesContext.setViewRoot ( newRoot ) ;
}
How to foward to another JSP from an actionListener ActionEvent
簡(jiǎn)單的方法是在commandlink中添加一個(gè) action attribute? .然后你有一個(gè)actionListener?和 an action Attribute, 兩個(gè)都是可行的.
但是你還可以使用下面的代碼:
String?viewId?=?"/edit.jsp"; 如何從java代碼中重定向一個(gè)JSF頁(yè)面示例代碼如下:
|
下面是一個(gè)email驗(yàn)證器的示例: ?
EmailValidator.java:
import?javax.faces.application.FacesMessage; |
Tags.java:
import?javax.faces.application.Application; |
?
?
?
EmailValidatorTag.java:
import?javax.faces.component.UIComponent; |
?
?
faces-config.xml:
<validator> <validator-id>Email</validator-id> <validator-class>EmailValidator</validator-class> </validator>
mytags.tld:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlibversion>1.0</tlibversion> <jspversion>1.2</jspversion> <shortname>mytags</shortname> <uri>mytags</uri> <tag> <name>validateEmail</name> <tagclass>EmailValidatorTag</tagclass> <attribute> <name>errorMessage</name> <description>message if a validation error occurs</description> </attribute> </tag> </taglib>
?
mypage.jsp:
<h:inputText id="email" required="true"> <mytags:validateEmail errorMessage="#{bean.message}"/> </h:inputText>
如何為每一個(gè)錯(cuò)誤消息顯示一個(gè)圖片
使用CSS style來(lái)實(shí)現(xiàn)該功能.例如,你有如下的代碼來(lái)顯示消息:<div align="center"> <h:messages id="errMsgs" styleClass="errorFeedback" layout="table" /> </div>
??errorFeedback style class 可能是下面的代碼:
.errorFeedback { color: black; vertical-align: middle; background-image: url(/AccountSetup/images/warning_feedback.gif); background-repeat: no-repeat; background-position: left top; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold; line-height: 18px; font-size: 10pt; text-align: left; text-indent: 22px;}
(轉(zhuǎn)) 從 Filter和Servlet中如何訪問(wèn)FacesContext?
?
|
J2EE 全面簡(jiǎn)介(轉(zhuǎn)載)
本文從五個(gè)方面對(duì)J2EE進(jìn)行了比較全面的介紹。從J2EE的概念說(shuō)起,到它的優(yōu)勢(shì),到J2EE典型的四層模型,和它的框架結(jié)構(gòu),最后是J2EE十三種核心技術(shù)的一個(gè)簡(jiǎn)介。本文分門(mén)別類的對(duì)J2EE中的服務(wù),組件,層次,容器,API都做了比較詳細(xì)的介紹,相信看完此文,讀者會(huì)對(duì)J2EE有一個(gè)更清晰的認(rèn)識(shí)。
目前,Java 2平臺(tái)有3個(gè)版本,它們是適用于小型設(shè)備和智能卡的Java 2平臺(tái)Micro版(Java 2 Platform Micro Edition,J2ME)、適用于桌面系統(tǒng)的Java 2平臺(tái)標(biāo)準(zhǔn)版(Java 2 Platform Standard Edition,J2SE)、適用于創(chuàng)建服務(wù)器應(yīng)用程序和服務(wù)的Java 2平臺(tái)企業(yè)版(Java 2 Platform Enterprise Edition,J2EE)。
J2EE是一種利用Java 2平臺(tái)來(lái)簡(jiǎn)化企業(yè)解決方案的開(kāi)發(fā)、部署和管理相關(guān)的復(fù)雜問(wèn)題的體系結(jié)構(gòu)。J2EE技術(shù)的基礎(chǔ)就是核心Java平臺(tái)或Java 2平臺(tái)的標(biāo)準(zhǔn)版,J2EE不僅鞏固了標(biāo)準(zhǔn)版中的許多優(yōu)點(diǎn),例如"編寫(xiě)一次、隨處運(yùn)行"的特性、方便存取數(shù)據(jù)庫(kù)的JDBC API、CORBA技術(shù)以及能夠在Internet應(yīng)用中保護(hù)數(shù)據(jù)的安全模式等等,同時(shí)還提供了對(duì) EJB(Enterprise JavaBeans)、Java Servlets API、JSP(Java Server Pages)以及XML技術(shù)的全面支持。其最終目的就是成為一個(gè)能夠使企業(yè)開(kāi)發(fā)者大幅縮短投放市場(chǎng)時(shí)間的體系結(jié)構(gòu)。
J2EE體系結(jié)構(gòu)提供中間層集成框架用來(lái)滿足無(wú)需太多費(fèi)用而又需要高可用性、高可靠性以及可擴(kuò)展性的應(yīng)用的需求。通過(guò)提供統(tǒng)一的開(kāi)發(fā)平臺(tái),J2EE降低了開(kāi)發(fā)多層應(yīng)用的費(fèi)用和復(fù)雜性,同時(shí)提供對(duì)現(xiàn)有應(yīng)用程序集成強(qiáng)有力支持,完全支持Enterprise JavaBeans,有良好的向?qū)еС执虬筒渴饝?yīng)用,添加目錄支持,增強(qiáng)了安全機(jī)制,提高了性能。
![]() ![]() |
![]()
|
J2EE為搭建具有可伸縮性、靈活性、易維護(hù)性的商務(wù)系統(tǒng)提供了良好的機(jī)制:
- 保留現(xiàn)存的IT資產(chǎn): 由于企業(yè)必須適應(yīng)新的商業(yè)需求,利用已有的企業(yè)信息系統(tǒng)方面的投資,而不是重新制定全盤(pán)方案就變得很重要。這樣,一個(gè)以漸進(jìn)的(而不是激進(jìn)的,全盤(pán)否定的)方式建立在已有系統(tǒng)之上的服務(wù)器端平臺(tái)機(jī)制是公司所需求的。J2EE架構(gòu)可以充分利用用戶原有的投資,如一些公司使用的BEA Tuxedo、IBM CICS, IBM Encina,、Inprise VisiBroker 以及Netscape Application Server。這之所以成為可能是因?yàn)镴2EE擁有廣泛的業(yè)界支持和一些重要的'企業(yè)計(jì)算'領(lǐng)域供應(yīng)商的參與。每一個(gè)供應(yīng)商都對(duì)現(xiàn)有的客戶提供了不用廢棄已有投資,進(jìn)入可移植的J2EE領(lǐng)域的升級(jí)途徑。由于基于J2EE平臺(tái)的產(chǎn)品幾乎能夠在任何操作系統(tǒng)和硬件配置上運(yùn)行,現(xiàn)有的操作系統(tǒng)和硬件也能被保留使用。
- 高效的開(kāi)發(fā): J2EE允許公司把一些通用的、很繁瑣的服務(wù)端任務(wù)交給中間件供應(yīng)商去完成。這樣開(kāi)發(fā)人員可以集中精力在如何創(chuàng)建商業(yè)邏輯上,相應(yīng)地縮短了開(kāi)發(fā)時(shí)間。高級(jí)中間件供應(yīng)商提供以下這些復(fù)雜的中間件服務(wù):
- 狀態(tài)管理服務(wù) -- 讓開(kāi)發(fā)人員寫(xiě)更少的代碼,不用關(guān)心如何管理狀態(tài),這樣能夠更快地完成程序開(kāi)發(fā)。
- 持續(xù)性服務(wù) -- 讓開(kāi)發(fā)人員不用對(duì)數(shù)據(jù)訪問(wèn)邏輯進(jìn)行編碼就能編寫(xiě)應(yīng)用程序,能生成更輕巧,與數(shù)據(jù)庫(kù)無(wú)關(guān)的應(yīng)用程序,這種應(yīng)用程序更易于開(kāi)發(fā)與維護(hù)。
- 分布式共享數(shù)據(jù)對(duì)象CACHE服務(wù) -- 讓開(kāi)發(fā)人員編制高性能的系統(tǒng),極大提高整體部署的伸縮性。
- 支持異構(gòu)環(huán)境: J2EE能夠開(kāi)發(fā)部署在異構(gòu)環(huán)境中的可移植程序。基于J2EE的應(yīng)用程序不依賴任何特定操作系統(tǒng)、中間件、硬件。因此設(shè)計(jì)合理的基于J2EE的程序只需開(kāi)發(fā)一次就可部署到各種平臺(tái)。這在典型的異構(gòu)企業(yè)計(jì)算環(huán)境中是十分關(guān)鍵的。J2EE標(biāo)準(zhǔn)也允許客戶訂購(gòu)與J2EE兼容的第三方的現(xiàn)成的組件,把他們部署到異構(gòu)環(huán)境中,節(jié)省了由自己制訂整個(gè)方案所需的費(fèi)用。
- 可伸縮性: 企業(yè)必須要選擇一種服務(wù)器端平臺(tái),這種平臺(tái)應(yīng)能提供極佳的可伸縮性去滿足那些在他們系統(tǒng)上進(jìn)行商業(yè)運(yùn)作的大批新客戶。基于J2EE平臺(tái)的應(yīng)用程序可被部署到各種操作系統(tǒng)上。例如可被部署到高端UNIX與大型機(jī)系統(tǒng),這種系統(tǒng)單機(jī)可支持64至256個(gè)處理器。(這是NT服務(wù)器所望塵莫及的)J2EE領(lǐng)域的供應(yīng)商提供了更為廣泛的負(fù)載平衡策略。能消除系統(tǒng)中的瓶頸,允許多臺(tái)服務(wù)器集成部署。這種部署可達(dá)數(shù)千個(gè)處理器,實(shí)現(xiàn)可高度伸縮的系統(tǒng),滿足未來(lái)商業(yè)應(yīng)用的需要。
- 穩(wěn)定的可用性: 一個(gè)服務(wù)器端平臺(tái)必須能全天候運(yùn)轉(zhuǎn)以滿足公司客戶、合作伙伴的需要。因?yàn)镮NTERNET是全球化的、無(wú)處不在的,即使在夜間按計(jì)劃停機(jī)也可能造成嚴(yán)重?fù)p失。若是意外停機(jī),那會(huì)有災(zāi)難性后果。J2EE部署到可靠的操作環(huán)境中,他們支持長(zhǎng)期的可用性。一些J2EE部署在WINDOWS環(huán)境中,客戶也可選擇健壯性能更好的操作系統(tǒng)如Sun Solaris、IBM OS/390。最健壯的操作系統(tǒng)可達(dá)到99.999%的可用性或每年只需5分鐘停機(jī)時(shí)間。這是實(shí)時(shí)性很強(qiáng)商業(yè)系統(tǒng)理想的選擇。
![]() ![]() |
![]()
|
J2EE使用多層的分布式應(yīng)用模型,應(yīng)用邏輯按功能劃分為組件,各個(gè)應(yīng)用組件根據(jù)他們所在的層分布在不同的機(jī)器上。事實(shí)上,sun設(shè)計(jì)J2EE的初衷正是為了解決兩層模式(client/server)的弊端,在傳統(tǒng)模式中,客戶端擔(dān)當(dāng)了過(guò)多的角色而顯得臃腫,在這種模式中,第一次部署的時(shí)候比較容易,但難于升級(jí)或改進(jìn),可伸展性也不理想,而且經(jīng)常基于某種專有的協(xié)議?D?D通常是某種數(shù)據(jù)庫(kù)協(xié)議。它使得重用業(yè)務(wù)邏輯和界面邏輯非常困難。現(xiàn)在J2EE 的多層企業(yè)級(jí)應(yīng)用模型將兩層化模型中的不同層面切分成許多層。一個(gè)多層化應(yīng)用能夠?yàn)椴煌拿糠N服務(wù)提供一個(gè)獨(dú)立的層,以下是 J2EE 典型的四層結(jié)構(gòu):
- 運(yùn)行在客戶端機(jī)器上的客戶層組件
- 運(yùn)行在J2EE服務(wù)器上的Web層組件
- 運(yùn)行在J2EE服務(wù)器上的業(yè)務(wù)邏輯層組件
- 運(yùn)行在EIS服務(wù)器上的企業(yè)信息系統(tǒng)(Enterprise information system)層軟件
介.files/fig1.gif)
J2EE應(yīng)用程序組件
J2EE應(yīng)用程序是由組件構(gòu)成的.J2EE組件是具有獨(dú)立功能的軟件單元,它們通過(guò)相關(guān)的類和文件組裝成J2EE應(yīng)用程序,并與其他組件交互。J2EE說(shuō)明書(shū)中定義了以下的J2EE組件:
- 應(yīng)用客戶端程序和applets是客戶層組件.
- Java Servlet和JavaServer Pages(JSP)是web層組件.
- Enterprise JavaBeans(EJB)是業(yè)務(wù)層組件.
客戶層組件
J2EE應(yīng)用程序可以是基于web方式的,也可以是基于傳統(tǒng)方式的.
web 層組件
J2EE web層組件可以是JSP 頁(yè)面或Servlets.按照J(rèn)2EE規(guī)范,靜態(tài)的HTML頁(yè)面和Applets不算是web層組件。
正如下圖所示的客戶層那樣,web層可能包含某些 JavaBean 對(duì)象來(lái)處理用戶輸入,并把輸入發(fā)送給運(yùn)行在業(yè)務(wù)層上的enterprise bean 來(lái)進(jìn)行處理。
介.files/fig2.gif)
業(yè)務(wù)層組件
業(yè)務(wù)層代碼的邏輯用來(lái)滿足銀行,零售,金融等特殊商務(wù)領(lǐng)域的需要,由運(yùn)行在業(yè)務(wù)層上的enterprise bean 進(jìn)行處理. 下圖表明了一個(gè)enterprise bean 是如何從客戶端程序接收數(shù)據(jù),進(jìn)行處理(如果必要的話), 并發(fā)送到EIS 層儲(chǔ)存的,這個(gè)過(guò)程也可以逆向進(jìn)行。
有三種企業(yè)級(jí)的bean: 會(huì)話(session) beans, 實(shí)體(entity) beans, 和消息驅(qū)動(dòng)(message-driven) beans. 會(huì)話bean 表示與客戶端程序的臨時(shí)交互. 當(dāng)客戶端程序執(zhí)行完后, 會(huì)話bean 和相關(guān)數(shù)據(jù)就會(huì)消失. 相反, 實(shí)體bean 表示數(shù)據(jù)庫(kù)的表中一行永久的記錄. 當(dāng)客戶端程序中止或服務(wù)器關(guān)閉時(shí), 就會(huì)有潛在的服務(wù)保證實(shí)體bean 的數(shù)據(jù)得以保存.消息驅(qū)動(dòng) bean 結(jié)合了會(huì)話bean 和 JMS的消息監(jiān)聽(tīng)器的特性, 允許一個(gè)業(yè)務(wù)層組件異步接收J(rèn)MS 消息.
介.files/fig3.gif)
企業(yè)信息系統(tǒng)層
企業(yè)信息系統(tǒng)層處理企業(yè)信息系統(tǒng)軟件包括企業(yè)基礎(chǔ)建設(shè)系統(tǒng)例如企業(yè)資源計(jì)劃 (ERP), 大型機(jī)事務(wù)處理, 數(shù)據(jù)庫(kù)系統(tǒng),和其它的遺留信息系統(tǒng). 例如,J2EE 應(yīng)用組件可能為了數(shù)據(jù)庫(kù)連接需要訪問(wèn)企業(yè)信息系統(tǒng)
![]() ![]() |
![]()
|
這種基于組件,具有平臺(tái)無(wú)關(guān)性的J2EE 結(jié)構(gòu)使得J2EE 程序的編寫(xiě)十分簡(jiǎn)單,因?yàn)闃I(yè)務(wù)邏輯被封裝成可復(fù)用的組件,并且J2EE 服務(wù)器以容器的形式為所有的組件類型提供后臺(tái)服務(wù). 因?yàn)槟悴挥米约洪_(kāi)發(fā)這種服務(wù), 所以你可以集中精力解決手頭的業(yè)務(wù)問(wèn)題.
容器和服務(wù)
容器設(shè)置定制了J2EE服務(wù)器所提供得內(nèi)在支持,包括安全,事務(wù)管理,JNDI(Java Naming and Directory Interface)尋址,遠(yuǎn)程連接等服務(wù),以下列出最重要的幾種服務(wù):
- J2EE安全(Security)模型可以讓你配置 web 組件或enterprise bean ,這樣只有被授權(quán)的用戶才能訪問(wèn)系統(tǒng)資源. 每一客戶屬于一個(gè)特別的角色,而每個(gè)角色只允許激活特定的方法。你應(yīng)在enterprise bean的布置描述中聲明角色和可被激活的方法。由于這種聲明性的方法,你不必編寫(xiě)加強(qiáng)安全性的規(guī)則。
- J2EE 事務(wù)管理(Transaction Management)模型讓你指定組成一個(gè)事務(wù)中所有方法間的關(guān)系,這樣一個(gè)事務(wù)中的所有方法被當(dāng)成一個(gè)單一的單元. 當(dāng)客戶端激活一個(gè)enterprise bean中的方法,容器介入一管理事務(wù)。因有容器管理事務(wù),在enterprise bean中不必對(duì)事務(wù)的邊界進(jìn)行編碼。要求控制分布式事務(wù)的代碼會(huì)非常復(fù)雜。你只需在布置描述文件中聲明enterprise bean的事務(wù)屬性,而不用編寫(xiě)并調(diào)試復(fù)雜的代碼。容器將讀此文件并為你處理此enterprise bean的事務(wù)。
- JNDI 尋址(JNDI Lookup)服務(wù)向企業(yè)內(nèi)的多重名字和目錄服務(wù)提供了一個(gè)統(tǒng)一的接口,這樣應(yīng)用程序組件可以訪問(wèn)名字和目錄服務(wù).
- J2EE遠(yuǎn)程連接(Remote Client Connectivity)模型管理客戶端和enterprise bean間的低層交互. 當(dāng)一個(gè)enterprise bean創(chuàng)建后, 一個(gè)客戶端可以調(diào)用它的方法就象它和客戶端位于同一虛擬機(jī)上一樣.
- 生存周期管理(Life Cycle Management)模型管理enterprise bean的創(chuàng)建和移除,一個(gè)enterprise bean在其生存周期中將會(huì)歷經(jīng)幾種狀態(tài)。容器創(chuàng)建enterprise bean,并在可用實(shí)例池與活動(dòng)狀態(tài)中移動(dòng)他,而最終將其從容器中移除。即使可以調(diào)用enterprise bean的create及remove方法,容器也將會(huì)在后臺(tái)執(zhí)行這些任務(wù)。
- 數(shù)據(jù)庫(kù)連接池(Database Connection Pooling)模型是一個(gè)有價(jià)值的資源。獲取數(shù)據(jù)庫(kù)連接是一項(xiàng)耗時(shí)的工作,而且連接數(shù)非常有限。容器通過(guò)管理連接池來(lái)緩和這些問(wèn)題。enterprise bean可從池中迅速獲取連接。在bean釋放連接之可為其他bean使用。
容器類型
J2EE應(yīng)用組件可以安裝部署到以下幾種容器中去:
- EJB 容器管理所有J2EE 應(yīng)用程序中企業(yè)級(jí)bean 的執(zhí)行. enterprise bean 和它們的容器運(yùn)行在J2EE 服務(wù)器上.
- Web 容器管理所有J2EE 應(yīng)用程序中JSP頁(yè)面和Servlet組件的執(zhí)行. Web 組件和它們的容器運(yùn)行在J2EE 服務(wù)器上.
- 應(yīng)用程序客戶端容器管理所有J2EE應(yīng)用程序中應(yīng)用程序客戶端組件的執(zhí)行. 應(yīng)用程序客戶端和它們的容器運(yùn)行在J2EE 服務(wù)器上.
- Applet 容器是運(yùn)行在客戶端機(jī)器上的web瀏覽器和 Java 插件的結(jié)合.
介.files/fig4.gif)
![]() ![]() |
![]()
|
J2EE平臺(tái)由一整套服務(wù)(Services)、應(yīng)用程序接口(APIs)和協(xié)議構(gòu)成,它對(duì)開(kāi)發(fā)基于Web的多層應(yīng)用提供了功能支持,下面對(duì)J2EE中的13種技術(shù)規(guī)范進(jìn)行簡(jiǎn)單的描述(限于篇幅,這里只能進(jìn)行簡(jiǎn)單的描述):
- JDBC(Java Database Connectivity): JDBC API為訪問(wèn)不同的數(shù)據(jù)庫(kù)提供了一種統(tǒng)一的途徑,象ODBC一樣,JDBC對(duì)開(kāi)發(fā)者屏蔽了一些細(xì)節(jié)問(wèn)題,另外,JDCB對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)也具有平臺(tái)無(wú)關(guān)性。
- JNDI(Java Name and Directory Interface): JNDI API被用于執(zhí)行名字和目錄服務(wù)。它提供了一致的模型來(lái)存取和操作企業(yè)級(jí)的資源如DNS和LDAP,本地文件系統(tǒng),或應(yīng)用服務(wù)器中的對(duì)象。
- EJB(Enterprise JavaBean): J2EE技術(shù)之所以贏得某體廣泛重視的原因之一就是EJB。它們提供了一個(gè)框架來(lái)開(kāi)發(fā)和實(shí)施分布式商務(wù)邏輯,由此很顯著地簡(jiǎn)化了具有可伸縮性和高度復(fù)雜的企業(yè)級(jí)應(yīng)用的開(kāi)發(fā)。EJB規(guī)范定義了EJB組件在何時(shí)如何與它們的容器進(jìn)行交互作用。容器負(fù)責(zé)提供公用的服務(wù),例如目錄服務(wù)、事務(wù)管理、安全性、資源緩沖池以及容錯(cuò)性。但這里值得注意的是,EJB并不是實(shí)現(xiàn)J2EE的唯一途徑。正是由于J2EE的開(kāi)放性,使得有的廠商能夠以一種和EJB平行的方式來(lái)達(dá)到同樣的目的。
- RMI(Remote Method Invoke): 正如其名字所表示的那樣,RMI協(xié)議調(diào)用遠(yuǎn)程對(duì)象上方法。它使用了序列化方式在客戶端和服務(wù)器端傳遞數(shù)據(jù)。RMI是一種被EJB使用的更底層的協(xié)議。
- Java IDL/CORBA: 在Java IDL的支持下,開(kāi)發(fā)人員可以將Java和CORBA集成在一起。他們可以創(chuàng)建Java對(duì)象并使之可在CORBA ORB中展開(kāi), 或者他們還可以創(chuàng)建Java類并作為和其它ORB一起展開(kāi)的CORBA對(duì)象的客戶。后一種方法提供了另外一種途徑,通過(guò)它Java可以被用于將你的新的應(yīng)用和舊的系統(tǒng)相集成。
- JSP(Java Server Pages): JSP頁(yè)面由HTML代碼和嵌入其中的Java代碼所組成。服務(wù)器在頁(yè)面被客戶端所請(qǐng)求以后對(duì)這些Java代碼進(jìn)行處理,然后將生成的HTML頁(yè)面返回給客戶端的瀏覽器。
- Java Servlet: Servlet是一種小型的Java程序,它擴(kuò)展了Web服務(wù)器的功能。作為一種服務(wù)器端的應(yīng)用,當(dāng)被請(qǐng)求時(shí)開(kāi)始執(zhí)行,這和CGI Perl腳本很相似。Servlet提供的功能大多與JSP類似,不過(guò)實(shí)現(xiàn)的方式不同。JSP通常是大多數(shù)HTML代碼中嵌入少量的Java代碼,而servlets全部由Java寫(xiě)成并且生成HTML。
- XML(Extensible Markup Language): XML是一種可以用來(lái)定義其它標(biāo)記語(yǔ)言的語(yǔ)言。它被用來(lái)在不同的商務(wù)過(guò)程中共享數(shù)據(jù)。XML的發(fā)展和Java是相互獨(dú)立的,但是,它和Java具有的相同目標(biāo)正是平臺(tái)獨(dú)立性。通過(guò)將Java和XML的組合,您可以得到一個(gè)完美的具有平臺(tái)獨(dú)立性的解決方案。
- JMS(Java Message Service): MS是用于和面向消息的中間件相互通信的應(yīng)用程序接口(API)。它既支持點(diǎn)對(duì)點(diǎn)的域,有支持發(fā)布/訂閱(publish/subscribe)類型的域,并且提供對(duì)下列類型的支持:經(jīng)認(rèn)可的消息傳遞,事務(wù)型消息的傳遞,一致性消息和具有持久性的訂閱者支持。JMS還提供了另一種方式來(lái)對(duì)您的應(yīng)用與舊的后臺(tái)系統(tǒng)相集成。
- JTA(Java Transaction Architecture): JTA定義了一種標(biāo)準(zhǔn)的API,應(yīng)用系統(tǒng)由此可以訪問(wèn)各種事務(wù)監(jiān)控。
- JTS(Java Transaction Service): JTS是CORBA OTS事務(wù)監(jiān)控的基本的實(shí)現(xiàn)。JTS規(guī)定了事務(wù)管理器的實(shí)現(xiàn)方式。該事務(wù)管理器是在高層支持Java Transaction API (JTA)規(guī)范,并且在較底層實(shí)現(xiàn)OMG OTS specification的Java映像。JTS事務(wù)管理器為應(yīng)用服務(wù)器、資源管理器、獨(dú)立的應(yīng)用以及通信資源管理器提供了事務(wù)服務(wù)。
- JavaMail: JavaMail是用于存取郵件服務(wù)器的API,它提供了一套郵件服務(wù)器的抽象類。不僅支持SMTP服務(wù)器,也支持IMAP服務(wù)器。
- JTA(JavaBeans Activation Framework): JavaMail利用JAF來(lái)處理MIME編碼的郵件附件。MIME的字節(jié)流可以被轉(zhuǎn)換成Java對(duì)象,或者轉(zhuǎn)換自Java對(duì)象。大多數(shù)應(yīng)用都可以不需要直接使用JAF。
from:http://www.javaeye.com/topic/9706
數(shù)據(jù)庫(kù)對(duì)象的緩存策略
前言
本文探討Jive(曾經(jīng)開(kāi)源的Java論壇)和Hibernate(Java開(kāi)源持久層)的數(shù)據(jù)庫(kù)對(duì)象的緩存策略,并闡述作者本人的Lightor(Java開(kāi)源持久層)采用的數(shù)據(jù)庫(kù)對(duì)象緩存策略。
本文的探討基于以前開(kāi)源的Jive代碼,Hibernate2.1.7源碼,和作者本人的Lightor代碼。
本文用ID (Identifier的縮寫(xiě))來(lái)代表數(shù)據(jù)記錄的關(guān)鍵字。
數(shù)據(jù)對(duì)象查詢一般分為兩種:條件查詢,返回一個(gè)滿足條件的數(shù)據(jù)對(duì)象列表; ID查詢,返回ID對(duì)應(yīng)的數(shù)據(jù)對(duì)象。
本文主要探討“條件查詢”和“ID查詢”這兩種情況的緩存策略。
本文只探討一個(gè)JVM內(nèi)的數(shù)據(jù)緩存策略,不涉及分布式緩存;本文只探討對(duì)應(yīng)單表的數(shù)據(jù)對(duì)象的緩存,不涉及關(guān)聯(lián)表對(duì)象的情況。
一、Jive的緩存策略
1.Jive的緩存策略的過(guò)程描述:
(1)條件查詢的時(shí)候,Jive用 select id from table_name where …. (只選擇ID字段)這樣的SQL語(yǔ)句查詢數(shù)據(jù)庫(kù),來(lái)獲得一個(gè)ID列表。
(2) Jive根據(jù)ID列表中的每個(gè)ID,首先查看緩存中是否存在對(duì)應(yīng)ID的數(shù)據(jù)對(duì)象:如果存在,那么直接取出,加入到 結(jié)果列表中;如果不存在,那么通過(guò)一條select * from table_name where id = {ID value} 這樣的SQL查詢數(shù)據(jù)庫(kù),取出對(duì)應(yīng)的數(shù)據(jù)對(duì)象,放入到結(jié)果列表,并把這個(gè)數(shù)據(jù)對(duì)象按照ID放入到緩存中。
(3) ID查詢的時(shí)候,Jive執(zhí)行類似第(2)步的過(guò)程,先從緩存中查找該ID,查不到,再查詢數(shù)據(jù)庫(kù),然后把結(jié)果放入到緩存。
(4) 刪除、更新、增加數(shù)據(jù)的時(shí)候,同時(shí)更新緩存。
2.Jive緩存策略的優(yōu)點(diǎn):
(1) ID查詢的時(shí)候,如果該ID已經(jīng)存在于緩存中,那么可以直接取出。節(jié)省了一條數(shù)據(jù)庫(kù)查詢。
(2) 當(dāng)多次條件查詢的結(jié)果集相交的情況下,交集里面的數(shù)據(jù)對(duì)象不用重復(fù)從數(shù)據(jù)庫(kù)整個(gè)獲取,直接從緩存中獲取即可。
比如,第一次查詢的ID列表為{1, 2},然后根據(jù)ID列表的ID從數(shù)據(jù)庫(kù)中一個(gè)一個(gè)取出數(shù)據(jù)對(duì)象,結(jié)果集為{a(id = 1), b(id = 2)}。
下一次查詢的ID列表為{2, 3},由于ID = 2的數(shù)據(jù)對(duì)象已經(jīng)存在于緩存中,那么只要從數(shù)據(jù)庫(kù)中取出ID = 3的數(shù)據(jù)對(duì)象即可。
3.Jive緩存策略的缺點(diǎn):
(1) 在根據(jù)條件查找數(shù)據(jù)對(duì)象列表的過(guò)程中,DAO的第(1)步用來(lái)獲得ID列表的那一次數(shù)據(jù)庫(kù)查詢,是必不可少的。
(2) 如果第(1)步返回的ID列表中有n個(gè)ID,在最壞的命中率(緩存中一個(gè)對(duì)應(yīng)ID都沒(méi)有)情況下,Jive還要再查詢n次數(shù)據(jù)庫(kù)。最壞情況下,共需要n + 1數(shù)據(jù)庫(kù)查詢。
二、Hibernate的二級(jí)緩存策略
Hibernate用Session類包裝了數(shù)據(jù)庫(kù)連接從打開(kāi)到關(guān)閉的過(guò)程。
Session內(nèi)部維護(hù)一個(gè)數(shù)據(jù)對(duì)象集合,包括了本Session內(nèi)選取的、操作的數(shù)據(jù)對(duì)象。這稱為Session內(nèi)部緩存,是Hibernate的第一級(jí)最快緩存,屬于Hibernate的既定行為,不需要進(jìn)行配置(也沒(méi)有辦法配置 :-)。
Session的生命期很短,存在于Session內(nèi)部的第一級(jí)最快緩存的生命期當(dāng)然也很短,命中率自然也很低。當(dāng)然,這個(gè)Session內(nèi)部緩存的主要作用是保持Session內(nèi)部數(shù)據(jù)狀態(tài)同步。
如果需要跨Session的命中率較高的全局緩存,那么必須對(duì)Hibernate進(jìn)行二級(jí)緩存配置。一般來(lái)說(shuō),同樣數(shù)據(jù)類型(Class)的數(shù)據(jù)對(duì)象,共用一個(gè)二級(jí)緩存(或其中的同一塊)。
1.Hibernate二級(jí)緩存策略的過(guò)程描述:
(1)條件查詢的時(shí)候,總是發(fā)出一條select * from table_name where …. (選擇所有字段)這樣的SQL語(yǔ)句查詢數(shù)據(jù)庫(kù),一次獲得所有的數(shù)據(jù)對(duì)象。
(2) 把獲得的所有數(shù)據(jù)對(duì)象根據(jù)ID放入到第二級(jí)緩存中。
(3) 當(dāng)Hibernate根據(jù)ID訪問(wèn)數(shù)據(jù)對(duì)象的時(shí)候,首先從Session一級(jí)緩存中查;查不到,如果配置了二級(jí)緩存,那么從二級(jí)緩存中查;查不到,再查詢數(shù)據(jù)庫(kù),把結(jié)果按照ID放入到緩存。
(4) 刪除、更新、增加數(shù)據(jù)的時(shí)候,同時(shí)更新緩存。
2.Hibernate二級(jí)緩存策略的優(yōu)點(diǎn):
(1) 具有Jive緩存策略同樣的第(1)條優(yōu)點(diǎn):ID查詢的時(shí)候,如果該ID已經(jīng)存在于緩存中,那么可以直接取出。節(jié)省了一條數(shù)據(jù)庫(kù)查詢。
(2) 不具有Jive緩存策略的第(2)條缺點(diǎn),即hibernate不會(huì)有最壞情況下的 n + 1次數(shù)據(jù)庫(kù)查詢。
3.Hibernate二級(jí)緩存策略的缺點(diǎn):
(1) 同Jive緩存策略的第(1)條缺點(diǎn)一樣,條件查詢的時(shí)候,第(1)步的數(shù)據(jù)庫(kù)查詢語(yǔ)句是不可少的。而且Hibernate選擇所有的字段,比只選擇ID字段花費(fèi)的時(shí)間和空間都多。
(2) 不具備Jive緩存策略的第(2)條優(yōu)點(diǎn)。條件查詢的時(shí)候,必須把數(shù)據(jù)庫(kù)對(duì)象從數(shù)據(jù)庫(kù)中整個(gè)取出,即使該數(shù)據(jù)庫(kù)的ID已經(jīng)存在于緩存中。
三、Hibernate的Query緩存策略
可以看到,Jive緩存和Hibernate的二級(jí)緩存策略,都只是針對(duì)于ID查詢的緩存策略,對(duì)于條件查詢則毫無(wú)作用。(盡管Jive緩存的第(2)個(gè)優(yōu)點(diǎn),能夠避免重復(fù)從數(shù)據(jù)庫(kù)獲取同一個(gè)ID對(duì)應(yīng)的數(shù)據(jù)對(duì)象,但select id from …這條數(shù)據(jù)庫(kù)查詢是每次條件查詢都必不可少的)。
為此,Hibernate提供了針對(duì)條件查詢的Query緩存。
1.Hibernate的Query緩存策略的過(guò)程描述:
(1) 條件查詢的請(qǐng)求一般都包括如下信息:SQL, SQL需要的參數(shù),記錄范圍(起始位置rowStart,最大記錄個(gè)數(shù)maxRows),等。
(2) Hibernate首先根據(jù)這些信息組成一個(gè)Query Key,根據(jù)這個(gè)Query Key到Query緩存中查找對(duì)應(yīng)的結(jié)果列表。如果存在,那么返回這個(gè)結(jié)果列表;如果不存在,查詢數(shù)據(jù)庫(kù),獲取結(jié)果列表,把整個(gè)結(jié)果列表根據(jù)Query Key放入到Query緩存中。
(3) Query Key中的SQL涉及到一些表名,如果這些表的任何數(shù)據(jù)發(fā)生修改、刪除、增加等操作,這些相關(guān)的Query Key都要從緩存中清空。
2.Hibernate的Query緩存策略的優(yōu)點(diǎn)
(1) 條件查詢的時(shí)候,如果Query Key已經(jīng)存在于緩存,那么不需要再查詢數(shù)據(jù)庫(kù)。命中的情況下,一次數(shù)據(jù)庫(kù)查詢也不需要。
3.Hibernate的Query緩存策略的缺點(diǎn)
(1) 條件查詢涉及到的表中,如果有任何一條記錄增加、刪除、或改變,那么緩存中所有和該表相關(guān)的Query Key都會(huì)失效。
比如,有這樣幾組Query Key,它們的SQL里面都包括table1。
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 21, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c2 = ? …., parameter = ‘a(chǎn)bc’, rowStart = 11, maxRows = 20.
當(dāng)table1的任何數(shù)據(jù)對(duì)象(任何字段)改變、增加、刪除的時(shí)候,這些Query Key對(duì)應(yīng)的結(jié)果集都不能保證沒(méi)有發(fā)生變化。
很難做到根據(jù)數(shù)據(jù)對(duì)象的改動(dòng)精確判斷哪些Query Key對(duì)應(yīng)的結(jié)果集受到影響。最簡(jiǎn)單的實(shí)現(xiàn)方法,就是清空所有SQL包含table1的Query Key。
(2) Query緩存中,Query Key對(duì)應(yīng)的是數(shù)據(jù)對(duì)象列表,假如不同的Query Key對(duì)應(yīng)的數(shù)據(jù)對(duì)象列表有交集,那么,交集部分的數(shù)據(jù)對(duì)象就是重復(fù)存儲(chǔ)的。
比如,Query Key 1對(duì)應(yīng)的數(shù)據(jù)對(duì)象列表為{a(id = 1), b(id = 2)},Query Key 2對(duì)應(yīng)的數(shù)據(jù)對(duì)象列表為{a(id = 1), c(id = 3)},這個(gè)a就在兩個(gè)List同時(shí)存在了兩份。
4.二級(jí)緩存和Query緩存同步的困惑
假如,Query緩存中,一個(gè)Query Key對(duì)應(yīng)的結(jié)果列表為{a (id = 1) , b (id = 2), c (id = 3)}; 二級(jí)緩存里面有也id = 1對(duì)應(yīng)的數(shù)據(jù)對(duì)象a。
這兩個(gè)數(shù)據(jù)對(duì)象a之間是什么關(guān)系?能夠保持狀態(tài)同步嗎?
我閱讀Hibernate的相關(guān)源碼,沒(méi)有發(fā)現(xiàn)兩個(gè)緩存之間的這種同步關(guān)系。
或者兩者之間毫無(wú)關(guān)系。就像我上面所說(shuō)的,只要表數(shù)據(jù)發(fā)生變化,相關(guān)的Query Key都要被清空。所以不用考慮同步問(wèn)題?
四、Lightor的緩存策略
Lightor是我做的Java開(kāi)源持久層框架。Lightor的意思是,Lightweight O/R。Hibernate,JDO,EJB CMP這些持久層框架,都是Layer。Lightor算不上Layer,而只是一個(gè)Helper。這里的O/R意思不是Object/Relational,而是Object/ResultSet的意思。:-)
Lightor的緩存策略,主要參照Hibernate的緩存思路,Lightor的緩存也分為 Query緩存和ID緩存。但其中有一點(diǎn)不同,兩者之間并不是毫無(wú)聯(lián)系的,而是相互關(guān)聯(lián)的。
1.Lightor的緩存策略的過(guò)程描述:
(1) 條件查詢的請(qǐng)求一般都包括如下信息:SQL, 對(duì)應(yīng)SQL的參數(shù),起始記錄位置(rowStart),最大記錄個(gè)數(shù)(maxRows),等。
(2) Lightor首先根據(jù)這些信息組成一個(gè)Query Key,根據(jù)這個(gè)Query Key到Query緩存中查找對(duì)應(yīng)的結(jié)果ID列表。注意,這里獲取的是ID列表。
如果結(jié)果ID列表存在于Query緩存,那么根據(jù)這個(gè)ID列表的每個(gè)ID,到ID緩存中取對(duì)應(yīng)的數(shù)據(jù)對(duì)象。如果所有ID對(duì)應(yīng)的數(shù)據(jù)對(duì)象都找到,那個(gè)返回這個(gè)數(shù)據(jù)對(duì)象結(jié)果列表。注意,這里獲取的是整個(gè)數(shù)據(jù)對(duì)象(所有字段)的列表。
如果結(jié)果ID列表不存在于Query緩存,或者結(jié)果ID列表中的某一個(gè)ID不存在于ID緩存,那么,就查詢數(shù)據(jù)庫(kù),獲取結(jié)果列表。然后,把獲取的每個(gè)數(shù)據(jù)對(duì)象按照ID放入到ID緩存;并組裝成一個(gè)ID列表,按照Query Key存放到Query緩存中。注意,這里是把ID列表,而不是整個(gè)對(duì)象列表,放入到Query緩存中。
(3) ID查詢的時(shí)候,Lightor先從ID緩存中查找該ID,如果不存在,那么查詢數(shù)據(jù)庫(kù),把結(jié)果放入ID緩存。
(4) Query Key中的SQL涉及到一些表名,如果這些表的任何數(shù)據(jù)發(fā)生修改、刪除、增加等操作,這些相關(guān)的Query Key都要從緩存中清空。
2.Lightor的緩存策略的優(yōu)點(diǎn)
(1) Lightor的ID緩存具有Jive緩存,和Hibernate二級(jí)ID緩存的優(yōu)點(diǎn)。ID查詢的時(shí)候,如果該ID已經(jīng)存在于緩存中,那么可以直接取出。節(jié)省了一條數(shù)據(jù)庫(kù)查詢。
(2) Lightor的Query緩存具有Hibernate的Query緩存的優(yōu)點(diǎn)。條件查詢的時(shí)候,如果Query Key已經(jīng)存在于緩存,那么不需要再查詢數(shù)據(jù)庫(kù)。命中的情況下,一次數(shù)據(jù)庫(kù)查詢也不需要。
(3) Lightor的Query緩存中,Query Key對(duì)應(yīng)的是ID列表,而不是數(shù)據(jù)對(duì)象列表,真正的數(shù)據(jù)對(duì)象只存在于ID緩存中。所以,不同的Query Key對(duì)應(yīng)的ID列表如果有交集,ID對(duì)應(yīng)的數(shù)據(jù)對(duì)象也不會(huì)在ID緩存中重復(fù)存儲(chǔ)。
(4) Lightor的緩存也沒(méi)有Jive緩存的最壞情況n + 1次數(shù)據(jù)庫(kù)查詢?nèi)秉c(diǎn)。
3.Lightor的緩存策略的缺點(diǎn)
(1) Lightor的Query緩存具有Hibernate的Query緩存的缺點(diǎn)。條件查詢涉及到的表中,如果有任何一條記錄增加、刪除、或改變,那么緩存中所有和該表相關(guān)的Query Key都會(huì)失效。
(2) Lightor的ID緩存也具有hibernate的二級(jí)ID緩存具有的缺點(diǎn)。條件查詢的時(shí)候,即使ID已經(jīng)存在于緩存中,也需要重新把數(shù)據(jù)對(duì)象整個(gè)從數(shù)據(jù)庫(kù)取出,放入到緩存中。
五、Query Key的效率
Query緩存的Query Key的空間和時(shí)間開(kāi)銷比較大。
Query Key里面存放的東西不少,SQL, 參數(shù),范圍(起始,個(gè)數(shù))。
這里面最大的東西就是SQL。又占地方,又花時(shí)間(hashCode, equals)。
Query Key最關(guān)鍵的兩個(gè)方法是hashCode和equals,重點(diǎn)是SQL的hashCode和equals。
Lightor的做法是,由于Lightor直接使用SQL,不用HQL、OQL之類,所以推薦盡量使用static final String的SQL,能夠節(jié)省空間和時(shí)間,以至于Query Key的效率能夠相當(dāng)于ID Key的效率。
至于Hibernate的QueryKey,有興趣的讀者可以去下載閱讀Hibernate的各個(gè)版本的源代碼,跟蹤一下QueryKey的實(shí)現(xiàn)優(yōu)化過(guò)程。
六、總結(jié)
這里列一個(gè)表,綜合表示Jive, Hibernate, Lightor的緩存策略的特征。
N + 1問(wèn)題 重復(fù)ID緩存問(wèn)題 Query緩存支持
Jive緩存 有 無(wú) 不支持
Hibernate緩存 無(wú) 有 支持
Lightor緩存 無(wú) 有 支持
注:
“重復(fù)ID緩存問(wèn)題”的含義是,每次條件查詢,不是只取ID列表,而是取出完整對(duì)象(所有字段)的列表。這樣,同一個(gè)ID對(duì)應(yīng)的數(shù)據(jù)對(duì)象,即使在緩存中已經(jīng)存在,也可能被重新放入緩存。參見(jiàn)相關(guān)緩存的缺點(diǎn)描述。
“重復(fù)ID緩存問(wèn)題”的負(fù)面效應(yīng)到底有多大,就看你的select id from …(只選擇ID)比你的 select * from … (選擇所有字段)快多少。主要影響因素是,字段的個(gè)數(shù),字段值的長(zhǎng)度,與數(shù)據(jù)庫(kù)服務(wù)器之間網(wǎng)絡(luò)傳輸速度。
不管怎么說(shuō),即使選擇所有字段,也只是一次數(shù)據(jù)庫(kù)查詢。而N + 1問(wèn)題帶來(lái)的可能最壞的負(fù)面效應(yīng)(N + 1次數(shù)據(jù)查詢)卻是非常大的。
選擇緩存策略的時(shí)候,應(yīng)根據(jù)這些情況發(fā)生的概率和正負(fù)面效應(yīng)進(jìn)行取舍。
----- added later
看到Robbin在04年6月的一篇相關(guān)文章。
Hibernate Iterator JCS分析
http://www.hibernate.org.cn/71.html
而Hibernate List方式是JDBC的簡(jiǎn)單封裝,一次sql就把所有的數(shù)據(jù)都取出來(lái)了,它不會(huì)像Iterator那樣先取主鍵,然后再取數(shù)據(jù),因此List無(wú)法利用JCS。不過(guò)List也可以把從數(shù)據(jù)庫(kù)中取出的數(shù)據(jù)填充到JCS里面去。
最佳的方式:第一次訪問(wèn)使用List,快速填充JCS,以后訪問(wèn)采用Iterator,充分利用JCS。
主要就我所了解的J2EE開(kāi)發(fā)的框架或開(kāi)源項(xiàng)目做個(gè)介紹,可以根據(jù)需求選用適當(dāng)?shù)拈_(kāi)源組件進(jìn)行開(kāi)發(fā).主要還是以Spring為核心,也總結(jié)了一些以前web開(kāi)發(fā)常用的開(kāi)源工具和開(kāi)源類庫(kù)
1.Hashtable是Dictionary的子類,HashMap是Map接口的一個(gè)實(shí)現(xiàn)類;
2.Hashtable中的方法是同步的,而HashMap中的方法在缺省情況下是非同步的。即是說(shuō),在多線程應(yīng)用程序中,不用專門(mén)的操作就安全地可以使用Hashtable了;而對(duì)于HashMap,則需要額外的同步機(jī)制。但HashMap的同步問(wèn)題可通過(guò)Collections的一個(gè)靜態(tài)方法得到解決:
Map Collections.synchronizedMap(Map m)
這個(gè)方法返回一個(gè)同步的Map,這個(gè)Map封裝了底層的HashMap的所有方法,使得底層的HashMap即使是在多線程的環(huán)境中也是安全的。
3.在HashMap中,null可以作為鍵,這樣的鍵只有一個(gè);可以有一個(gè)或多個(gè)鍵所對(duì)應(yīng)的值為null。當(dāng)get()方法返回null值時(shí),即可以表示HashMap中沒(méi)有該鍵,也可以表示該鍵所對(duì)應(yīng)的值為null。因此,在HashMap中不能由get()方法來(lái)判斷HashMap中是否存在某個(gè)鍵,而應(yīng)該用containsKey()方法來(lái)判斷。
Vector、ArrayList和List的異同
線性表,鏈表,哈希表是常用的數(shù)據(jù)結(jié)構(gòu),在進(jìn)行Java開(kāi)發(fā)時(shí),JDK已經(jīng)為我們提供了一系列相應(yīng)的類來(lái)實(shí)現(xiàn)基本的數(shù)據(jù)結(jié)構(gòu)。這些類均在java.util包中。本文試圖通過(guò)簡(jiǎn)單的描述,向讀者闡述各個(gè)類的作用以及如何正確使用這些類。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口
Collection是最基本的集合接口,一個(gè)Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。
所有實(shí)現(xiàn)Collection接口的類都必須提供兩個(gè)標(biāo)準(zhǔn)的構(gòu)造函數(shù):無(wú)參數(shù)的構(gòu)造函數(shù)用于創(chuàng)建一個(gè)空的Collection,有一個(gè)Collection參數(shù)的構(gòu)造函數(shù)用于創(chuàng)建一個(gè)新的Collection,這個(gè)新的Collection與傳入的Collection有相同的元素。后一個(gè)構(gòu)造函數(shù)允許用戶復(fù)制一個(gè)Collection。
如何遍歷Collection中的每一個(gè)元素?不論Collection的實(shí)際類型如何,它都支持一個(gè)iterator()的方法,該方法返回一個(gè)迭代子,使用該迭代子即可逐一訪問(wèn)Collection中每一個(gè)元素。典型的用法如下:
Iterator it = collection.iterator(); // 獲得一個(gè)迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一個(gè)元素
}
由Collection接口派生的兩個(gè)接口是List和Set。
List接口
List是有序的Collection,使用此接口能夠精確的控制每個(gè)元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似于數(shù)組下標(biāo))來(lái)訪問(wèn)List中的元素,這類似于Java的數(shù)組。
和下面要提到的Set不同,List允許有相同的元素。
除了具有Collection接口必備的iterator()方法外,List還提供一個(gè)listIterator()方法,返回一個(gè)ListIterator接口,和標(biāo)準(zhǔn)的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設(shè)定元素,還能向前或向后遍歷。
實(shí)現(xiàn)List接口的常用類有LinkedList,ArrayList,Vector和Stack。
LinkedList類
LinkedList實(shí)現(xiàn)了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊(duì)列(queue)或雙向隊(duì)列(deque)。
注意LinkedList沒(méi)有同步方法。如果多個(gè)線程同時(shí)訪問(wèn)一個(gè)List,則必須自己實(shí)現(xiàn)訪問(wèn)同步。一種解決方法是在創(chuàng)建List時(shí)構(gòu)造一個(gè)同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList類
ArrayList實(shí)現(xiàn)了可變大小的數(shù)組。它允許所有元素,包括null。ArrayList沒(méi)有同步。
size,isEmpty,get,set方法運(yùn)行時(shí)間為常數(shù)。但是add方法開(kāi)銷為分?jǐn)偟某?shù),添加n個(gè)元素需要O(n)的時(shí)間。其他的方法運(yùn)行時(shí)間為線性。
每個(gè)ArrayList實(shí)例都有一個(gè)容量(Capacity),即用于存儲(chǔ)元素的數(shù)組的大小。這個(gè)容量可隨著不斷添加新元素而自動(dòng)增加,但是增長(zhǎng)算法并沒(méi)有定義。當(dāng)需要插入大量元素時(shí),在插入前可以調(diào)用ensureCapacity方法來(lái)增加ArrayList的容量以提高插入效率。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
Vector類
Vector非常類似ArrayList,但是Vector是同步的。由Vector創(chuàng)建的Iterator,雖然和ArrayList創(chuàng)建的Iterator是同一接口,但是,因?yàn)閂ector是同步的,當(dāng)一個(gè)Iterator被創(chuàng)建而且正在被使用,另一個(gè)線程改變了Vector的狀態(tài)(例如,添加或刪除了一些元素),這時(shí)調(diào)用Iterator的方法時(shí)將拋出ConcurrentModificationException,因此必須捕獲該異常。
Stack 類
Stack繼承自Vector,實(shí)現(xiàn)一個(gè)后進(jìn)先出的堆棧。Stack提供5個(gè)額外的方法使得Vector得以被當(dāng)作堆棧使用。基本的push和pop方法,還有peek方法得到棧頂?shù)脑兀琫mpty方法測(cè)試堆棧是否為空,search方法檢測(cè)一個(gè)元素在堆棧中的位置。Stack剛創(chuàng)建后是空棧。
Set接口
Set是一種不包含重復(fù)的元素的Collection,即任意的兩個(gè)元素e1和e2都有e1.equals(e2)=false,Set最多有一個(gè)null元素。
很明顯,Set的構(gòu)造函數(shù)有一個(gè)約束條件,傳入的Collection參數(shù)不能包含重復(fù)的元素。
請(qǐng)注意:必須小心操作可變對(duì)象(Mutable Object)。如果一個(gè)Set中的可變?cè)馗淖兞俗陨頎顟B(tài)導(dǎo)致Object.equals(Object)=true將導(dǎo)致一些問(wèn)題。
Map接口
請(qǐng)注意,Map沒(méi)有繼承Collection接口,Map提供key到value的映射。一個(gè)Map中不能包含相同的key,每個(gè)key只能映射一個(gè)value。Map接口提供3種集合的視圖,Map的內(nèi)容可以被當(dāng)作一組key集合,一組value集合,或者一組key-value映射。
Hashtable類
Hashtable繼承Map接口,實(shí)現(xiàn)一個(gè)key-value映射的哈希表。任何非空(non-null)的對(duì)象都可作為key或者value。
添加數(shù)據(jù)使用put(key, value),取出數(shù)據(jù)使用get(key),這兩個(gè)基本操作的時(shí)間開(kāi)銷為常數(shù)。
Hashtable通過(guò)initial capacity和load factor兩個(gè)參數(shù)調(diào)整性能。通常缺省的load factor 0.75較好地實(shí)現(xiàn)了時(shí)間和空間的均衡。增大load factor可以節(jié)省空間但相應(yīng)的查找時(shí)間將增大,這會(huì)影響像get和put這樣的操作。
使用Hashtable的簡(jiǎn)單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));
要取出一個(gè)數(shù),比如2,用相應(yīng)的key:
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);
由于作為key的對(duì)象將通過(guò)計(jì)算其散列函數(shù)來(lái)確定與之對(duì)應(yīng)的value的位置,因此任何作為key的對(duì)象都必須實(shí)現(xiàn)hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當(dāng)作key的話,要相當(dāng)小心,按照散列函數(shù)的定義,如果兩個(gè)對(duì)象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個(gè)對(duì)象不同,則它們的hashCode不一定不同,如果兩個(gè)不同對(duì)象的hashCode相同,這種現(xiàn)象稱為沖突,沖突會(huì)導(dǎo)致操作哈希表的時(shí)間開(kāi)銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。
如果相同的對(duì)象有不同的hashCode,對(duì)哈希表的操作會(huì)出現(xiàn)意想不到的結(jié)果(期待的get方法返回null),要避免這種問(wèn)題,只需要牢記一條:要同時(shí)復(fù)寫(xiě)equals方法和hashCode方法,而不要只寫(xiě)其中一個(gè)。
Hashtable是同步的。
HashMap類
HashMap和Hashtable類似,不同之處在于HashMap是非同步的,并且允許null,即null value和null key。,但是將HashMap視為Collection時(shí)(values()方法可返回Collection),其迭代子操作時(shí)間開(kāi)銷和HashMap的容量成比例。因此,如果迭代操作的性能相當(dāng)重要的話,不要將HashMap的初始化容量設(shè)得過(guò)高,或者load factor過(guò)低。
WeakHashMap類
WeakHashMap是一種改進(jìn)的HashMap,它對(duì)key實(shí)行“弱引用”,如果一個(gè)key不再被外部所引用,那么該key可以被GC回收。
總結(jié)
如果涉及到堆棧,隊(duì)列等操作,應(yīng)該考慮用List,對(duì)于需要快速插入,刪除元素,應(yīng)該使用LinkedList,如果需要快速隨機(jī)訪問(wèn)元素,應(yīng)該使用ArrayList。
如果程序在單線程環(huán)境中,或者訪問(wèn)僅僅在一個(gè)線程中進(jìn)行,考慮非同步的類,其效率較高,如果多個(gè)線程可能同時(shí)操作一個(gè)類,應(yīng)該使用同步的類。
要特別注意對(duì)哈希表的操作,作為key的對(duì)象要正確復(fù)寫(xiě)equals和hashCode方法。
盡量返回接口而非實(shí)際的類型,如返回List而非ArrayList,這樣如果以后需要將ArrayList換成LinkedList時(shí),客戶端代碼不用改變。這就是針對(duì)抽象編程。
摘自:ChinaITLab | 作者: | 瀏覽率:70 |
JSF對(duì)通過(guò)關(guān)聯(lián)組件和事件來(lái)構(gòu)建頁(yè)面而說(shuō)是非常棒的,但是,與所有現(xiàn)有的技術(shù)一樣,它需要一個(gè)控制器來(lái)分離出頁(yè)面間的導(dǎo)航?jīng)Q策,并提供到業(yè)務(wù)層的鏈接。它擁有一個(gè)基本的導(dǎo)航處理程序,可以用功能完備的處理程序來(lái)替換它。Page Flow為創(chuàng)建可重用的封裝頁(yè)面流提供了基礎(chǔ),并可以與視圖層并行工作。它是一個(gè)功能完備的導(dǎo)航處理程序,將JSF頁(yè)面作為最優(yōu)先的處理對(duì)象。本文將討論如何集成這兩種技術(shù)來(lái)利用二者的優(yōu)點(diǎn)。
構(gòu)建Beehive/JSF應(yīng)用程序要構(gòu)建Beehive/JSF應(yīng)用程序,首先要啟動(dòng)Page Flow,然后添加對(duì)JSF的支持。起點(diǎn)是從基本的支持NetUI(Beehive中包含Page Flow的組件)的項(xiàng)目開(kāi)始。根據(jù)指導(dǎo)構(gòu)建基本的支持NetUI的Web應(yīng)用程序。在本文中,我們暫且稱之為“jsf-beehive”,可以在 http://localhost:8080/jsf-beehive 上獲得。 接下來(lái),安裝并配置JSF。Page Flow可以使用任何與JavaServer Faces 1.1兼容的實(shí)現(xiàn),并針對(duì)兩種主流實(shí)現(xiàn)進(jìn)行了測(cè)試:Apache MyFaces和JSF Reference Implementation。根據(jù)下面的指導(dǎo)在新的Web應(yīng)用程序中安裝JSF:MyFaces v1.0.9及更高版本,JSF Reference Implementation v1.1_01,或者其他實(shí)現(xiàn)。之后,可以使用WEB-INF/faces-config.xml中的一個(gè)簡(jiǎn)單入口啟動(dòng)Page Flow集成,入口在<application>標(biāo)簽之下,<navigation-rule>標(biāo)簽之上: <factory> <application-factory> org.apache.beehive.netui.pageflow.faces.PageFlowApplicationFactory </application-factory> </factory> 添加了這些就為頁(yè)面流提供了一個(gè)機(jī)會(huì),使其可以提供自己的JSF框架對(duì)象版本來(lái)定制其行為。通常來(lái)說(shuō),只有在使用頁(yè)面流功能的時(shí)候,JSF行為才會(huì)被修改;JSF的基本行為不會(huì)改變。 基本集成JSF中頁(yè)面流的最基本用處是引發(fā)(調(diào)用)來(lái)自JSF頁(yè)面的動(dòng)作。JSF頁(yè)面可以處理頁(yè)面內(nèi)事件,而頁(yè)面流動(dòng)作則是從一個(gè)頁(yè)面導(dǎo)航到另一頁(yè)面的方法。首先,在Web應(yīng)用程序中創(chuàng)建一個(gè)名為“example”的目錄,在其中創(chuàng)建一個(gè)頁(yè)面流控制器類: package example; import org.apache.beehive.netui.pageflow.Forward; import org.apache.beehive.netui.pageflow.PageFlowController; import org.apache.beehive.netui.pageflow.annotations.Jpf; @Jpf.Controller( simpleActions={ @Jpf.SimpleAction(name="begin", path="page1.faces") } ) public class ExampleController extends PageFlowController { @Jpf.Action( forwards={ @Jpf.Forward(name="success", path="page2.faces") } ) public Forward goPage2() { Forward fwd = new Forward("success"); return fwd; } } 在這個(gè)頁(yè)面流中有兩個(gè)動(dòng)作:跳轉(zhuǎn)到page1.faces的begin動(dòng)作和跳轉(zhuǎn)到page2.faces的goPage2動(dòng)作。將goPage2作為一個(gè)方法動(dòng)作(而不是簡(jiǎn)單動(dòng)作)的原因是稍后將會(huì)對(duì)其進(jìn)行擴(kuò)充。 在構(gòu)造頁(yè)面的時(shí)候,應(yīng)當(dāng)以.jsp為擴(kuò)展名創(chuàng)建page1和page2;JSF servlet處理每個(gè).faces請(qǐng)求,并最終跳轉(zhuǎn)到相關(guān)的JSP。所以,跳轉(zhuǎn)到page1.faces最終將顯示page1.jsp,如下: <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> ? <html> <body> <f:view> <h:form> <h:panelGrid> <h:outputText value="Page 1 of page flow #{pageFlow.URI}"/> <h:commandLink action="goPage2" value="Go to page 2"/> </h:panelGrid> </h:form> </f:view> </body> </html> 從JSF頁(yè)面引發(fā)一個(gè)動(dòng)作很簡(jiǎn)單:使用命令組件的action屬性中的動(dòng)作名字就可以了。在上面的例子中,commandLink指向goPage2動(dòng)作。使用頁(yè)面流集成,這意味著goPage2動(dòng)作會(huì)在example.ExampleController中運(yùn)行。 就是這樣。要試驗(yàn)的話,構(gòu)建應(yīng)用程序,點(diǎn)擊 http://localhost:8080/jsf-beehive/example/ExampleController.jpf ,這將通過(guò)begin動(dòng)作跳轉(zhuǎn)到page1.faces。單擊鏈接“Go to page 2”,會(huì)引發(fā)goPage2動(dòng)作并跳轉(zhuǎn)到page2.faces。 后臺(tái)BeanPage Flow框架可以管理與JSF頁(yè)面相關(guān)的后臺(tái)bean(backing bean)。該類是放置與頁(yè)面相關(guān)的事件處理程序和狀態(tài)的方便場(chǎng)所。可以把它看作是集中放置與頁(yè)面交互時(shí)所運(yùn)行的所有代碼的單一場(chǎng)所。當(dāng)點(diǎn)擊一個(gè)JSF頁(yè)面時(shí),Page Flow會(huì)判斷是否有具有同樣名稱和包的類,例如,page /example/page1.faces的example.page1類。如果存在這樣的類,并且它用@Jpf.FacesBacking進(jìn)行注釋并擴(kuò)展了FacesBackingBean,它就會(huì)創(chuàng)建該類的一個(gè)實(shí)例。當(dāng)離開(kāi)JSF頁(yè)面而轉(zhuǎn)到一個(gè)動(dòng)作或者其它任何頁(yè)面時(shí),后臺(tái)bean會(huì)被銷毀。后臺(tái)bean與JSF頁(yè)面共存亡。 綁定到后臺(tái)bean中的屬性下面是page1.faces的一個(gè)非常簡(jiǎn)單的后臺(tái)bean,以及屬性someProperty。文件名是page1.java: package example; import org.apache.beehive.netui.pageflow.FacesBackingBean; import org.apache.beehive.netui.pageflow.annotations.Jpf; @Jpf.FacesBacking public class page1 extends FacesBackingBean { private String _someProperty = "This is a property value from" + getClass().getName() + "."; public String getSomeProperty() { return _someProperty; } public void setSomeProperty(String someProperty) { _someProperty = someProperty; } } 在JSF頁(yè)面(page1.jsp)中,可以利用backing綁定上下文來(lái)綁定到這個(gè)屬性: <h:outputText value="#{backing.someProperty}"/> 上面的例子顯示了someProperty(最終在后臺(tái)bean上調(diào)用getSomeProperty())的值。類似地,設(shè)置這個(gè)值: <h:inputText value="#{backing.someProperty}"/> 注意,在這個(gè)例子中,后臺(tái)bean中沒(méi)有出現(xiàn)事件處理程序或組件引用。這就縮短了代碼;后臺(tái)bean是放置頁(yè)面所有的處理程序和組件引用的好地方。 從后臺(tái)bean引發(fā)頁(yè)面流動(dòng)作在上面的“基本集成”部分,我們直接從JSF組件引發(fā)頁(yè)面流動(dòng)作。通常情況下,只需這樣即可;當(dāng)單擊一個(gè)按鈕或者鏈接時(shí),會(huì)運(yùn)行一個(gè)動(dòng)作并跳轉(zhuǎn)到另一個(gè)頁(yè)面上。如果想在調(diào)用控制器之前運(yùn)行一些與頁(yè)面相關(guān)的代碼,或者如果希望頁(yè)面可以在幾個(gè)動(dòng)作之間進(jìn)行動(dòng)態(tài)選擇的話,可以在命令處理程序(JSF頁(yè)面所運(yùn)行的一個(gè)Java方法)中引發(fā)一個(gè)動(dòng)作。下面是一個(gè)命令處理程序的例子,可以把它放到后臺(tái)bean page2.java中(或者其它任何可公開(kāi)訪問(wèn)的bean中): public String chooseNextPage() { return "goPage3"; } 這是一個(gè)非常簡(jiǎn)單的命令處理程序,它選擇了goPage3動(dòng)作。可以用標(biāo)準(zhǔn)的JSF方式從一個(gè)JSF命令組件綁定到這個(gè)命令處理程序: <h:commandButton action="#{backing.chooseNextPage}" value="Submit"/> 當(dāng)單擊鏈接時(shí),會(huì)運(yùn)行chooseNextPage命令處理程序,它會(huì)選擇引發(fā)goPage3動(dòng)作。還可以對(duì)命令處理程序方法使用一個(gè)特殊的頁(yè)面流注釋——@Jpf.CommandHandler: @Jpf.CommandHandler( raiseActions={ @Jpf.RaiseAction(action="goPage3") } ) public String chooseNextPage() { return "goPage3"; } 該注釋使支持Beehive的工具可以知道命令處理程序引發(fā)了后臺(tái)bean中的哪個(gè)動(dòng)作,并允許擴(kuò)展JSF動(dòng)作處理的能力(參見(jiàn)下面“從JSF頁(yè)面向頁(yè)面流發(fā)送數(shù)據(jù)”部分)。 從后臺(tái)bean訪問(wèn)當(dāng)前頁(yè)面流或共享流在某些情況下,您或許想直接從后臺(tái)bean訪問(wèn)當(dāng)前頁(yè)面流或一個(gè)活動(dòng)的共享流。為此,只需創(chuàng)建一個(gè)適當(dāng)類型的字段,并使用@Jpf.PageFlowField或@Jpf.SharedFlowField對(duì)其進(jìn)行適當(dāng)注釋: @Jpf.CommandHandler( raiseActions={ @Jpf.RaiseAction(action="goPage3") } ) public String chooseNextPage() { return "goPage3"; } 這些字段將在創(chuàng)建后臺(tái)bean的時(shí)候被初始化。無(wú)需手動(dòng)對(duì)其進(jìn)行初始化。下面的例子使用了自動(dòng)初始化的ExampleController字段。在這個(gè)例子中,“show hints”單選鈕的事件處理程序在頁(yè)面流中設(shè)置了一個(gè)普通優(yōu)先級(jí)。 @Jpf.PageFlowField private ExampleController myController; @Jpf.SharedFlowField(name="sharedFlow2") // "sharedFlow2" is a // name defined in the // page flow controller private ExampleSharedFlow mySharedFlow; 在很多情況下,頁(yè)面不需要直接與頁(yè)面流或者共享流進(jìn)行交互;使用其它方法從頁(yè)面流向JSF頁(yè)面?zhèn)鬟f數(shù)據(jù)就足夠了,反之亦然。下面我將給出一些例子。 從頁(yè)面流控制器訪問(wèn)后臺(tái)bean您不能從頁(yè)面流控制器訪問(wèn)后臺(tái)bean!至少,這不容易做到,這是有意為之的。后臺(tái)bean與JSF頁(yè)面緊密相關(guān),當(dāng)您離開(kāi)頁(yè)面的時(shí)候,后臺(tái)bean會(huì)被銷毀。正如頁(yè)面流控制器不應(yīng)了解頁(yè)面細(xì)節(jié)一樣,它也不應(yīng)了解后臺(tái)bean。當(dāng)然了,可以從后臺(tái)bean向控制器傳遞數(shù)據(jù)(稍后將會(huì)介紹),甚至可以傳遞后臺(tái)bean實(shí)例本身,但是在大多數(shù)情況下,后臺(tái)bean的內(nèi)容是不應(yīng)當(dāng)泄露給控制器的。 生命周期方法通常,當(dāng)后臺(tái)bean發(fā)生某些事情的時(shí)候,比如當(dāng)它被創(chuàng)建或銷毀時(shí),我們希望能運(yùn)行代碼。在Page Flow框架的生命周期中,它會(huì)對(duì)后臺(tái)bean調(diào)用一些方法:
不管要在哪個(gè)時(shí)期運(yùn)行代碼,只需重寫(xiě)適當(dāng)?shù)姆椒ǎ?/font> protected void onCreate() { /*some create-time logic */ } 當(dāng)重寫(xiě)這些方法時(shí),不需要調(diào)用空的super版本。 在JSF頁(yè)面和頁(yè)面流之間傳遞數(shù)據(jù)現(xiàn)在我們?cè)摽纯慈绾卧贘SF頁(yè)面和頁(yè)面流之間傳遞數(shù)據(jù)了。 從頁(yè)面流向JSF頁(yè)面發(fā)送數(shù)據(jù)通常,您會(huì)想要利用頁(yè)面流的數(shù)據(jù)來(lái)初始化一個(gè)頁(yè)面。為此,可以向page2.faces的Forward添加“action outputs”: @Jpf.Action( forwards={ @Jpf.Forward( name="success", path="page2.faces", actionOutputs={ @Jpf.ActionOutput(name="message", type=String.class,required=true) } ) } ) public Forward goPage2() { Forward fwd = new Forward("success"); fwd.addActionOutput("message", "Got the message."); return fwd; } 做完這些之后,可以直接從JSF頁(yè)面或者后臺(tái)bean將該值作為頁(yè)面輸入來(lái)訪問(wèn)。(如果您不喜歡鍵入冗長(zhǎng)的注釋,可以省去斜體的。它們主要用于再次檢查添加的對(duì)象類型是否正確,確定不缺失類型。) 可以在頁(yè)面中利用JSF表示語(yǔ)言中的頁(yè)面流pageInput綁定上下文綁定到這個(gè)值: <h:outputText value="#{pageInput.message}"/> 注意,可以利用pageFlow和sharedFlow綁定上下文綁定到頁(yè)面流控制器自身或者任何可用的共享流的屬性: <h:outputText value="#{pageFlow.someProperty}"/> <h:outputText value="#{sharedFlow.mySharedFlow.someProperty}"/> 最后,要想從后臺(tái)bean訪問(wèn)頁(yè)面輸入,只需在bean類代碼中的任意地方調(diào)用getPageInput: String message = (String) getPageInput("message"); 從JSF頁(yè)面向頁(yè)面流發(fā)送數(shù)據(jù)還可以隨著頁(yè)面流所引發(fā)的動(dòng)作發(fā)送數(shù)據(jù)。很多動(dòng)作將要求表單bean作為輸入;通常,表單bean用于從頁(yè)面獲取數(shù)據(jù)送到控制器。首先,讓我們構(gòu)建一個(gè)動(dòng)作來(lái)接收表單bean并跳轉(zhuǎn)到頁(yè)面: @Jpf.Action( forwards={ @Jpf.Forward(name="success", path="page3.faces") } ) public Forward goPage3(NameBean nameBean) { _userName = nameBean.getFirstName() + ' ' + nameBean.getLastName(); return new Forward("success"); } 該動(dòng)作包含一個(gè)NameBean,它是一個(gè)將getters/setters作為其firstName和lastName屬性的表單bean類。它設(shè)置一個(gè)成員變量保存完整名字,之后跳轉(zhuǎn)到page3.faces。我們知道,可以直接從JSF頁(yè)面或者它的后臺(tái)bean引發(fā)一個(gè)動(dòng)作。在這兩種情況下,都可以向動(dòng)作發(fā)送表單bean。下面讓我們依次看看每種情況。 從后臺(tái)bean發(fā)送表單bean要從后臺(tái)bean中的命令處理程序發(fā)送表單bean,需要使用一個(gè)特定的注釋。下面給出了page2.java中的情況: private ExampleController.NameBean _nameBean; protected void onCreate() { _nameBean = new ExampleController.NameBean(); } public ExampleController.NameBean getName() { return _nameBean; } @Jpf.CommandHandler( raiseActions={ @Jpf.RaiseAction(action="goPage3", outputFormBean="_nameBean") } ) public String chooseNextPage() { return "goPage3"; } 在這個(gè)例子中,JSF頁(yè)面可以用它選擇的任何方式填充_nameBean的值(例如,通過(guò)將h:inputText值綁定到#{backing.name.firstName}和#{backing.name.lastName})。之后它使用@Jpf.RaiseAction上的outputFormBean屬性來(lái)標(biāo)記_nameBean應(yīng)當(dāng)被傳遞到動(dòng)作goPage3。 從JSF頁(yè)面發(fā)送表單bean從JSF頁(yè)面直接發(fā)送表單bean很容易,只要您可以通過(guò)數(shù)據(jù)綁定表達(dá)式得到bean值。這是通過(guò)在commandButton組件內(nèi)部添加名為submitFormBean的h:attribute組件來(lái)實(shí)現(xiàn)的: <h:commandButton action="#{backing.chooseNextPage}" value="Submit directly from page"> <f:attribute name="submitFormBean" value="backing.name" /> </h:commandButton> 在這里,為了使表單bean發(fā)送到動(dòng)作goPage3,按鈕綁定到后臺(tái)bean的“name”屬性(getName)。 結(jié)束語(yǔ)本文展示了如何將JSF在構(gòu)建頁(yè)面方面的豐富特性與Beehive Page Flow在控制頁(yè)面間導(dǎo)航方面的強(qiáng)大功能相結(jié)合。二者的集成非常容易,但是卻會(huì)對(duì)應(yīng)用造成深遠(yuǎn)的影響:它將JSF頁(yè)面與應(yīng)用級(jí)邏輯相分離,并把頁(yè)面帶入Page Flow所提供的功能領(lǐng)域中。JSF頁(yè)面得到了清楚的任務(wù):作為單個(gè)(如果有足夠能力的話)視圖元素參與到應(yīng)用程序的流中。文中沒(méi)有展示JSF頁(yè)面中具有事件處理功能且控制器中具有復(fù)雜的導(dǎo)航邏輯的完備應(yīng)用程序。但是隨著應(yīng)用程序的復(fù)雜程度提高,它就會(huì)更加需要責(zé)任的劃分以及頁(yè)面流添加給JSF的高級(jí)流功能。您可以花幾分鐘嘗試一下——很快您就將意識(shí)到這樣做所帶來(lái)的好處。 |
[原創(chuàng)文章,轉(zhuǎn)載請(qǐng)保留或注明出處:http://www.regexlab.com/zh/regref.htm]
引言
??? 正則表達(dá)式(regular
expression)描述了一種字符串匹配的模式,可以用來(lái):(1)檢查一個(gè)串中是否含有符合某個(gè)規(guī)則的子串,并且可以得到這個(gè)子串;(2)根據(jù)匹配規(guī)則對(duì)字符串進(jìn)行靈活的替換操作。
???
正則表達(dá)式學(xué)習(xí)起來(lái)其實(shí)是很簡(jiǎn)單的,不多的幾個(gè)較為抽象的概念也很容易理解。之所以很多人感覺(jué)正則表達(dá)式比較復(fù)雜,一方面是因?yàn)榇蠖鄶?shù)的文檔沒(méi)有做到由淺
入深地講解,概念上沒(méi)有注意先后順序,給讀者的理解帶來(lái)困難;另一方面,各種引擎自帶的文檔一般都要介紹它特有的功能,然而這部分特有的功能并不是我們首
先要理解的。
??? 文章中的每一個(gè)舉例,都可以點(diǎn)擊進(jìn)入到測(cè)試頁(yè)面進(jìn)行測(cè)試。閑話少說(shuō),開(kāi)始。
1. 正則表達(dá)式規(guī)則
1.1 普通字符
???
字母、數(shù)字、漢字、下劃線、以及后邊章節(jié)中沒(méi)有特殊定義的標(biāo)點(diǎn)符號(hào),都是"普通字符"。表達(dá)式中的普通字符,在匹配一個(gè)字符串的時(shí)候,匹配與之相同的一個(gè)字符。
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"c";匹配到的位置是:開(kāi)始于2,結(jié)束于3。(注:下標(biāo)從0開(kāi)始還是從1開(kāi)始,因當(dāng)前編程語(yǔ)言的不同而可能不同)
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"bcd";匹配到的位置是:開(kāi)始于1,結(jié)束于4。
1.2 簡(jiǎn)單的轉(zhuǎn)義字符
??? 一些不便書(shū)寫(xiě)的字符,采用在前面加 "\" 的方法。這些字符其實(shí)我們都已經(jīng)熟知了。
表達(dá)式 |
可匹配 |
\r, \n |
代表回車和換行符 |
\t |
制表符 |
\\ |
代表 "\" 本身 |
??? 還有其他一些在后邊章節(jié)中有特殊用處的標(biāo)點(diǎn)符號(hào),在前面加 "\" 后,就代表該符號(hào)本身。比如:^, $ 都有特殊意義,如果要想匹配字符串中 "^" 和 "$" 字符,則表達(dá)式就需要寫(xiě)成 "\^" 和 "\$"。
表達(dá)式 |
可匹配 |
\^ |
匹配 ^ 符號(hào)本身 |
\$ |
匹配 $ 符號(hào)本身 |
\. |
匹配小數(shù)點(diǎn)(.)本身 |
??? 這些轉(zhuǎn)義字符的匹配方法與 "普通字符" 是類似的。也是匹配與之相同的一個(gè)字符。
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"$d";匹配到的位置是:開(kāi)始于3,結(jié)束于5。
1.3 能夠與 '多種字符' 匹配的表達(dá)式
??? 正則表達(dá)式中的一些表示方法,可以匹配 '多種字符' 其中的任意一個(gè)字符。比如,表達(dá)式 "\d" 可以匹配任意一個(gè)數(shù)字。雖然可以匹配其中任意字符,但是只能是一個(gè),不是多個(gè)。這就好比玩撲克牌時(shí)候,大小王可以代替任意一張牌,但是只能代替一張牌。
表達(dá)式 |
可匹配 |
\d |
任意一個(gè)數(shù)字,0~9 中的任意一個(gè) |
\w |
任意一個(gè)字母或數(shù)字或下劃線,也就是 A~Z,a~z,0~9,_ 中任意一個(gè) |
\s |
包括空格、制表符、換頁(yè)符等空白字符的其中任意一個(gè) |
. |
小數(shù)點(diǎn)可以匹配除了換行符(\n)以外的任意一個(gè)字符 |
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"12";匹配到的位置是:開(kāi)始于3,結(jié)束于5。
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"aa1";匹配到的位置是:開(kāi)始于1,結(jié)束于4。
1.4 自定義能夠匹配 '多種字符' 的表達(dá)式
??? 使用方括號(hào) [ ] 包含一系列字符,能夠匹配其中任意一個(gè)字符。用 [^ ] 包含一系列字符,則能夠匹配其中字符之外的任意一個(gè)字符。同樣的道理,雖然可以匹配其中任意一個(gè),但是只能是一個(gè),不是多個(gè)。
表達(dá)式 |
可匹配 |
[ab5@] |
匹配 "a" 或 "b" 或 "5" 或 "@" |
[^abc] |
匹配 "a","b","c" 之外的任意一個(gè)字符 |
[f-k] |
匹配 "f"~"k" 之間的任意一個(gè)字母 |
[^A-F0-3] |
匹配 "A"~"F","0"~"3" 之外的任意一個(gè)字符 |
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"bc";匹配到的位置是:開(kāi)始于1,結(jié)束于3。
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"1";匹配到的位置是:開(kāi)始于3,結(jié)束于4。
1.5 修飾匹配次數(shù)的特殊符號(hào)
??? 前面章節(jié)中講到的表達(dá)式,無(wú)論是只能匹配一種字符的表達(dá)式,還是可以匹配多種字符其中任意一個(gè)的表達(dá)式,都只能匹配一次。如果使用表達(dá)式再加上修飾匹配次數(shù)的特殊符號(hào),那么不用重復(fù)書(shū)寫(xiě)表達(dá)式就可以重復(fù)匹配。
??? 使用方法是:"次數(shù)修飾"放在"被修飾的表達(dá)式"后邊。比如:"[bcd][bcd]" 可以寫(xiě)成 "[bcd]{2}"。
表達(dá)式 |
作用 |
{n} |
表達(dá)式重復(fù)n次,比如:; |
{m,n} |
|
{m,} |
|
? |
|
+ |
|
* |
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"12.5";匹配到的位置是:開(kāi)始于10,結(jié)束于14。
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"goooooogle";匹配到的位置是:開(kāi)始于7,結(jié)束于17。
1.6 其他一些代表抽象意義的特殊符號(hào)
??? 一些符號(hào)在表達(dá)式中代表抽象的特殊意義:
表達(dá)式 |
作用 |
^ |
與字符串開(kāi)始的地方匹配,不匹配任何字符 |
$ |
與字符串結(jié)束的地方匹配,不匹配任何字符 |
\b |
匹配一個(gè)單詞邊界,也就是單詞和空格之間的位置,不匹配任何字符 |
??? 進(jìn)一步的文字說(shuō)明仍然比較抽象,因此,舉例幫助大家理解。
??? ,匹配結(jié)果是:失敗。因?yàn)?"^" 要求與字符串開(kāi)始的地方匹配,因此,只有當(dāng) "aaa"
位于字符串的開(kāi)頭的時(shí)候,"^aaa" 才能匹配,。
??? 舉例2:表達(dá)式
"aaa$" 在匹配 "xxx
aaa xxx" 時(shí),匹配結(jié)果是:失敗。因?yàn)?"$" 要求與字符串結(jié)束的地方匹配,因此,只有當(dāng) "aaa"
位于字符串的結(jié)尾的時(shí)候,"aaa$" 才能匹配,。
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"@a";匹配到的位置是:開(kāi)始于2,結(jié)束于4。
??? 進(jìn)一步說(shuō)明:"\b"
與 "^" 和 "$" 類似,本身不匹配任何字符,但是它要求它在匹配結(jié)果中所處位置的左右兩邊,其中一邊是 "\w" 范圍,另一邊是
非"\w" 的范圍。
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"end";匹配到的位置是:開(kāi)始于15,結(jié)束于18。
??? 一些符號(hào)可以影響表達(dá)式內(nèi)部的子表達(dá)式之間的關(guān)系:
表達(dá)式 |
作用 |
| |
左右兩邊表達(dá)式之間 "或" 關(guān)系,匹配左邊或者右邊 |
( ) |
(1). 在被修飾匹配次數(shù)的時(shí)候,括號(hào)中的表達(dá)式可以作為整體被修飾 |
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"Tom";匹配到的位置是:開(kāi)始于4,結(jié)束于7。匹配下一個(gè)時(shí),匹配結(jié)果是:成功;匹配到的內(nèi)容是:"Jack";匹配到的位置時(shí):開(kāi)始于15,結(jié)束于19。
??? ,匹配結(jié)果是:成功;匹配到內(nèi)容是:"go go go";匹配到的位置是:開(kāi)始于6,結(jié)束于14。
??? ,匹配的結(jié)果是:成功;匹配到的內(nèi)容是:"¥20.5";匹配到的位置是:開(kāi)始于6,結(jié)束于10。單獨(dú)獲取括號(hào)范圍匹配到的內(nèi)容是:"20.5"。
2. 正則表達(dá)式中的一些高級(jí)規(guī)則
2.1 匹配次數(shù)中的貪婪與非貪婪
??? 在使用修飾匹配次數(shù)的特殊符號(hào)時(shí),有幾種表示方法可以使同一個(gè)表達(dá)式能夠匹配不同的次數(shù),比如:"{m,n}", "{m,}", "?", "*", "+",具體匹配的次數(shù)隨被匹配的字符串而定。這種重復(fù)匹配不定次數(shù)的表達(dá)式在匹配過(guò)程中,總是盡可能多的匹配。比如,針對(duì)文本 "dxxxdxxxd",舉例如下:
表達(dá)式 |
匹配結(jié)果 |
"\w+" 將匹配第一個(gè) "d" 之后的所有字符 "xxxdxxxd" |
|
"\w+" 將匹配第一個(gè) "d" 和最后一個(gè) "d" 之間的所有字符 "xxxdxxx"。雖然 "\w+" 也能夠匹配上最后一個(gè) "d",但是為了使整個(gè)表達(dá)式匹配成功,"\w+" 可以 "讓出" 它本來(lái)能夠匹配的最后一個(gè) "d" |
??? 由此可見(jiàn),"\w+" 在匹配的時(shí)候,總是盡可能多的匹配符合它規(guī)則的字符。雖然第二個(gè)舉例中,它沒(méi)有匹配最后一個(gè) "d",但那也是為了讓整個(gè)表達(dá)式能夠匹配成功。同理,帶 "*" 和 "{m,n}" 的表達(dá)式都是盡可能地多匹配,帶 "?" 的表達(dá)式在可匹配可不匹配的時(shí)候,也是盡可能的 "要匹配"。這 種匹配原則就叫作 "貪婪" 模式 。
??? 非貪婪模式:
???
在修飾匹配次數(shù)的特殊符號(hào)后再加上一個(gè) "?" 號(hào),則可以使匹配次數(shù)不定的表達(dá)式盡可能少的匹配,使可匹配可不匹配的表達(dá)式,盡可能的
"不匹配"。這種匹配原則叫作 "非貪婪" 模式,也叫作 "勉強(qiáng)"
模式。如果少匹配就會(huì)導(dǎo)致整個(gè)表達(dá)式匹配失敗的時(shí)候,與貪婪模式類似,非貪婪模式會(huì)最小限度的再匹配一些,以使整個(gè)表達(dá)式匹配成功。舉例如下,針對(duì)文本
"dxxxdxxxd" 舉例:
表達(dá)式 |
匹配結(jié)果 |
"\w+?" 將盡可能少的匹配第一個(gè) "d" 之后的字符,結(jié)果是:"\w+?" 只匹配了一個(gè) "x" |
|
為了讓整個(gè)表達(dá)式匹配成功,"\w+?" 不得不匹配 "xxx" 才可以讓后邊的 "d" 匹配,從而使整個(gè)表達(dá)式匹配成功。因此,結(jié)果是:"\w+?" 匹配 "xxx" |
??? 更多的情況,舉例如下:
??? 舉
例1:表達(dá)式 "<td>(.*)</td>" 與字符串
"<td><p>aa</p></td>
<td><p>bb</p></td>" 匹配時(shí),匹配的結(jié)果是:成功;匹配到的內(nèi)容是 "<td><p>aa</p></td> <td><p>bb</p></td>" 整個(gè)字符串,
表達(dá)式中的 "</td>" 將與字符串中最后一個(gè) "</td>" 匹配。
??? ,將只得到 "<td><p>aa</p></td>",
再次匹配下一個(gè)時(shí),可以得到第二個(gè)
"<td><p>bb</p></td>"。
2.2 反向引用 \1, \2...
???
表達(dá)式在匹配時(shí),表達(dá)式引擎會(huì)將小括號(hào) "( )"
包含的表達(dá)式所匹配到的字符串記錄下來(lái)。在獲取匹配結(jié)果的時(shí)候,小括號(hào)包含的表達(dá)式所匹配到的字符串可以單獨(dú)獲取。這一點(diǎn),在前面的舉例中,已經(jīng)多次展示
了。在實(shí)際應(yīng)用場(chǎng)合中,當(dāng)用某種邊界來(lái)查找,而所要獲取的內(nèi)容又不包含邊界時(shí),必須使用小括號(hào)來(lái)指定所要的范圍。比如前面的
"<td>(.*?)</td>"。
???
其實(shí),"小括號(hào)包含的表達(dá)式所匹配到的字符串" 不僅是在匹配結(jié)束后才可以使用,在匹配過(guò)程中也可以使用。表達(dá)式后邊的部分,可以引用前面
"括號(hào)內(nèi)的子匹配已經(jīng)匹配到的字符串"。引用方法是 "\" 加上一個(gè)數(shù)字。"\1" 引用第1對(duì)括號(hào)內(nèi)匹配到的字符串,"\2"
引用第2對(duì)括號(hào)內(nèi)匹配到的字符串……以此類推,如果一對(duì)括號(hào)內(nèi)包含另一對(duì)括號(hào),則外層的括號(hào)先排序號(hào)。換句話說(shuō),哪一對(duì)的左括號(hào) "("
在前,那這一對(duì)就先排序號(hào)。
??? 舉例如下:
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:" 'Hello' "。再次匹配下一個(gè)時(shí),可以匹配到 " "World" "。
??? ,匹配結(jié)果是:成功;匹配到的內(nèi)容是 "ccccc"。再次匹配下一個(gè)時(shí),將得到 999999999。這個(gè)表達(dá)式要求 "\w" 范圍的字符至少重復(fù)5次,。
??? 舉例3:表達(dá)式
"<(\w+)\s*(\w+(=('|").*?\4)?\s*)*>.*?</\1>" 在匹配 "<td id='td1' style="bgcolor:white"></td>" 時(shí),匹配結(jié)果是成功。如果 "<td>" 與 "</td>" 不配對(duì),則會(huì)匹配失敗;如果改成其他配對(duì),也可以匹配成功。
2.3 預(yù)搜索,不匹配;反向預(yù)搜索,不匹配
??? 前面的章節(jié)中,我講到了幾個(gè)代表抽象意義的特殊符號(hào):"^","$","\b"。它們都有一個(gè)共同點(diǎn),那就是:它們本身不匹配任何字符,只是對(duì) "字符串的兩頭" 或者 "字符之間的縫隙" 附加了一個(gè)條件。理解到這個(gè)概念以后,本節(jié)將繼續(xù)介紹另外一種對(duì) "兩頭" 或者 "縫隙" 附加條件的,更加靈活的表示方法。
??? 正向預(yù)搜索:"(?=xxxxx)","(?!xxxxx)"
??? 格式:"(?=xxxxx)",在被匹配的字符串中,它對(duì)所處的 "縫隙" 或者 "兩頭" 附加的條件是:所在縫隙的右側(cè),必須能夠匹配上
xxxxx 這部分的表達(dá)式。因?yàn)樗皇窃诖俗鳛檫@個(gè)縫隙上附加的條件,所以它并不影響后邊的表達(dá)式去真正匹配這個(gè)縫隙之后的字符。這就類似
"\b",本身不匹配任何字符。"\b" 只是將所在縫隙之前、之后的字符取來(lái)進(jìn)行了一下判斷,不會(huì)影響后邊的表達(dá)式來(lái)真正的匹配。
??? ,將只匹配 "Windows NT" 中的 "Windows ",其他的 "Windows " 字樣則不被匹配。
??? ,將可以匹配6個(gè)"f"的前4個(gè),可以匹配9個(gè)"9"的前7個(gè)。這個(gè)表達(dá)式可以讀解成:重復(fù)4次以上的字母數(shù)字,則匹配其剩下最后2位之前的部分。當(dāng)然,這個(gè)表達(dá)式可以不這樣寫(xiě),在此的目的是作為演示之用。
??? 格式:"(?!xxxxx)",所在縫隙的右側(cè),必須不能匹配 xxxxx 這部分表達(dá)式。
??? ,將從頭一直匹配到 "stop" 之前的位置,如果字符串中沒(méi)有 "stop",則匹配整個(gè)字符串。
??? ,只能匹配 "do"。在本條舉例中,"do" 后邊使用 "(?!\w)" 和使用 "\b" 效果是一樣的。
??? 反向預(yù)搜索:"(?<=xxxxx)","(?<!xxxxx)"
??? 這兩種格式的概念和正向預(yù)搜索是類似的,反向預(yù)搜索要求的條件是:所在縫隙的
"左側(cè)",兩種格式分別要求必須能夠匹配和必須不能夠匹配指定表達(dá)式,而不是去判斷右側(cè)。與 "正向預(yù)搜索"
一樣的是:它們都是對(duì)所在縫隙的一種附加條件,本身都不匹配任何字符。
??? 舉例5:表達(dá)式 "(?<=\d{4})\d+(?=\d{4})" 在匹配 "1234567890123456"
時(shí),將匹配除了前4個(gè)數(shù)字和后4個(gè)數(shù)字之外的中間8個(gè)數(shù)字。由于 JScript.RegExp
不支持反向預(yù)搜索,因此,本條舉例不能夠進(jìn)行演示。很多其他的引擎可以支持反向預(yù)搜索,比如:Java 1.4 以上的
java.util.regex 包,.NET 中System.Text.RegularExpressions
命名空間,boost::regex 以及 GRETA 正則表達(dá)式庫(kù)等。
3. 其他通用規(guī)則
??? 還有一些在各個(gè)正則表達(dá)式引擎之間比較通用的規(guī)則,在前面的講解過(guò)程中沒(méi)有提到。
3.1 表達(dá)式中,可以使用 "\xXX" 和 "\uXXXX" 表示一個(gè)字符("X" 表示一個(gè)十六進(jìn)制數(shù))
形式 |
字符范圍 |
\xXX |
|
\uXXXX |
3.2 在表達(dá)式 "\s","\d","\w","\b" 表示特殊意義的同時(shí),對(duì)應(yīng)的大寫(xiě)字母表示相反的意義
表達(dá)式 |
可匹配 |
\S |
|
\D |
|
\W |
|
\B |
3.3 在表達(dá)式中有特殊意義,需要添加 "\" 才能匹配該字符本身的字符匯總
字符 |
說(shuō)明 |
^ |
匹配輸入字符串的開(kāi)始位置。要匹配 "^" 字符本身,請(qǐng)使用 "\^" |
$ |
匹配輸入字符串的結(jié)尾位置。要匹配 "$" 字符本身,請(qǐng)使用 "\$" |
( ) |
標(biāo)記一個(gè)子表達(dá)式的開(kāi)始和結(jié)束位置。要匹配小括號(hào),請(qǐng)使用 "\(" 和 "\)" |
[ ] |
用來(lái)自定義能夠匹配 '多種字符' 的表達(dá)式。要匹配中括號(hào),請(qǐng)使用 "\[" 和 "\]" |
{ } |
修飾匹配次數(shù)的符號(hào)。要匹配大括號(hào),請(qǐng)使用 "\{" 和 "\}" |
. |
匹配除了換行符(\n)以外的任意一個(gè)字符。要匹配小數(shù)點(diǎn)本身,請(qǐng)使用 "\." |
? |
修飾匹配次數(shù)為 0 次或 1 次。要匹配 "?" 字符本身,請(qǐng)使用 "\?" |
+ |
修飾匹配次數(shù)為至少 1 次。要匹配 "+" 字符本身,請(qǐng)使用 "\+" |
* |
修飾匹配次數(shù)為 0 次或任意次。要匹配 "*" 字符本身,請(qǐng)使用 "\*" |
| |
左右兩邊表達(dá)式之間 "或" 關(guān)系。匹配 "|" 本身,請(qǐng)使用 "\|" |
3.4 括號(hào) "( )" 內(nèi)的子表達(dá)式,如果希望匹配結(jié)果不進(jìn)行記錄供以后使用,可以使用 "(?:xxxxx)" 格式
??? ,結(jié)果是 "bbccdd"。括號(hào) "(?:)" 范圍的匹配結(jié)果不進(jìn)行記錄,因此 "(\w)" 使用 "\1" 來(lái)引用。
3.5 常用的表達(dá)式屬性設(shè)置簡(jiǎn)介:Ignorecase,Singleline,Multiline,Global
表達(dá)式屬性 |
說(shuō)明 |
Ignorecase |
默認(rèn)情況下,表達(dá)式中的字母是要區(qū)分大小寫(xiě)的。配置為 Ignorecase 可使匹配時(shí)不區(qū)分大小寫(xiě)。有的表達(dá)式引擎,把 "大小寫(xiě)" 概念延伸至 UNICODE 范圍的大小寫(xiě)。 |
Singleline |
默認(rèn)情況下,小數(shù)點(diǎn) "." 匹配除了換行符(\n)以外的字符。配置為 Singleline 可使小數(shù)點(diǎn)可匹配包括換行符在內(nèi)的所有字符。 |
Multiline |
默認(rèn)情況下,表達(dá)式 "^" 和 "$" 只匹配字符串的開(kāi)始 ① 和結(jié)尾 ④ 位置。如: |
Global |
主要在將表達(dá)式用來(lái)替換時(shí)起作用,配置為 Global 表示替換所有的匹配。 |
4. 綜合提示
4.1 如果要要求表達(dá)式所匹配的內(nèi)容是整個(gè)字符串,而不是從字符串中找一部分,那么可以在表達(dá)式的首尾使用 "^" 和 "$",比如:"^\d+$" 要求整個(gè)字符串只有數(shù)字。
4.2 如果要求匹配的內(nèi)容是一個(gè)完整的單詞,而不會(huì)是單詞的一部分,那么在表達(dá)式首尾使用 "\b",比如:。
4.3 表達(dá)式不要匹配空字符串。否則會(huì)一直得到匹配成功,而結(jié)果什么都沒(méi)有匹配到。比如:準(zhǔn)備寫(xiě)一個(gè)匹配 "123"、"123."、"123.5"、".5" 這幾種形式的表達(dá)式時(shí),整數(shù)、小數(shù)點(diǎn)、小數(shù)數(shù)字都可以省略,但是不要將表達(dá)式寫(xiě)成:"\d*\.?\d*",因?yàn)槿绻裁炊紱](méi)有,這個(gè)表達(dá)式也可以匹配成 功。。
4.4 能匹配空字符串的子匹配不要循環(huán)無(wú)限次。如果括號(hào)內(nèi)的子表達(dá)式中的每一部分都可以匹配 0 次,而這個(gè)括號(hào)整體又可以匹配無(wú)限次,那么情況可能比上一條所說(shuō)的更嚴(yán)重,匹配過(guò)程中可能死循環(huán)。雖然現(xiàn)在有些正則表達(dá)式引擎已經(jīng)通過(guò)辦法避免了這種情況 出現(xiàn)死循環(huán)了,比如 .NET 的正則表達(dá)式,但是我們?nèi)匀粦?yīng)該盡量避免出現(xiàn)這種情況。如果我們?cè)趯?xiě)表達(dá)式時(shí)遇到了死循環(huán),也可以從這一點(diǎn)入手,查找一下是否是本條所說(shuō)的原因。
4.5 合理選擇貪婪模式與非貪婪模式,參見(jiàn)話題討論。
4.6 或 "|" 的左右兩邊,對(duì)某個(gè)字符最好只有一邊可以匹配,這樣,不會(huì)因?yàn)?"|" 兩邊的表達(dá)式因?yàn)榻粨Q位置而有所不同。