Topquan's Blog

          分享價值----成就你我----我的博客----你的家

          JAVA基礎加強(二)

          “polymorphism(多態)”一詞來自希臘語,意為“多種形式”。多數Java程序員把多態看作對象的一種能力,使其能調用正確的方法版本。盡管如此,這種面向實現的觀點導致了多態的神奇功能,勝于僅僅把多態看成純粹的概念。

            Java中的多態總是子類型的多態。幾乎是機械式產生了一些多態的行為,使我們不去考慮其中涉及的類型問題。本文研究了一種面向類型的對象觀點,分析了如何將對象能夠表現的行為和對象即將表現的行為分離開來。拋開Java中的多態都是來自繼承的概念,我們仍然可以感到,Java中的接口是一組沒有公共代碼的對象共享實現。

            多態的分類

            多態在面向對象語言中是個很普遍的概念.雖然我們經常把多態混為一談,但實際上有四種不同類型的多態。在開始正式的子類型多態的細節討論前,然我們先來看看普通面向對象中的多態。

            Luca Cardelli和Peter Wegner("On Understanding Types, Data Abstraction, and Polymorphism"一文的作者, 文章參考資源鏈接)把多態分為兩大類----特定的和通用的----四小類:強制的,重載的,參數的和包含的。他們的結構如下:

          screen.width-600)this.style.width=screen.width-600;">

            在這樣一個體系中,多態表現出多種形式的能力。通用多態引用有相同結構類型的大量對象,他們有著共同的特征。特定的多態涉及的是小部分沒有相同特征的對象。四種多態可做以下描述:

            強制的:一種隱式做類型轉換的方法。

            重載的:將一個標志符用作多個意義。

            參數的:為不同類型的參數提供相同的操作。

            包含的:類包含關系的抽象操作。

            我將在講述子類型多態前簡單介紹一下這幾種多態。

            強制的多態

            強制多態隱式的將參數按某種方法,轉換成編譯器認為正確的類型以避免錯誤。在以下的表達式中,編譯器必須決定二元運算符‘+’所應做的工作:

            2.0 + 2.0

            2.0 + 2

            2.0 + "2"

            第一個表達式將兩個double的操作數相加;Java中特別聲明了這種用法。

            第二個表達式將double型和int相加。Java中沒有明確定義這種運算。不過,編譯器隱式的將第二個操作數轉換為double型,并作double型的加法。做對程序員來說十分方便,否則將會拋出一個編譯錯誤,或者強制程序員顯式的將int轉換為double。

            第三個表達式將double與一個String相加。Java中同樣沒有定義這樣的操作。所以,編譯器將double轉換成String類型,并將他們做串聯。

            強制多態也會發生在方法調用中。假設類Derived繼承了類Base,類C有一個方法,原型為m(Base),在下面的代碼中,編譯器隱式的將Derived類的對象derived轉化為Base類的對象。這種隱式的轉換使m(Base)方法使用所有能轉換成Base類的所有參數。

          C c = new C();

          Derived derived = new Derived();

          c.m( derived );

            并且,隱式的強制轉換,可以避免類型轉換的麻煩,減少編譯錯誤。當然,編譯器仍然會優先驗證符合定義的對象類型。

            重載的多態

            重載允許用相同的運算符或方法,去表示截然不同的意義。‘+’在上面的程序中有兩個意思:兩個double型的數相加;兩個串相連。另外還有整型相加,長整型,等等。這些運算符的重載,依賴于編譯器根據上下文做出的選擇。以往的編譯器會把操作數隱式轉換為完全符合操作符的類型。雖然Java明確支持重載,但不支持用戶定義的操作符重載。

            Java支持用戶定義的函數重載。一個類中可以有相同名字的方法,這些方法可以有不同的意義。這些重載的方法中,必須滿足參數數目不同,相同位置上的參數類型不同。這些不同可以幫助編譯器區分不同版本的方法。

            編譯器以這種唯一表示的特征來表示不同的方法,比用名字表示更為有效。據此,所有的多態行為都能編譯通過。

            強制和重載的多態都被分類為特定的多態,因為這些多態都是在特定的意義上的。這些被劃入多態的特性給程序員帶來了很大的方便。強制多態排除了麻煩的類型和編譯錯誤。重載多態像一塊糖,允許程序員用相同的名字表示不同的方法,很方便。

            參數的多態

            參數多態允許把許多類型抽象成單一的表示。例如,List抽象類中,描述了一組具有同樣特征的對象,提供了一個通用的模板。你可以通過指定一種類型以重用這個抽象類。這些參數可以是任何用戶定義的類型,大量的用戶可以使用這個抽象類,因此參數多態毫無疑問的成為最強大的多態。

            乍一看,上面抽象類好像是java.util.List的功能。然而,Java實際上并不支持真正的安全類型風格的參數多態,這也是java.util.List和java.util的其他集合類是用原始的java.lang.Object寫的原因(參考我的文章"A Primordial Interface?" 以獲得更多細節)。Java的單根繼承方式解決了部分問題,但沒有發揮出參數多態的全部功能。Eric Allen有一篇精彩的文章“Behold the Power of Parametric Polymorphism”,描述了Java通用類型的需求,并建議給Sun的Java規格需求#000014號文檔"Add Generic Types to the Java Programming Language."(參考資源鏈接)

            包含的多態

            包含多態通過值的類型和集合的包含關系實現了多態的行為.在包括Java在內的眾多面向對象語言中,包含關系是子類型的。所以,Java的包含多態是子類型的多態。

            在早期,Java開發者們所提及的多態就特指子類型的多態。通過一種面向類型的觀點,我們可以看到子類型多態的強大功能。以下的文章中我們將仔細探討這個問題。為簡明起見,下文中的多態均指包含多態。

            面向類型觀點

            圖1的UML類圖給出了類和類型的簡單繼承關系,以便于解釋多態機制。模型中包含5種類型,4個類和一個接口。雖然UML中稱為類圖,我把它看成類型圖。如"Thanks Type and Gentle Class," 一文中所述,每個類和接口都是一種用戶定義的類型。按獨立實現的觀點(如面向類型的觀點),下圖中的每個矩形代表一種類型。從實現方法看,四種類型運用了類的結構,一種運用了接口的結構。

          screen.width-600)this.style.width=screen.width-600;">
          圖1:示范代碼的UML類圖

            以下的代碼實現了每個用戶定義的數據類型,我把實現寫得很簡單。

          /* Base.java */

          public class Base
          {
           public String m1()
           {
            return "Base.m1()";
           }

           public String m2( String s )
           {
            return "Base.m2( " + s + " )";
           }
          }

          /* IType.java */

          interface IType
          {
           String m2( String s );
           String m3();
          }

          /* Derived.java */

          public class Derived
          extends Base
          implements IType
          {
           public String m1()
           {
            return "Derived.m1()";
           }

           public String m3()
           {
            return "Derived.m3()";
           }
          }

          /* Derived2.java */

          public class Derived2
          extends Derived
          {
           public String m2( String s )
           {
            return "Derived2.m2( " + s + " )";
           }
           public String m4()
           {
            return "Derived2.m4()";
           }
          }

          /* Separate.java */

          public class Separate
          implements IType
          {
           public String m1()
           {
            return "Separate.m1()";
           }
           public String m2( String s )
           {
            return "Separate.m2( " + s + " )";
           }

           public String m3()
           {
            return "Separate.m3()";
           }
          }


            用這樣的類型聲明和類的定義,圖2從概念的觀點描述了Java指令。

          Derived2 derived2 = new Derived2();

          screen.width-600)this.style.width=screen.width-600;">
          圖2 :Derived2 對象上的引用

            上文中聲明了derived2這個對象,它是Derived2類的。圖2種的最頂層把Derived2引用描述成一個集合的窗口,雖然其下的Derived2對象是可見的。這里為每個Derived2類型的操作留了一個孔。Derived2對象的每個操作都去映射適當的代碼,按照上面的代碼所描述的那樣。例如,Derived2對象映射了在Derived中定義的m1()方法。而且還重載了Base類的m1()方法。一個Derived2的引用變量無權訪問Base類中被重載的m1()方法。但這并不意味著不可以用super.m1()的方法調用去使用這個方法。關系到derived2這個引用的變量,這個代碼是不合適的。Derived2的其他的操作映射同樣表明了每種類型操作的代碼執行。

            既然你有一個Derived2對象,可以用任何一個Derived2類型的變量去引用它。如圖1所示,Derived, Base和IType都是Derived2的基類。所以,Base類的引用是很有用的。圖3描述了以下語句的概念觀點。

          Base base = derived2;

          screen.width-600)this.style.width=screen.width-600;">
          圖3:Base類引用附于Derived2對象之上
           
            雖然Base類的引用不用再訪問m3()和m4(),但是卻不會改變它Derived2對象的任何特征及操作映射。無論是變量derived2還是base,其調用m1()或m2(String)所執行的代碼都是一樣的。

          String tmp;
          // Derived2 reference (Figure 2)
          tmp = derived2.m1(); // tmp is "Derived.m1()"
          tmp = derived2.m2( "Hello" ); // tmp is "Derived2.m2( Hello )"

          // Base reference (Figure 3)

          tmp = base.m1(); // tmp is "Derived.m1()"
          tmp = base.m2( "Hello" ); // tmp is "Derived2.m2( Hello )"


            兩個引用之所以調用同一個行為,是因為Derived2對象并不知道去調用哪個方法。對象只知道什么時候調用,它隨著繼承實現的順序去執行。這樣的順序決定了Derived2對象調用Derived里的m1()方法,并調用Derived2里的m2(String)方法。這種結果取決于對象本身的類型,而不是引用的類型。

            盡管如此,但不意味著你用derived2和base引用的效果是完全一樣的。如圖3所示,Base的引用只能看到Base類型擁有的操作。所以,雖然Derived2有對方法m3()和m4()的映射,但是變量base不能訪問這些方法。

          String tmp;
          // Derived2 reference (Figure 2)
          tmp = derived2.m3(); // tmp is "Derived.m3()"
          tmp = derived2.m4(); // tmp is "Derived2.m4()"

          // Base reference (Figure 3)

          tmp = base.m3(); // Compile-time error
          tmp = base.m4(); // Compile-time error


            運行期的Derived2對象保持了接受m3()和m4()方法的能力。類型的限制使Base的引用不能在編譯期調用這些方法。編譯期的類型檢查像一套鎧甲,保證了運行期對象只能和正確的操作進行相互作用。換句話說,類型定義了對象間相互作用的邊界。

            多態的依附性

            類型的一致性是多態的核心。對象上的每一個引用,靜態的類型檢查器都要確認這樣的依附和其對象的層次是一致的。當一個引用成功的依附于另一個不同的對象時,有趣的多態現象就產生了。(嚴格的說,對象類型是指類的定義。)你也可以把幾個不同的引用依附于同一個對象。在開始更有趣的場景前,我們先來看一下下面的情況為什么不會產生多態。

            多個引用依附于一個對象

            圖2和圖3描述的例子是把兩個及兩個以上的引用依附于一個對象。雖然Derived2對象在被依附之后仍保持了變量的類型,但是,圖3中的Base類型的引用依附之后,其功能減少了。結論很明顯:把一個基類的引用依附于派生類的對象之上會減少其能力。

            一個開發這怎么會選擇減少對象能力的方案呢?這種選擇是間接的。假設有一個名為ref的引用依附于一個包含如下方法的類的對象:

          public String poly1( Base base )
          {
           return base.m1();
          }


            用一個Derived2的參數調用poly(Base)是符合參數類型檢查的:

          ref.poly1( derived2 );


            方法調用把一個本地Base類型的變量依附在一個引入的對象上。所以,雖然這個方法只接受Base類型的參數,但Derived2對象仍是允許的。開發這就不必選擇丟失功能的方案。從人眼在通過Derived2對象時所看到的情況,Base類型引用的依附導致了功能的喪失。但從執行的觀點看,每一個傳入poly1(Base)的參數都認為是Base的對象。執行機并不在乎有多個引用指向同一個對象,它只注重把指向另一個對象的引用傳給方法。這些對象的類型不一致并不是主要問題。執行器只關心給運行時的對象找到適當的實現。面向類型的觀點展示了多態的巨大能力。

            附于多個對象的引用

            讓我們來看一下發生在poly1(Base)中的多態行為。下面的代碼創建了三個對象,并通過引用傳給poly1(Base):

          Derived2 derived2 = new Derived2();
          Derived derived = new Derived();
          Base base = new Base();

          String tmp;

          tmp = ref.poly1( derived2 ); // tmp is "Derived.m1()"
          tmp = ref.poly1( derived ); // tmp is "Derived.m1()"
          tmp = ref.poly1( base ); // tmp is "Base.m1()"


            poly1(Base)的實現代碼是調用傳進來的參數的m1()方法。圖3和圖4展示了把三個類的對象傳給方法時,面向類型的所使用的體系結構。

          screen.width-600)this.style.width=screen.width-600;">
          圖4:將Base引用指向Derived類,以及Base對象

            請注意每個圖中方法m1()的映射。圖3中,m1()調用了Derived類的代碼;上面代碼中的注釋標明了ploy1(Base)調用Derived.m1()。圖4中Derived對象調用的仍然是Derived類的m1()方法。最后,圖4中,Base對象調用的m1()是Base類中定義的代碼。

            多態的魅力何在?再來看一下poly1(Base)的代碼,它可以接受任何屬于Base類范疇的參數。然而,當他收到一個Derived2的對象時,它實際上卻調用了Derived版本的方法。當你根據Base類派生出其他類時,如Derived,Derived2,poly1(Base)都可以接受這些參數,并作出選擇調用合適的方法。多態允許你在完成poly1(Base)后擴展它的用途。

            這看起來當然很神奇。基本的理解展示了多態的內部工作原理。在面向類型的觀點中,底層的對象所實現的代碼是非實質性的。重要的是,類型檢查器會在編譯期間為每個引用選擇合適的代碼以實現其方法。多態使開發者運用面向類型的觀點,不考慮實現的細節。這樣有助于把類型和實現分離(實際用處是把接口和實現分離)。

            對象接口

            多態依賴于類型和實現的分離,多用來把接口和實現分離。但下面的觀點好像把Java的關鍵字interface搞得很糊涂。

            更為重要的使開發者們怎樣理解短語“the interface to an object",典型地,根據上下文,這個短語的意思是指一切對象類中所定義的方法,至一切對象公開的方法。這種傾向于以實現為中心的觀點較之于面向類型的觀點來說,使我們更加注重于對象在運行期的能力。圖3中,引用面板的對象表面被標志成"Derived2 Object"。這個面板上列出了Derived2對象的所有可用的方法。但是要理解多態,我們必須從實現這一層次上解放出來,并注意面向類型的透視圖中被標為"Base Reference"的面板。在這一層意思上,引用變量的類型指明了一個對象的表面。這只是一個表面,不是接口。在類型一致的原則下,我們可以用面向類型的觀點,為一個對象依附多個引用。對interface to an object這個短語的理解沒有確定的理解。

            在類型概念中,the interface to an object refers 引用了面向類型觀點的最大可能----如圖2的情形。把一個基類的引用指向相同的對象縮小了這樣的觀點----如圖3所示。類型概念能使人獲得把對象間的相互作用同實現細節分離的要領。相對于一個對象的接口,面向類型的觀點更鼓勵人們去使用一個對象的引用。引用類型規定了對象間的相互作用。當你考慮一個對象能做什么的時候,只需搞明白他的類型,而不需要去考慮他的實現細節。

            Java接口

            以上所談到的多態行為用到了類的繼承關系所建立起來的子類型關系。Java接口同樣支持用戶定義的類型,相對地,Java的接口機制啟動了建立在類型層次結構上的多態行為。假設一個名為ref的引用變量,并使其指向一個包含一下方法的類對象:

          public String poly2( IType iType )
          {
           return iType.m3();
          }

            為了弄明白poly2(IType)中的多態,以下的代碼從不同的類創建兩個對象,并分別把他們傳給poly2(IType):

          Derived2 derived2 = new Derived2();
          Separate separate = new Separate();

          String tmp;

          tmp = ref.poly2( derived2 ); // tmp is "Derived.m3()"
          tmp = ref.poly2( separate ); // tmp is "Separate.m3()"

            上面的代碼類似于關于poly1(Base)中的多態的討論。poly2(IType)的實現代碼是調用每個對象的本地版本的m3()方法。如同以前,代碼的注釋表明了每次調用所返回的CString類型的結果。圖5表明了兩次調用poly2(IType)的概念結構:


          圖5:指向Derived2和Separate對象的IType引用

            方法poly1(Base)和poly2(IType)中所表現的多態行為的相似之處可以從透視圖中直接看出來。把我們在實現在一層上的理解再提高一層,就可以看到這兩段代碼的技巧。基類的引用指向了作為參數傳進的類,并且按照類型的限制調用對象的方法。引用既不知道也不關心執行哪一段代碼。編譯期間的子類型關系檢查保證了通過的對象有能力在被調用的時候選擇合適的實現代碼。

            然而,他們在實現層上有一個重要的差別。在poly1(Base)的例子中(圖3和圖4),Base-Derived-Derived2的類繼承結構為子類型關系的建立提供了條件,并決定了方法去調用哪段代碼。在poly2(IType)的例子中(如圖5),則是完全不同的動態發生的。Derived2和Separate不共享任何實現的層次,但是他們還是通過IType的引用展示了多態的行為。

            這樣的多態行為使Java的接口的功能的重大意義顯得很明顯。圖1中的UML類圖說明了Derived是Base和IType的子類型。通過完全脫離實現細節的類型的定義方法,Java實現了多類型繼承,并且不存在Java所禁止的多繼承所帶來的煩人的問題。完全脫離實現層次的類可以按照Java接口實現分組。在圖1中,接口IType和Derived,Separate以及這類型的其他子類型應該劃為一組。

            按照這種完全不同于實現層次的分類方法,Java的接口機制是多態變得很方便,哪怕不存在任何共享的實現或者復寫的方法。如圖5所示,一個IType的引用,用多態的方法訪問到了Derived2和Separate對象的m3()方法。

            再次探討對象的接口

            注意圖5中的Derived2和Separate對象的對m1()的映射方法。如前所述,每一個對象的接口都包含方法m1()。但卻沒有辦法用這兩個對象使方法m1()表現出多態的行為。每一個對象占有一個m1()方法是不夠的。必須存在一個可以操作m1()方法的類型,通過這個類型可以看到對象。這些對象似乎是共享了m1()方法,但在沒有共同基類的條件下,多態是不可能的。通過對象的接口來看多態,會把這個概念搞混。

            結論

            從全文所述的面向對象多態所建立起來的子類型多態,你可以清楚地認識到這種面向類型的觀點。如果你想理解子類型多態的思想,就應該把注意力從實現的細節轉移到類型的上。類型把對象分成組,并且管理著這些對象的接口。類型的繼承層次結構決定了實現多態所需的類型關系。

            有趣的是,實現的細節并不影響子類型多態的層次結構。類型決定了對象調用什么方法,而實現則決定了對象怎么執行這個方法。也就是說,類型表明了責任,而負責實施的則是具體的實現。將實現和類型分離后,我們好像看到了這兩個部分在一起跳舞,類型決定了他的舞伴和舞蹈的名字,而實現則是舞蹈動作的設計師。

          J2EE學習者越來越多,J2EE本身技術不斷在發展,涌現出各種概念,本文章試圖從一種容易理解的角度對這些概念向初學者進行解釋,以便掌握學習J2EE學習方向。首先我們需要知道Java和J2EE是兩個不同概念,Java不只是指一種語言,已經代表與微軟不同的另外一個巨大陣營,所以Java有時是指一種軟件系統的流派,當然目前主要是.NET和Java兩大主流體系。

            J2EE可以說指Java在數據庫信息系統上實現,數據庫信息系統從早期的dBase、到Delphi/VB等C/S結構,發展到B/S(Browser瀏覽器/Server服務器)結構,而J2EE主要是指B/S結構的實現。

            J2EE又是一種框架和標準,框架類似API、庫的概念,但是要超出它們。如果需要詳細了解框架,可先從設計模式開始學習。

            J2EE是一個虛的大的概念,J2EE標準主要有三種子技術標準:WEB技術、EJB技術和JMS,談到J2EE應該說最終要落實到這三個子概念上。

            這三種技術的每個技術在應用時都涉及兩個部分:容器部分和應用部分,Web容器也是指Jsp/Servlet容器,你如果要開發一個Web應用,無論是編譯或運行,都必須要有Jsp/Servlet庫或API支持(除了JDK/J2SE以外)。

            Web技術中除了Jsp/Servlet技術外,還需要JavaBeans或Java Class實現一些功能或者包裝攜帶數據,所以Web技術最初裸體簡稱為Jsp/Servlet+JavaBeans系統。
          談到JavaBeans技術,就涉及到組件構件技術(component),這是Java的核心基礎部分,很多軟件設計概念(設計模式)都是通過JavaBeans實現的。

            JavaBeans不屬于J2EE概念范疇中,如果一個JavaBeans對象被Web技術(也就是Jsp/Servlet)調用,那么JavaBeans就運行在J2EE的Web容器中;如果它被EJB調用,它就運行在EJB容器中。

            EJB(企業JavaBeans)是普通JavaBeans的一種提升和規范,因為企業信息系統開發中需要一個可伸縮的性能和事務、安全機制,這樣能保證企業系統平滑發展,而不是發展到一種規模重新更換一套軟件系統。

            至此,JavaBeans組件發展到EJB后,并不是說以前的那種JavaBeans形式就消失了,這就自然形成了兩種JavaBeans技術:EJB和POJO,POJO完全不同于EJB概念,指的是普通JavaBeans,而且這個JavaBeans不依附某種框架,或者干脆可以說:這個JavaBeans是你為這個應用程序單獨開發創建的。

            J2EE應用系統開發工具有很多:如JBuilder、Eclipse等,這些IDE首先是Java開發工具,也就是說,它們首要基本功能是可以開發出JavaBeans或Java class,但是如果要開發出J2EE系統,就要落實到要么是Web技術或EJB技術,那么就有可能要一些專門模塊功能(如eclipse需要lomboz插件),最重要的是,因為J2EE系統區分為容器和應用兩個部分,所以,在任何開發工具中開發J2EE都需要指定J2EE容器。

            J2EE容器分為WEB容器和EJB容器,Tomcat/Resin是Web容器;JBoss是EJB容器+Web容器等,其中Web容器直接使用Tomcat實現的。所以你開發的Web應用程序可以在上面兩種容器運行,而你開發的Web+EJB應用則只可以在JBoss服務器上運行,商業產品Websphere/Weblogic等和JBoss屬于同一種性質。

            J2EE容器也稱為J2EE服務器,大部分時它們概念是一致的。
          如果你的J2EE應用系統的數據庫連接是通過JNDI獲得,也就是說是從容器中獲得,那么你的J2EE應用系統基本與數據庫無關,如果你在你的J2EE應用系統耦合了數據庫JDBC驅動的配置,那么你的J2EE應用系統就有數據庫概念色彩,作為一個成熟需要推廣的J2EE應用系統,不推薦和具體數據庫耦合,當然這其中如何保證J2EE應用系統運行性能又是體現你的設計水平了。

            衡量J2EE應用系統設計開發水平高低的標準就是:解耦性;你的應用系統各個功能是否能夠徹底脫離?是否不相互依賴,也只有這樣,才能體現可維護性、可拓展性的軟件設計目標。

            為了達到這個目的,誕生各種框架概念,J2EE框架標準將一個系統劃分為WEB和EJB主要部分,當然我們有時不是以這個具體技術區分,而是從設計上抽象為表現層、服務層和持久層,這三個層次從一個高度將J2EE分離開來,實現解耦目的。

            因此,我們實際編程中,也要將自己的功能向這三個層次上靠,做到大方向清楚,涇渭分明,但是沒有技術上約束限制要做到這點是很不容易的,因此我們還是必須借助J2EE具體技術來實現,這時,你可以使用EJB規范實現服務層和持久層,Web技術實現表現層;

            EJB為什么能將服務層從Jsp/Servlet手中分離出來,因為它對JavaBeans編碼有強制的約束,現在有一種對JavaBeans弱約束,使用Ioc模式實現的(當然EJB 3.0也采取這種方式),在Ioc模式誕生前,一般都是通過工廠模式來對JavaBeans約束,形成一個服務層,這也是是Jive這樣開源論壇設計原理之一。

            由此,將服務層從表現層中分離出來目前有兩種可選架構選擇:管理普通JavaBeans(POJO)框架(如Spring、JdonFramework)以及管理EJB的EJB框架,因為EJB不只是框架,還是標準,而標準可以擴展發展,所以,這兩種區別將來是可能模糊,被納入同一個標準了。 但是,個人認為:標準制定是為某個目的服務的,總要犧牲一些換取另外一些,所以,這兩種架構會長時間并存。

            這兩種架構分歧也曾經誕生一個新名詞:完全POJO的系統也稱為輕量級系統(lightweight),其實這個名詞本身就沒有一個嚴格定義,更多是一個吸引人的招牌,輕量是指容易學習容易使用嗎?按照這個定義,其實輕量Spring等系統并不容易學習;而且EJB 3.0(依然叫EJB)以后的系統是否可稱為輕量級了呢?
          前面談了服務層框架,使用服務層框架可以將JavaBeans從Jsp/Servlet中分離出來,而使用表現層框架則可以將Jsp中剩余的JavaBeans完全分離,這部分JavaBeans主要負責顯示相關,一般是通過標簽庫(taglib)實現,不同框架有不同自己的標簽庫,Struts是應用比較廣泛的一種表現層框架。

            這樣,表現層和服務層的分離是通過兩種框架達到目的,剩余的就是持久層框架了,通過持久層的框架將數據庫存儲從服務層中分離出來是其目的,持久層框架有兩種方向:直接自己編寫JDBC等SQL語句(如iBatis);使用O/R Mapping技術實現的Hibernate和JDO技術;當然還有EJB中的實體Bean技術。

            持久層框架目前呈現百花齊放,各有優缺點的現狀,所以正如表現層框架一樣,目前沒有一個框架被指定為標準框架,當然,表現層框架現在又出來了一個JSF,它代表的頁面組件概念是一個新的發展方向,但是復雜的實現讓人有些忘而卻步。

            在所有這些J2EE技術中,雖然SUN公司發揮了很大的作用,不過總體來說:網絡上有這樣一個評價:SUN的理論天下無敵;SUN的產品用起來撞墻;對于初學者,特別是那些試圖通過或已經通過SUN認證的初學者,趕快擺脫SUN的陰影,立即開溜,使用開源領域的產品來實現自己的應用系統。

            最后,你的J2EE應用系統如果采取上面提到的表現層、服務層和持久層的框架實現,基本你也可以在無需深刻掌握設計模式的情況下開發出一個高質量的應用系統了。

            還要注意的是: 開發出一個高質量的J2EE系統還需要正確的業務需求理解,那么域建模提供了一種比較切實可行的正確理解業務需求的方法,相關詳細知識可從UML角度結合理解。

            當然,如果你想設計自己的行業框架,那么第一步從設計模式開始吧,因為設計模式提供你一個實現JavaBeans或類之間解耦參考實現方法,當你學會了系統基本單元JavaBean或類之間解耦時,那么系統模塊之間的解耦你就可能掌握,進而你就可以實現行業框架的提煉了,這又是另外一個發展方向了。

          public class statictest {
          ??? public statictest()
          ??? {???
          ??? }
          ??? public static void prin(String s)
          ??? {
          ??? System.out.println(s);
          ??? }
          ??? public static int i=printy("hehe");
          ??? public static int printy(String s)
          ????? {
          ????????? System.out.println(s);
          ????????? return 4;
          ?????? }
          ??? public static void main(String[] args) {
          ??????? statictest.prin("fdsafa");
          ??? }
          }
          輸出結果 hehe fdsafa
          當生成一個類的對象時,或者首次訪問屬于哪個類的靜態數據成員時,,進行初始化.
          package untitled4;
          class teststatic
          {
          ?? static int i=prin("test");
          ?? static int prin(String s)
          ?? {
          ?????? System.out.println(s);
          ?????? return 2;
          ?? }
          ?? static void play()
          ?? {
          ?? System.out.println("play");
          ?? }

          }
          public class statictest2 {
          ??? public statictest2() {
          ??? }

          ??? public static void main(String[] args) {
          ????? teststatic.play();
          ??? }
          }
          輸出結果?? TEST PLAY
          對于類CLASS A的執行相當于調用A.main(),,他首先對A的元素初始化(遵循,從A的基類開始,STATIC)
          非STATIC在對象生成時候才初始化

          posted on 2006-05-08 22:57 topquan 閱讀(208) 評論(0)  編輯  收藏 所屬分類: JAVA Base&App


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 久治县| 十堰市| 左云县| 昌平区| 邻水| 正宁县| 贵溪市| 什邡市| 景宁| 宜宾县| 大同县| 横峰县| 沂源县| 信阳市| 龙口市| 绍兴市| 沙洋县| 宜阳县| 常山县| 澎湖县| 台北市| 兴仁县| 宁陵县| 偏关县| 民和| 鄂托克前旗| 民丰县| 陆良县| 白山市| 佛山市| 读书| 鸡西市| 岳阳市| 九台市| 攀枝花市| 阿拉尔市| 昔阳县| 睢宁县| 九江市| 清流县| 隆安县|