|
本文就java基礎部分容易混淆的一些知識點進行了一下總結。因為Java本身知識點非常多,不可能在很短的篇幅就能敘述完,而且就某一個點來講,如欲仔細去探究,也能闡述的非常多。這里不做全面仔細的論述,僅做為一個引子,拋磚引玉。具體個例,還需各位看官自己驗證一下,以增進理解和記憶。 ?? ??歡迎就這一部分各位朋友與我進行探討,共同進步。 1、雖然有很多朋友可能進行了多年的java開發老手,但可能仍舊對某些點缺乏仔細探究。 2、去一些公司求職面試或筆試時的技術題目中,也往往會涉及到這里的一些內容。 ??所以,希望下邊的這些總結能夠對一些學習java或求職的朋友有些許幫助。
??? ?1、?關于java類中的缺省的構造器 如果一個java類沒有顯式定義沒有參數的構造器,將有一個默認缺省的構造器。如果定義了一個有參數的構造器,那么原來的缺省的構造器將不在有效。 public?class?A{? } 此時如果用?new?A();?java編譯器將使用缺省的構造器。 public?class?A{? public?A(int?i){? } } 如果此時用?new?A();?將產生一個編譯錯誤,因為此時顯式定義了,一個有參數的構造器。
2、Java中的類名與文件名 1、在一個java文件中可以有多于一個類定義(更常見于某些組件的監聽器類),但只能有一個public?class定義,且與文件同名。 2、如果一個java源文件中沒有public類,那么每個類的名字沒特殊規則,即不必與文件同名。 3、在編譯后產生的class文件中,仍舊是多個單獨分開的class文件。
3、import關鍵字 1、import語句必須定義在所有的class定義之前。 2、import語句只是為編譯器指明了一個路徑,并不像C或C++中的#include,所以用import?.*并不影響性能
4、Java中的幾個特殊關鍵字 Java中的關鍵字許多大家都比較熟悉,而有幾個就不是很常用,如: 1、goto和const是保留關鍵字,在java中沒使用 2、strictfp和volatile不常用;?sizeof、zhen不是關鍵字。 3、true,false,null不是嚴格意義上的關鍵字,而是literals。
?? 5、java方法中的傳遞值參 在Java方法中傳遞參數,對于基本類型來講傳遞的是值參數,相當于建立的一個參數的拷貝,不影響原來變量的值。 在引用方法中可以改變傳遞對象的內容,但對象引用(像A@5d87b2)從來不會改變。
public?class?tt{? public?static?void?main?(String?args[]){ ??A?aa?=?new?A(); ??aa.num?=5; ??tt?t?=?new?tt(); ??System.out.println("11?aa="+aa?+?"num="+aa.num); ??t.test(aa); ??System.out.println("22?aa="+aa?+?"num="+aa.num); }? void?test(A?a){ ??A?ab?=?new?A(); ??a?=?ab; ??System.out.println("33?ab="+ab?+?"num="+ab.num); }? } class?A{? int?num; }? ?? 6、變量初始化 java中的變量在使用之前必須被初始化,當創建一個對象的時候一些類的變量會自動初始化并賦予缺省值。 數字類賦值0;char類型賦值'\u0000';?boolean類型賦值false;引用對象賦值null; 注意的是在方法之外的類變量的值是自動賦初始值,而方法內的局部變量必須手工初始化。?
class?AA{? int?num;? void?test(){ ??int?j; ??j?=5;//沒有這一行則編譯不會通過。 ??j?=?j+num; } }? ?? 7、switch語句 這個點經常在求職筆試題目中出現。default放在最上邊編譯沒問題;碰到符合分支的,如果沒有break會一直向下運行。?
public?class?tt{? public?static?void?main?(String?args[]){? ??tt?t?=?new?tt();? ??t.test(2);//可改變成3運行一下看一下結果? }? void?test(int?i){ ??switch?(i){ ????default:? ????System.out.println("default");? ????case?1:? ????System.out.println("111"); ????break; ????case?2: ????System.out.println("222"); ????break;? ??} }? }? ?? 8、關于java中的label使用 ??break?[label] ??continue[lbele] ??lable:?statement;?//這里的statement必須是一個loop循環 public?class?tt{? public?static?void?main?(String?args[]){? ??tt?t?=?new?tt(); ??t.test();? } void?test(){? ??System.out.println("0000"); ??lb1:for?(int?i=0;i<10;i++){ ????lb2:for?(int?j=0;?j<2;?j++){ ??????if?(i==2)?continue?lb1; ??????System.out.println("i="+i?+"?j="+j);? ????}? ??} ??System.out.println("111111");? }? }? ?? 9、類型轉換校正 class?Employee ??????| class?Manager 向上校正,總是允許的,Manager直接使用父類Employee的方法。 向下校正,必須用instanceof檢驗,才能將一個Employee轉換為Manager對象。
public?void?test(Employee?e){ if?(e?instanceof?Manager){ ??Manager?m?=?(Mnager)e; ??... } }? ?? 10、方法重載(overloading)、方法覆蓋(overriding)
方法重載(overloading)一定要求名字相同,參數不同,返回類型可以相同也可以不同
class?A{? void?test(int?i){? } } class?AA?extends?A{ int?test(int?i,?int?j){ ??return?5;? }? }? 注:方法覆蓋(overriding)要求名字,參數,返回類型全部必須相同,訪問控制符可以不同,但必須大過父類的。因為如果名字和參數都已經相同了則一定要求返回類型相同,否則認為這是一個新的方法了,名字就必須不同了。
class?A{? void?test(int?i){? } } class?AA?extends?A{ public?void?test(int?i){//若是換成private則編譯不通過。? }? }
注:關于覆蓋方法拋出異常的問題。如A是父類,B是繼承A的子類。B中的方法meth()去覆蓋父類A的此方法時,B中不能throws出新的異常,只能是父類拋出的異?;蚱渥蛹8踔量梢圆粧伋霎惓!?
?? 11、關于類的構造器重載問題
class?A{ public?A(int?i){ }? } class?AA?extends?A{ public?AA(){ ??int?i?=?5;?//?這里出錯,沒有父構造器? } }? 由于父類A自定義了構造器,所以缺省的構造器就丟失了,當子類的構造器自動試圖調用父類沒參數的構造器時卻沒有,所以會編譯出錯。?
?? 12、關于static關鍵字總結: 1、不能在static修飾的方法中引用this變量,只能引用一些靜態變量或方法,或new新的對象(可以定義局部變量)。 簡言之,靜態方法或塊中,只能引用靜態的方法或變量。 2、類中的成員變量(static修飾)有缺省值,而類的定義的方法中的局部變量沒有缺省值。 3、在類的構造器中,可以引用任何的靜態或非靜態的變量和方法,可以在非static方法中調用static方法。 4、static{}塊中的代碼在類裝載中僅執行一次。 5、在7-7,A?static?method?cannot?be?overridden?but?can?be?hidden.?不理解。 6、不能在無論非static方法中或static方法中定義static變量。?
?? 13、關于final關鍵字 1、不能繼承final修飾的類,不能覆蓋final修飾的方法。 2、final修飾的變量,若沒賦值,必須在構造器中賦初始值。
class?A{ final?int?j; public?A(){? ??j?=?9;//若沒有此行,則編譯不通過。? }? } 3、final類型的方法參數可定義,但不能改變。 class?A{ void?m(final?int?i){?//這一行的聲明i為一個final沒問題。 ??i?++?;?//但在這里面,i的值不能再被改變。? } }?
?? 14、Interface接口關鍵字 1、接口中的變量 ??1、必須初始化其值。 ??2、默認修飾符為public+static+final,其他的修飾符不允許。 2、接口中的方法 ??1、默認為public+abstract ??2、其它修飾符?static,private,protected,final,synchronized,native均不能有。
interface?A{ void?s(); } class?AA?implements?A{ void?s(){?//編譯器在這里提示由于接口中的方法s()修飾符默認是public, ????????//而這里的s()默認是protected,小于public所以不允許。 } }?
?? 15、abstract抽象關鍵字 abstract?class?A{ private?int?i;? private?void?m();{} }? 抽象類中可以有私有的變量和私有屬性,而接口就不行(原因如上), 這是因為java是按實例虛擬調用的,在生成某一個具體的對象可以有私有的屬性或方法的。
abstract?class?A{ private?int?i;? private?void?m(){}; public?abstract?void?n();//若是private則編譯不通過。 } 抽象類中的抽象方法是讓其他類繼承的,如果本身都是私有的,就沒有什么意義了?
?? 16、集合類型 以有無順序,允許不允許重復區分 Collections:?一組對象,無序集合,允許重復 Set:無序集合,不允許重復 List:有序集合,允許重復 注意:在JDK1.1中定義的集合類型,都是線程安全的,所以都是“重量級”的。像HashTable,Vector 而在java2中定義的一些新的集合類型如HashMap,?ArrayList不是線程安全的,是“輕量級”的,但速度快,性能好。這一點在許多公司面試試題都見過。?
?? 17、布局管理器 FlowLayout,BorderLayout,GridLayout,CardLayout 關于Panel和Frame默認的Layout常在一些公司的面試試題中出現。 1、Panel和Applet類默認的布局管理器是FlowLayout?一個一個的加上去 2、Frame和window類默認的布局管理器是BorderLayout?按東南西北加入 3、xyLayout是Borland公司開發的布局管理器。?
?? 18、面試試題中Applet部分 1、使用代碼 ??<applet?code?=?"a.class"?width=100?height=200> ????<param?name=a?vlaue"11"> ??</applet> 2、可以覆蓋的方法init(),start(),stop(),destory(),paint(g)?
?? 19、面試試題中線程部分 1、基本實現方式兩中,繼承Thread類和實現Runnable接口 2、必須實現父類或接口中的run()方法。 3、有關線程方法,start()啟動線程。 ??join()指在調用這個線程的方法或進程中,必須等待此線程運行結束才能繼續其他進程。 4、線程中的同步synchronized,注意死鎖。?
?? 20、對象串行化 1、僅僅對象類型的數據可以串行化。 2、標記為transient的數據不可以串行化。 存儲一個對象到某種永久性存儲叫persistence,如存儲到磁盤、磁帶或別的機器的內存中。 java.io.Serializable接口沒有定義方法要實現,僅僅是一個標記暗示實現了這個接口的類可以被考慮串行化。沒有實現這個接口的對象不能保存或存儲它們的狀態。 當一個對象被串行化的時候,僅僅數據被保留,而方法和構造器不是串行化的部分。 一些對象類是不能串行化的因為他們代表的數據是經常變化的。如java.io.FileInputSream和java.langThread。如果串行化的對象包含了不可串行化的對象,整個串行化動作會失敗,并拋出NotSerializableException。?
?? 21、java中的網絡通訊
一般的TCP/IP網絡數據通信主要可分2種,TCP和UDP
TCP:TCP是面向連接的通信協議,就像打電話,先要撥通建立連接,傳送的數據不會丟失。 ??java提供了ServerSocket和socket類。在server端,建立一個serverSocket,并指定端口,并偵聽連接。
服務器端代碼? ServerSocket?sc=new?ServerSocket(1111); Socket?socket1=?sc.accept(); DataInputStream?s_in?=?new?DataInputStream(socket1.getInputStream());
客戶端代碼? Socket?socket2?=?new?Socket("192.168.1.1",1111);?
UDP:UDP非面向連接,就像寫信,將傳輸的數據包成一個分組,可能有數據丟失
服務器端代碼 DatagramSocket?server?=?new?DatagramSocket(1234); DatagramPacket?in_packet?=new?DatagramPacket(in_buf,2000); server.recieve(in_packet);
客戶端代碼 DatagramSocket?client=?new?DatagramSocket(1235); DatagramPacket?out_packet=? ????????????new?DatagramPacket?(out_buf,100,"192.168.1.1",1234); client.send(outPacket);
?? 22、String對象 一般講來創建的兩個對象如果用==來比較肯定是不等的,因為他們的引用地址是不同的,而==是對于對象來講是比較對象地址的。但對于String對象來講是一個例外,兩個String對象如果值相同,==比較也是相同的。我想這可能與Sun公司對String對象定義有關。
public?class?tt{? public?static?void?main?(String?args[]){? ??tt?t?=?new?tt();? ??t.test(2);? }? void?test(int?i){ ??String?s1?=?"123"; ??String?s2?=?"123"; ??if?(s1==s2)? ????System.out.println("111111"); ??else ????System.out.println("2222222"); }? } 結果輸出:111111 |
|
Java語言接口與繼承的本質
|
loveofgod
轉貼? (參與分:2027,專家分:345)?? 發表:2006-07-20 02:25 ??版本:1.0 ??閱讀:12次 |
|
計算機學院研二的兄弟與我討論Java,一見面,幾個問題全是關于接口,接口有什么用?為什么要用接口?什么時候該使用接口?很慶幸他們不是問我Java如何連接SQL?Server,或者是如何開發J2EE應用,這類問題有殺傷力,避之則吉。今年計算機學院本科有個畢業設計課題是做J2ME,選這個題目的學生在5月末都還在苦著臉研究java.util.*這個包,這個這個……唉。?
大多數人認為,接口的意義在于頂替多重繼承。眾所周知Java沒有c++那樣多重繼承的機制,但是卻能夠實作多個接口。其實這樣做是很牽強的,接口和繼承是完全不同的東西,接口沒有能力代替多重繼承,也沒有這個義務。接口的作用,一言以蔽之,就是標志類的類別(type?of?class)。把不同類型的類歸于不同的接口,可以更好的管理他們。OO的精髓,我以為,是對對象的抽象,最能體現這一點的就是接口。為什么我們討論設計模式都只針對具備了抽象能力的語言(比如c++、java、c#等),就是因為設計模式所研究的,實際上就是如何合理的去抽象。(cowboy的名言是“抽象就是抽去像的部分”,看似調侃,實乃至理)。?
設計模式中最基礎的是工廠模式(Factory),在我最近的一個很簡單的應用中,我想盡量的讓我的程序能夠在多個數據庫間移植,當然,這涉及很多問題,單是如何兼容不同DBMS的SQL就讓人頭痛。我們不妨先把問題簡單化,只考慮如何連接不同的數據庫。?
假設我有很多個類,分別是Mysql.java、SQLServer.java、Oracle.java、DB2.java,他們分別連接不同的數據庫,統一返回一個Connection對象,并且都有一個close方法,用于關閉連接。只需要針對你的DBMS,選擇不同的類,就可以用了,但是我的用戶他會使用什么數據庫?我不知道,我希望的是盡量少的修改代碼,就能滿足他的需要。我可以抽象如下接口:?
package?org.bromon.test;? public?interface?DB? {? java.sql.Connection?openDB(String?url,String?user,String?password);? void?close();? }?
這個接口只定義兩個方法,沒有任何有實際意義的代碼,具體的代碼由實作這個接口的類來給出,比如Mysql.java:?
Package?org.bromon.test;? import?java.sql.*;? public?class?Mysql?implements?DB? {? private?String?url=”jdbc:mysql:localhost:3306/test”;? private?String?user=”root”;? private?String?password=””;? private?Connection?conn;? public?Connection?openDB(url,user,password)? {? //連接數據庫的代碼? }?
public?void?close()? {? //關閉數據庫? }? }?
類似的當然還有Oracle.java等等,接口DB給這些類歸了個類,在應用程序中我們這樣定義對象:?
org.bromon.test.DB?myDB;?
使用myDB來操作數據庫,就可以不用管實際上我所使用的是哪個類,這就是所謂的“開-閉”原則。但是問題在于接口是不能實例化的,myDB=new?DB(),這樣的代碼是絕對錯誤的,我們只能myDB=new?Mysql()或者myDB=new?Oracle()。麻煩了,我還是需要指定具體實例化的是哪個類,用了接口跟沒用一樣。所以我們需要一個工廠:?
package?org.bromon.test;? public?class?DBFactory? {? public?static?DB?Connection?getConn()? {? Return(new?Mysql());? }? }?
所以實例化的代碼變成:myDB=DBFactory.getConn();?
這就是23種模式中最基礎的普通工廠(Factory),工廠類負責具體實例化哪個類,而其他的程序邏輯都是針對DB這個接口進行操作,這就是“針對接口編程”。責任都被推卸給工廠類了,當然你也可以繼續定義工廠接口,繼續把責任上拋,這就演變成抽象工廠(Abstract?Factory)。?
整個過程中接口不負責任何具體操作,其他的程序要連接數據庫的話,只需要構造一個DB對象就OK,而不管工廠類如何變化。這就是接口的意義----抽象。?
繼承的概念不用多說,很好理解。為什么要繼承呢?因為你想重用代碼?這絕對不是理由,繼承的意義也在于抽象,而不是代碼重用。如果對象A有一個run()方法,對象B也想有這個方法,所以有人就Class?B?extends?A。這是不經大腦的做法。如果在B中實例化一個A,調用A的Run()方法,是不是可以達到同樣的目的?如下:?
Class?B? {? A?a=new?A();? a.run();? }?
這就是利用類的聚合來重用代碼,是委派模式的雛形,是GoF一貫倡導的做法。?
那么繼承的意義何在?其實這是歷史原因造成的,最開始的OO語言只有繼承,沒有接口,所以只能以繼承來實現抽象,請一定注意,繼承的本意在于抽象,而非代碼重用(雖然繼承也有這個作用),這是很多Java爛書最嚴重的錯誤之一,它們所造成的陰影,我至今還沒有完全擺脫,壞書害人啊,尤其是入門類的,流毒太大。什么時候應該使用繼承?只在抽象類中使用,其他情況下盡量不使用。抽象類也是不能實例化的,它僅僅提供一個模版而已,這就很能說明問題。?
軟件開發的萬惡之源,一是重復代碼而不是重用代碼,二是爛用繼承,尤以c++程序員為甚。Java中取締多重繼承,目的就是制止爛用繼承,實是非常明智的做法,不過很多人都不理解。Java能夠更好的體現設計,這是讓我入迷的原因之一。
|
|
XML和J2EE的完美結合
|
loveofgod
轉貼? (參與分:2027,專家分:345)?? 發表:2006-07-19 17:15 ??版本:1.0 ??閱讀:15次 |
|
當前,Java?2平臺企業版(J2EE)架構在廠商市場和開發者社區中倍受推崇。作為一種工具,可擴展標記語言(XML)簡化了數據交換、進程間消息交換這一類的事情,因而對開發者逐漸變得有吸引力,并開始流行起來。自然,在J2EE架構中訪問或集成XML解決方案的想法也很誘人。因為這將是強大系統架構同高度靈活的數據管理方案的結合。?
XML的應用似乎是無窮無盡的,但它們大致上可以分為三大類:?
●簡單數據的表示和交換(針對XML的簡單API(SAX)和文檔對象模型(DOM)語法解析,不同的文檔類型定義(DTDs)和概要(schemas))?
●面向消息的計算(XML-RPC(遠程過程調用),SOAP協議,電子化業務XML(ebXML))? ●用戶界面相關、表示相關的上下文(可擴展樣式表語言(XSL),可擴展樣式表語言轉換(XSLT))? 這幾類應用在J2EE架構中恰好有天然的對應:數據表示和交換功能是EJB組件模型中持久化服務(persistence?services)的一部分,基于消息的通訊由Java消息服務(JMS)API來處理,而界面表示正是Java服務器頁面(JSP)和Java?Servlets的拿手好戲。? 在本文中,我們將看到當今基于J2EE的應用里,XML是如何在上述幾個方面進行應用的,以及在相關標準的未來版本中這些應用將會如何發展。? 基礎:數據的表示和交換? 原型化的XML應用(假設有的話)的內容通常是:數據以XML格式存放,為了進行顯示、修改甚至寫入某個XML文檔而經常被讀入到某個對象模型中。作為例子,假定我們正處理多種類型的媒體(圖品、視頻、文本文檔等等),并且用下面這個簡單的XML?DTD來描述這些媒體的元數據:? <!--?DTD?for?a?hypothetical?media?management?system?-->? <!--?Media?assets?are?the?root?of?the?object?hierarchy.?Assets?are?also? hierarchical?-?they?can?contain?other?assets.?-->? <!ELEMENT?media-asset?(name,?desc?,?type*,?media-asset*,?urn)>? <!--?Metadata?about?the?asset?-->? <!ELEMENT?name?(#PCDATA)>? <!ELEMENT?desc?(#PCDATA)>? <!ELEMENT?type?(desc,?mime-type?)>? <!ELEMENT?mime-type?(#PCDATA)>? <!ELEMENT?urn?(#PCDATA)>? ??? 以下是一個基于上述媒體DTD的XML文檔,描述了與某個課程講座相關的內容:? <?xml?version="1.0"??><!DOCTYPE?media-asset?PUBLIC?"-//Jim?Farley//DTD??? Media?Assets//EN"?"http://localhost/Articles/Sun/dtds/media.dtd">? <media-asset>? <name>第14講</name>? <desc>與第14講相關的所有內容</desc>? <!--?內容對象"lecture?14"的一套子組件?-->??? <media-asset>? <name>講座的幻燈片</name>? <type>? <desc>MS?PowerPoint</desc>? <mime-type>application/vnd.ms-powerpoint</mime-type>? </type>? <urn>http://javatraining.org/jaf/E123/lecture-? 14/slides.ppt</urn>? </media-asset>? <media-asset>? <name>講座的視頻片斷</name>? <type>? <desc>RealPlayer?streaming?video</desc>? <mime-type>video/vnd.rn-realvideo</mime-type>? </type>? <urn>http://javatraining.org/jaf/E123/lecture-? 14/lecture.rv</urn>? </media-asset>? <!--?講座開始?-->? <urn>http://javatraining.org/jaf/E123/lecture-14/index.jsp</urn>? </media-asset>??? 從Web或者企業級應用的角度看,能以這種方式訪問數據真是一種福音,因為它體現了高度的可移動性,使我們與元數據的實際資源本身隔離。這些資源可能來自一個關系數據庫系統、某種活動媒體服務器或者Web服務器上的一個靜態XML文檔,等等。如果想把這些數據加載到Java應用中,我們可以從當前眾多的Java語言XML解析器中選用一個,通過它將XML數據裝入一個DOM文檔,最后遍歷文檔,將所有這些數據轉換到我們應用系統的對象模型中。? 下面是個簡單的基于DOM的解析程序,可對上述的媒體DTD進行解析。解析器用的是? Apache?Xerces:? ??? package?jaf.xml;? import?java.util.*;? import?java.io.IOException;? import?org.w3c.dom.*;? import?org.xml.sax.*;? ??? //?XML文檔解析程序,使用上述媒體DTD.? public?class?MediaParser?implements?ErrorHandler?{? /**?使用Apache?Xerces解析器?*/? org.apache.xerces.parsers.DOMParser?mParser?=??? new?org.apache.xerces.parsers.DOMParser();? /**?構造函數?*/? public?MediaParser()?{? //?告訴解析器驗證并解析文檔? try?{? mParser.setFeature(?"http://xml.org/sax/features/validation",??? true);? }??? catch?(SAXException?e)?{? System.out.println("Error?setting?validation?on?parser:");? e.printStackTrace();? }? //?設置解析器的錯誤處理句柄? mParser.setErrorHandler(this);? }? /**?解析指定的URL,返回找到的XML文檔? */? public?Document?parse(String?url)?throws?SAXException,?IOException?{? mParser.parse(url);? Document?mediaDoc?=?mParser.getDocument();? return?mediaDoc;? }? /**?解析指定URL的XML文檔,將內容轉換成?MediaAsset?對象? */? public?Collection?loadAssets(String?url)?throws?SAXException,??? IOException?{? Document?doc?=?parse(url);? Collection?assets?=?new?LinkedList();? NodeList?assetNodes?=?doc.getElementsByTagName("media-asset");? for?(int?i?=?0;?i?<?assetNodes.getLength();?i++)?{? Node?assetNode?=?assetNodes.item(i);? MediaAsset?asset?=?new?MediaAsset(assetNode);? assets.add(asset);? }? return?assets;? }? /**? *?錯誤處理代碼(為簡潔起見省略了)? */? }? MediaParser類的構造函數初始化了一個Xerces?DOM解析器。parse()方法告訴解析器到哪個URL去找XML源,然后得到結果文檔并返回。loadAssets()方法調用parse()方法從某個XML源加載文檔,然后為文檔中找到的每個“media-asset”節點創建一個MediaAsset對象。? 以下是一個使用MediaAsset類的例子:? package?jaf.xml;? import?java.util.*;? public?class?MediaAsset?{? //?資源元數據? private?String?mName?=?"";? private?String?mDesc?=?"";? private?Collection?mChildren?=?new?LinkedList();? private?Vector?mTypes?=?new?Vector();? private?String?mUrn?=?"";? protected?MediaAsset(org.w3c.dom.Node?assetNode)?{? //?為簡潔起見省略后面代碼? .? .? .? }? }? 因為篇幅的關系省略了MediaAsset類的詳細代碼,但應用模式依然是清晰的。MediaAsset類遍歷文檔的節點,當它碰到不同的子節點時,它用子節點的內容填充自己的成員數據。如果它發現了一個嵌套的子資源節點,它只需要創建一個新的MediaAsset對象,然后將子資源節點的數據填充到新對象的成員數據中。? 實現上述處理的方法數不勝數。我們還可以使用其他的解析器或解析器架構,如Java?API?for?XML?Parsing?(JAXP)。除了使用DOM模型外,事件驅動的SAX模型也可用于解析XML。類似的程序也可用來產生XML數據??前提是允許產生新的數據對象(在本例中是MediaAsset),它可將其相應的XML實體插入到DOM中,然后將DOM輸出到一個流中(諸如一個文件,一個Socket,或者一個HTTP連接...)。還有其他更高層次的標準,可將XML映射到Java對象的過程進一步自動化(或簡化)。例如,使用XML概要(Schema)和XML綁定處理引擎,您可以半自動地將滿足某個XML?概要的XML數據轉變成Java數據對象。代表性的引擎是Castor,是由ExoLab小組管理的一個開放源代碼項目的產物。上述使用Xerces?DOM的簡單例子僅僅是演示了這一處理過程的底層模型。? 上述示例表明,在Java環境中解析或產生XML是非常方便的,這與J2EE沒有必然關聯。格式化為XML的數據可以從應用程序的任何層次流入或輸出,這使得與外部系統的集成性無可限量。但我們能否以一種更為直接的方式將XML數據源集成到J2EE架構中去呢??
駕馭消息?
J2EE架構包含了對JMS(Java消息服務)API的訪問,以實現面向消息的通信(J2EE?1.2.1版只需JMS?API即可,在J2EE?1.3版中JMS基本定型,此時必須由某個兼容J2EE平臺的服務器提供一個JMS?API?Provider)。這一類的異步交互(與之相對的是:本地或遠程方法調用所代表的同步交互)被證明在某些應用環境中是非常有用的。某些時候,交互只需要通過間接的請求或回答來實現,即:在某些情況下,發出消息后不可能立即收到答復,但我們仍希望當消息發出者重新在線時,確保他能收到答復信息。? 面向消息系統的實際應用之一就是企業之間的松散集成。類似于EDI(電子文檔交換)時代的文檔交換,兩個企業由于業務的需要而交換消息,此時通常不能為了使用RPC或者RMI、CORBA、DCOM之類的遠程方法交互而在兩者之間進行緊密集成。象JMS?API這樣的消息系統允許雙方交換基于JMS?API的消息載荷,前提是雙方在會話的時候均能提供兼容的JMS?API服務。當前仍然存在的困難是:雙方是否能尊從相同的格式或協議。? 這正是XML大顯身手的時候。XML明確地被設計來解決此類數據交換問題??靈丹妙藥就是“面向消息的概要表”(Message-Oriented?Communication?Scheme),實質就是基于一個雙方認同的DTD或schema,用XML格式來交換消息載荷。? JMS?API支持好幾種消息,其中的TextMessage代表文本消息載荷。一個簡單而有效的XML消息交換方案是,在一端將我們的XML文檔插入TextMessage,然后在另一端用自制的XML解析程序(如前面的MediaParser)解開數據并(可選地)將其轉換成Java對象。這使得我們既可以用JMS?API支持的公開預訂的消息模型,也可以用JMS?API支持的點對點的消息模型來發送XML消息。? 上述方法有一些局限,因為對于JMS運行時處理而言,XML的內容基本上是不透明的。例如,JMS?API允許使用基于特定消息頭的路由。這很容易理解,尤其當我們希望XML消息根據其內容采取不同走向時。例如在我們的MediaAsset例子中,我們希望公開講座內容,但只想把特定的內容傳送給那些預訂了課程的人,或傳送給那些表明可以接受某些媒體格式(如視頻流)的人。為了發揮JMS?API的價值,以便實現上述基于內容的消息路由,我們有必要從XML數據中解析出關鍵信息,然后在構造標準JMS?API消息頭時插入這些信息。這是可行的,但要實現XML信息我們就得額外地寫很多代碼(交換消息的雙方均如此)。? 為了在XML和JMS?API之間架起橋梁,一些廠商提供了自定義的JMS擴展,以便直接支持XML消息機制。例如,BEA系統公司基于J2EE的WebLogic應用服務器特別為TextMessage提供了XMLMessage子類,允許用XPath表達式來過濾XML消息。不過這是一種專有的擴展,這要求交換消息的雙方必須都能處理這類消息。? 為此,Sun公司目前正在開發用于XML消息的Java?API(JAXM)。其目標是提供一個高級別的標準服務,以實現基于ebXML的消息的合成與傳送。一個JAXM服務提供程序可以將這類消息映射到適當的物理消息系統(諸如JMS?API)中去。? 讓XML看得見? 將XML同Web系統的用戶界面進行集成顯然是一種有益的嘗試。絕大多數的界面程序,無論是基于還是不基于Web,都是將數據進行轉換,然后用易讀的格式展現給用戶。用諸如XML這種“易消化”的格式存放數據將簡化上述工作,同時它還大大提高了內容的可管理性,接下來我們就可看到這一點。不過首先要大書一筆的是,XML在Web界面層的應用得益于JSP技術的發展。? 一直以來大家都希望能清晰地區分Web應用程序的表示層與底層對象模型,JSP框架誕生于這些努力之中(包括早期JHTML嘗試)。JSP框架允許將Java代碼嵌入到HTML內容中,這樣既可以實現動態內容,又不必經常修改Java?Servlets的代碼。在頁面中包含Java技術的途徑是通過JSP標記(JSP?Tags),這些標記以XML風格出現。在JSP中,Java程序以代碼片段、服務器端JavaBeans組件、在服務器端觸發特定操作的不透明標記(標準的或自定義的)等形式存在。當某個用戶通過瀏覽器請求JSP頁面時,一個Java應用服務器解析該JSP頁面,將其編譯成一個Java?Servlet,然后執行該Servlet以產生答復頁面。? 一種直接將XML數據源集成到JSP的界面中去的方法是,將XML加載到JavaBeans組件中(如同我們在MediaAsset例子中所做的),然后在JSP中直接引用這些JavaBeans組件。? 下面是一個嵌入Java代碼片斷的例子:? <html>? <head>? <title>第14講的媒體資源</title>? </head>? <body>? <!--?引入我們的類?-->? <%@?page?import="jaf.xml.*"?%>? <center><H3>Media?Assets?for?Lecture?14:</H3></center>? <!--?定義一個資源對象,以便用于顯示?-->? <jsp:useBean?class="jaf.xml.MediaAsset"?id="asset"?/>? <!--?從一個先前定義的位置裝載資源?-->? <%?MediaParser?parser?=?new?MediaParser();? Collection?assets?=?parser.loadAssets("http://javaschool.org? /jaf/E162/lecture14-assets.xml");? Iterator?iter?=?assets.iterator();? %>? <table?border=0>? <tr><th>Name</th><th>Type</th><th>URN</th></tr>? <%? while?(iter.hasNext())?{? asset?=?(MediaAsset)iter.next();? %>? <tr><td><jsp:getProperty?name="asset"?property="name"?/></td>? <td><jsp:getProperty?name="asset"?property="type"?/></td>? <td><jsp:getProperty?name="asset"?property="URN"?/></td>? </tr>? <%? }? %>? </table>? </body>? </html>? 其中粗體部分為JSP代碼片斷和標記,其余部分是標準的HTML文本。? 上述程序還有一種更簡潔的寫法,那就是使用自定義JSP頁面標記。這樣我們就可以從JSP頁面中剔出代碼段,只使用JavaBeans組件和自定義的JSP標記即可。比如說,為了去掉創建解析器、加載資源數據到集合中的那段代碼,我們可創建一個自己的標記,由它在幕后完成這些工作。以下是例子:? .? .? .? <!--?引入我們的類?-->? <%@?page?import="jaf.xml.*"?%>? <center><H3>Media?Assets?for?Lecture?14:</H3></center>? <!--?加載我們自定義的標記庫?-->? <%@?taglib?uri="http://javaschool.org/taglib"?prefix="media"?%>? <!--?從一個先前定義的位置裝載資源?-->? <media:load?url="http://javaschool.org/jaf/E162/lecture14-assets.xml"? collectionName="assets"?cursorName="asset"?/>? <table?border=0>? .? .? .? 使用自定義標記的最大好處是使我們的程序代碼集中在一個地方(對Java技術而言,一般是指在“類”中),易于管理。這樣可以將程序中對象層同界面層的集成關系定義得很清晰,修改代碼所造成的影響是可以預測和管理的。? 直接將XML數據轉換成Web顯示內容的另一種方法是使用XSL和XSLT。在這種方案中,將XML數據映射成HTML(或WML等)的邏輯由XSL樣式表(XSL?StyleSheet)來定義。樣式表描述了每個特定XML數據實體應該怎樣轉換成界面數據實體(如HTML表格、內聯標記等)。在JSP架構中,XSL轉換只能應用于特定的XML數據源,最理想的是采用一套自定義的JSP標記并引用某個XSLT處理程序。這方面的典型示例請參考java.sun.com中關于XML同JSP構架集成的白皮書。? 同前面那個JSP自定義標記加XML解析器組件的方案相比,XSLT方案的伸縮性要好一些,而且具有更好的可管理性。在這種情形下,我們的轉換邏輯是編寫在一個XSL樣式表中,而不是在Java代碼中。這意味著當需要修改界面時,大多數情況下只是編輯樣式表或者HTML,代碼不受影響。不過在決定選用何種方案之前,還是要根據實際狀況仔細權衡。如果選用XSLT方案,那么就得有人負責維護這些XSL樣式表(要么是負責界面的人,要么是編寫程序的人)。XSLT既像內容,又像程序,因此雙方都不能把責任推給對方,結果大家可能都被這不倫不類的XSLT弄得矛盾百出。從這點上考慮,采用自定義標記并由界面開發者將其嵌入表示層的方法似乎更有吸引力,因為這樣軟件工程師只考慮Java代碼,而內容工程師也只操心內容標記。? Java?servlet過濾器是J2EE?1.3版在其Web層最新發布的一種Web組件。當Sevelet將請求寫入某個資源或者從某個資源中讀取回答信息時,過濾器可以非常方便地轉換其中的頭信息和內容信息。這里所說的資源可以是一個Java?servlet、一個JSP頁面,甚至一個靜態Web頁。過濾器的確很“酷”,因為它允許開發人員從轉換內容的代碼中分離出生成內容的那部分代碼,并加以重用。當需要通過XSLT方式將XML數據轉換到不同的XML應用目標時,Java?servlet過濾器尤其有用。? 在J2EE應用程序中使用Java?servlet過濾器轉換其輸出,以便兼容任何類型客戶端的前景呼之欲出。servlet過濾器能夠偵測到來自使用WAP協議(無線應用協議)的移動客戶端的呼叫,并且將答復內容轉換成WML(無線標記語言)格式。servlet過濾器也能檢測到來自iMode無線客戶的呼叫,并將其轉變成cHTML(緊湊HTML)格式。當然,servlet過濾器也能夠分辨出傳統的HTML瀏覽器客戶的請求,并用正確的格式進行回復。? 結束語? 在J2EE?1.2.1規范中,XML“集成”僅指組件或應用程序的XML格式的部署描述。在J2EE?1.3規范中,對XML的支持被擴展為要求具備SAX?2和DOM?2解析器,以及在兼容J2EE的服務器平臺上提供XSLT轉換處理程序。您可以毋庸置疑地相信,將來在J2EE架構中還會集成進更多的XML特性,因為J2EE規范的定義者們會認真傾聽開發者社區中對在企業級應用中使用更多XML的渴求呼聲。例如,JSR(Java定義請求)處理小組中與JAXM規范相關的部分(JSR?000067)承諾在J2EE后續規范中集成進JAXM。可以預見,在JSP架構、EJB和JDBC規范中均會有類似的變化。J2EE平臺中上述組件的變革,將使Java技術開發者目前用的XML更為規范化(以及標準化),發揮出更大的威力。
|
|
|
|
|
|
posted on 2006-07-20 12:07
MEYE 閱讀(671)
評論(0) 編輯 收藏 所屬分類:
JAVA