Brett McLaughlin, 作家/編輯, O‘Reilly Media, Inc.
2004 年 9 月 01 日
在
上一篇文章中,Brett 幫助您對 JaxMe API 有了深入的了解。在這一基礎(chǔ)上,本文將說明如何將 XML 文檔轉(zhuǎn)化成 Java
類實(shí)例、操縱底層的 XML 數(shù)據(jù)然后再把修改后的數(shù)據(jù)轉(zhuǎn)換成 XML。本文將為您提供翔實(shí)的 JaxMe
應(yīng)用知識,以便在您的應(yīng)用程序編寫中加以運(yùn)用。
首先要指出我希望您已經(jīng)讀過本系列文章的
上一篇,事實(shí)上我將沿用那篇文章中的例子,如果您沒有按照順序閱讀可能會有點(diǎn)手足無措。
迭代的過程
本文將指出數(shù)據(jù)綁定的迭代特性,這也是您需要真正注意的一點(diǎn)。很多 API,特別是那些屬于
工具類
的 API,都只需要放入類路徑然后直接使用即可,Jakarta Commons 類就是一個很好的例子。就是說只要放到 Java
工具組中就隨時都可以使用。但事實(shí)上,數(shù)據(jù)綁定 API
的工作方式有點(diǎn)不同,人們很少會到處使用數(shù)據(jù)綁定中的方法,而是在應(yīng)用程序的某一部分集中使用數(shù)據(jù)綁定。
為了強(qiáng)調(diào)這一點(diǎn),這些文章就是按照人們編寫代碼的方式寫成的。上一篇文章中我給出了一個簡單的
XML
模式,并假設(shè)有兩個實(shí)體以此作為相互通信的標(biāo)準(zhǔn)。這兩個實(shí)體可以是公司、同一公司內(nèi)的不同部門,也可以是兩個應(yīng)用程序組件。無論哪種情況,都使用
JaxMe 從該模式生成類,這些類然后大概被交給 Java 開發(fā)人員。通過 XML 模式的這種 Java 表示,就可以將符合那種模式的 XML
文檔轉(zhuǎn)化到 Java 類,或者相反。
 |
再重復(fù)一次
我相信有些讀者一直堅(jiān)持閱讀本專欄,對于數(shù)據(jù)綁定是什么的議論聽到過不下十次。但是,每個月都有不少新手寫信告訴我,他們正在努力弄明白這些東西。請?jiān)徫业牧_嗦吧,也許您旁邊的那個人正在學(xué)習(xí)呢,多重復(fù)一遍說不定能讓您的日子好過一點(diǎn)!
|
|
趕上進(jìn)度
首先我們來回顧上一篇文章中用于生成類的模式,如清單 1 所示。
清單 1. 用于學(xué)生的 XML Schema
<?xml version="1.0" encoding="UTF-8"?>
<schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
targetNamespace="http://dw.ibm.com/jaxme/student"
xml:lang="EN"
xmlns:stu="http://dw.ibm.com/jaxme/student"
xmlns="http://www./2001/XMLSchema"
>
<element name="students">
<complexType>
<sequence>
<element name="student" maxOccurs="unbounded"
type="stu:Student" />
<element name="college" minOccurs="0"
maxOccurs="unbounded" type="stu:College" />
</sequence>
</complexType>
</element>
<complexType name="Student">
<sequence>
<element name="firstName" type="string" />
<element name="lastName" type="string" />
<element name="collegeId" type="string" />
<element maxOccurs="unbounded" name="address"
type="stu:Address" />
</sequence>
</complexType>
<complexType name="Address">
<sequence>
<element name="street" type="string" />
<element name="city" type="string" />
<element name="state" type="string" />
<element name="zip" type="positiveInteger" />
</sequence>
<attribute name="type" type="string" use="required" />
</complexType>
<complexType name="College">
<sequence>
<element name="name" type="string" />
<element name="address" type="stu:Address" />
</sequence>
<attribute name="id" type="string" use="required" />
</complexType>
</schema>
|
有了這個模式之后就可以處理它的實(shí)例文檔,如清單 2 所示。
清單 2. 基本的學(xué)生列表
<?xml version="1.0" encoding="UTF-8"?>
<students
xmlns="http://dw.ibm.com/jaxme/student"
xmlns:xsi="http://www./2001/XMLSchema-instance"
xsi:schemaLocation="http://dw.ibm.com/jaxme/student student.xsd"
>
<student>
<firstName>Brett</firstName>
<lastName>McLaughlin</lastName>
<collegeId>LBU</collegeId>
<address type="home">
<street>1029 Burlingham</street>
<city>Waco</city>
<state>TX</state>
<zip>87610</zip>
</address>
</student>
<student>
<firstName>Gary</firstName>
<lastName>Greathouse</lastName>
<collegeId>LBU</collegeId>
<address type="home">
<street>9098 Townhall Drive</street>
<city>Waco</city>
<state>TX</state>
<zip>87621</zip>
</address>
</student>
<college id="LBU">
<name>Louisiana Baptist University</name>
<address type="home">
<street>6301 Westport Avenue</street>
<city>Shreveport</city>
<state>LA</state>
<zip>71129</zip>
</address>
</college>
</students>
|
我分別把這兩個文件命名為
students.xsd和
student1.xml。本文中將讀取 student1.xml,打印其中的一些信息,增加和改變一些信息,然后將修改的數(shù)據(jù)序列化為一個新的文件
student2.xml。任務(wù)非常簡單,但是涉及到了使用 JaxMe 進(jìn)行基本的數(shù)據(jù)綁定所需要了解的大部分知識。
把 XML 轉(zhuǎn)化為 Java 代碼
第一步是把這個 XML 文件轉(zhuǎn)化為 Java 表示。
上一篇 文章的
com.ibm.dw.jaxme.student 包提供了我們需要的類。現(xiàn)在要做的就是讀入 XML 文件,告訴 JaxMe 用什么類表示文件中的對象,讓數(shù)據(jù)綁定 API 完成它們的工作。清單 3 是一個完成這項(xiàng)工作的例子,先看一遍,后面有詳細(xì)的說明。
清單 3. 讀取并打印 student1.xml
package com.ibm.dw.jaxme.example;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
// JAXB classes
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
// SAX classes
import org.xml.sax.InputSource;
// Generated classes
import com.ibm.dw.jaxme.student.*;
public class JaxMeTester {
/** Input XML File */
private File inputFile;
public JaxMeTester(String inputFilename) {
this.inputFile = new File(inputFilename);
}
public Students readXML() throws IOException, JAXBException {
// Get a handle to the input file
InputSource source =
new InputSource(new FileInputStream(inputFile));
source.setSystemId(inputFile.toURL().toString());
// Parse
JAXBContext ctx = JAXBContext.newInstance(
"com.ibm.dw.jaxme.student");
Unmarshaller u = ctx.createUnmarshaller();
return (Students)u.unmarshal(source);
}
public void printStudents(Students students, PrintStream out) throws IOException {
// Get a map of college IDs and names
Map colleges = new HashMap();
List list = students.getCollege();
for (Iterator i = list.iterator(); i.hasNext(); ) {
College college = (College)i.next();
colleges.put(college.getId(), college.getName());
}
out.print("\n\n--- Student Listings ---\n\n");
list = students.getStudent();
for (Iterator i = list.iterator(); i.hasNext(); ) {
Student student = (Student)i.next();
out.println("Name: " + student.getFirstName() + " " + student.getLastName());
List addresses = student.getAddress();
for (Iterator j = addresses.iterator(); j.hasNext(); ) {
Address address = (Address)j.next();
printAddress(address, out);
}
out.println("College: " + colleges.get(student.getCollegeId()));
out.println();
}
list = students.getCollege();
for (Iterator i = list.iterator(); i.hasNext(); ) {
College college = (College)i.next();
out.println("Name: " + college.getName());
out.println("Address: ");
printAddress(college.getAddress(), out);
out.println();
}
}
private void printAddress(Address address, PrintStream out) throws IOException {
out.print(" " + address.getStreet() + "\n");
out.print(" " + address.getCity() + ", " + address.getState() + " " +
address.getZip() + "\n");
}
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Incorrect arguments supplied!");
System.err.println("Usage: java com.ibm.dw.jaxme.example.JaxMeTester " +
"[input XML filename]");
return;
}
try {
JaxMeTester tester = new JaxMeTester(args[0]);
Students students = tester.readXML();
tester.printStudents(students, System.out);
} catch (Exception e) {
System.err.println("Error occurred: " + e.getMessage());
e.printStackTrace(System.err);
}
}
}
|
設(shè)置輸入文件
首先要將 XML 輸入文件變?yōu)?JaxMe(以及底層的 SAX 解析器)能夠使用的形式。顯然應(yīng)該選擇 SAX 的
InputSource 類,這是文件、流以及您能夠想到的任何東西的統(tǒng)一輸入格式。清單 4 中的內(nèi)容是從上例中摘出來的,可以看到
JaxMeTester 所接受的
String 文件名被轉(zhuǎn)化為
InputSource (包括幾個中間步驟)。
清單 4. 將輸入文件轉(zhuǎn)化為 InputSource
/** Input XML File */
private File inputFile;
public JaxMeTester(String inputFilename) {
this.inputFile = new File(inputFilename);
}
public Students readXML() throws IOException, JAXBException {
// Get a handle to the input file
InputSource source =
new InputSource(new FileInputStream(inputFile));
source.setSystemId(inputFile.toURL().toString());
// Parse
}
|
如果您恰好熟悉 SAX,沒有什么特別值得注意的地方。惟一需要指出的是
File 的使用,這里沒有直接傳遞
String 。雖然可以采用后一種方法,但是這樣做就沒有 Java 語言
File 類所提供的保護(hù)了。事實(shí)上,
InputSource 在構(gòu)造函數(shù)中做的第一件事就是將
String 轉(zhuǎn)化為
File ,這正是我們要做的。此外,這種方法很容易設(shè)置輸入文件的系統(tǒng) ID,如果直接使用
String 而不是對象就麻煩得多了。
設(shè)置 JaxMe
接下來要設(shè)置 JaxMe 解組,只需要一行代碼,如清單 5 所示。這里要告訴 JAXB 上下文(要記住 JaxMe 是 JAXB 的一種實(shí)現(xiàn),因此常常使用這種語義)到哪里尋找
jaxb.properties文件。我總是將其放在與相關(guān)類相同的目錄中,就是說只要使用生成類的包名就可以了。
清單 5. 告訴 JaxMe 到哪里尋找屬性文件
JAXBContext ctx = JAXBContext.newInstance(
"com.ibm.dw.jaxme.student");
|
該文件本身只有一行,如清單 6 所示,它告訴 JAXB 要加載哪一種數(shù)據(jù)綁定上下文實(shí)現(xiàn)。
清單 6. jaxb.properties 文件
javax.xml.bind.context.factory=org.apache.ws.jaxme.impl.JAXBContextImpl
|
這里值得一提的是代碼中
沒有JaxMe 專用的類。雖然必須將 JaxMe 放在類路徑中,但 JAXB 從這個屬性文件中獲得所有 JaxMe 專用的信息。換句話說,不用改變代碼就可以從 JAXB 的參考實(shí)現(xiàn)切換為 JaxMe(強(qiáng)烈建議使用)。
解組
建立了 JAXB 上下文之后,將 XML 文件轉(zhuǎn)化為 Java 表示很容易,如清單 7 所示,這些細(xì)節(jié)沒有吸引人的地方。
清單 7. 解組 XML
public Students readXML() throws IOException, JAXBException {
// Get a handle to the input file
InputSource source =
new InputSource(new FileInputStream(inputFile));
source.setSystemId(inputFile.toURL().toString());
// Parse
JAXBContext ctx = JAXBContext.newInstance(
"com.ibm.dw.jaxme.student");
Unmarshaller u = ctx.createUnmarshaller();
return (Students)u.unmarshal(source);
}
|
當(dāng)然,這段代碼非常令人厭煩,但正因如此也就顯得很棒;這僅僅是
勞動,就您而言不用花費(fèi)多少心思。
處理 XML
一旦獲得
Students 對象,也就完成了這個練習(xí)中的數(shù)據(jù)綁定部分。
printStudents() 方法可以說明這一點(diǎn),因?yàn)樗静恢?JAXB 或者 JaxMe 的存在。事實(shí)上也可以在不同的類中(甚至非 Java 語言模塊中),沒有任何問題。這里不再重復(fù)列出代碼,只不過是 Java 對象的一些打印調(diào)用。
程序的輸出
您可以運(yùn)行
JaxMeTester 并提供前面的 XML 輸入文件來測試這些代碼。該程序?qū)垓v上一秒鐘,然后輸出與清單 8 類似的結(jié)果。這是最漂亮的打印工作,但是應(yīng)該讓您明白使用 JaxMe 讀 XML 文件是多么簡單。
清單 8. 程序?qū)?student1.xml 的輸出結(jié)果
test:
[java] --- Student Listings ---
[java] Name: Brett McLaughlin
[java] 1029 Burlingham
[java] Waco, TX 87610
[java] College: Louisiana Baptist University
[java] Name: Gary Greathouse
[java] 9098 Townhall Drive
[java] Waco, TX 87621
[java] College: Louisiana Baptist University
[java] Name: Louisiana Baptist University
[java] Address:
[java] 6301 Westport Avenue
[java] Shreveport, LA 71129
BUILD SUCCESSFUL
Total time: 3 seconds
|
 |
轉(zhuǎn)向 Ant
與以前的文章一樣,我使用 Ant 完成編譯、運(yùn)行和其他大部分工作。上一篇文章中已經(jīng)詳細(xì)介紹了 Ant 的用法,因此這里只需要引入構(gòu)建文件(
build.xml)就可以了。默認(rèn)的目標(biāo)包括生成類、編譯生成的示例類并運(yùn)行該例子,因此您只需要修改幾個路徑并輸入
ant 就可以了。
|
|
使用數(shù)據(jù)
您可能已經(jīng)猜到,一旦轉(zhuǎn)化成 Java 形式這些數(shù)據(jù)的使用就非常簡單了。而且這些操作同樣與 JaxMe 毫無關(guān)系。因此我只給出一些代碼,這些代碼增加一所新的學(xué)院并改變一直處理的學(xué)校,代碼的功能您可以自己分析,新增的方法如清單 9 所示。
清單 9. 在內(nèi)存中修改學(xué)生信息
public void modifyStudents(Students students) {
// Add a college
College college = new com.ibm.dw.jaxme.student.impl.CollegeImpl();
college.setName("Norris Bible Baptist Seminary");
college.setId("NBBS");
Address address = new com.ibm.dw.jaxme.student.impl.AddressImpl();
address.setStreet("724 North Jim Wright Freeway");
address.setCity("Ft. Worth");
address.setState("TX");
address.setZip(new java.math.BigInteger("76108"));
college.setAddress(address);
// Add the college in
List colleges = students.getCollege();
colleges.add(college);
// Change a student‘s college
List list = students.getStudent();
for (Iterator i = list.iterator(); i.hasNext(); ) {
Student student = (Student)i.next();
if (student.getFirstName().equals("Brett") &&
student.getLastName().equals("McLaughlin")) {
student.setCollegeId("NBBS");
}
}
}
|
代碼主體中還增加了一些額外的打印語句,如清單 10 所示。
清單 10. 其他的打印語句
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Incorrect arguments supplied!");
System.err.println("Usage: java com.ibm.dw.jaxme.example.JaxMeTester " +
"[input XML filename]");
return;
}
try {
JaxMeTester tester = new JaxMeTester(args[0]);
Students students = tester.readXML();
System.out.println("Students after reading in from disk...");
tester.printStudents(students, System.out);
tester.modifyStudents(students);
System.out.println("\n\nStudents after in-memory modifications...");
tester.printStudents(students, System.out);
} catch (Exception e) {
System.err.println("Error occurred: " + e.getMessage());
e.printStackTrace(System.err);
}
}
|
輸出結(jié)果如清單 11 所示,其中僅列出了學(xué)生清單修改
后的結(jié)果。
清單 11. 修改后的打印輸出
[java] --- Student Listings ---
[java] Name: Brett McLaughlin
[java] 1029 Burlingham
[java] Waco, TX 87610
[java] College: Norris Bible Baptist Seminary
[java] Name: Gary Greathouse
[java] 9098 Townhall Drive
[java] Waco, TX 87621
[java] College: Louisiana Baptist University
[java] Name: Louisiana Baptist University
[java] Address:
[java] 6301 Westport Avenue
[java] Shreveport, LA 71129
[java] Name: Norris Bible Baptist Seminary
[java] Address:
[java] 724 North Jim Wright Freeway
[java] Ft. Worth, TX 76108
|
對于多數(shù)讀者而言這都是些老生常談,但對于剛接觸數(shù)據(jù)綁定的讀者而言,讓我強(qiáng)調(diào)一下這一小段代碼的重要意義。它說明您不需要
將 XML 作為 XML處理。事實(shí)上,除了將您帶入數(shù)據(jù)綁定的大門之外,Java 代碼一直在完成其他所有工作。雖然 SAX 和 DOM(以及 JDOM、dom4j 等等)很重要,而且對于底層系統(tǒng)可以說至關(guān)重要,但一般的 Java 程序員不再需要了解這些東西了。
Java 程序員可以編寫接收和輸出基本 Java 對象的所有方法,不論這些對象來自何處去向何方。就像良好的數(shù)據(jù)庫代碼把數(shù)據(jù)庫交互和普通程序員分隔開一樣,數(shù)據(jù)綁定也能做到。一旦某個方法不再使用對象,它就不需要知道信息是否被保存,當(dāng)然也不需要知道信息是
如何保存的。這正是數(shù)據(jù)綁定的優(yōu)美之處!
從 Java 轉(zhuǎn)化到 XML
現(xiàn)在要將修改后的列表再保存到 XML 中。雖然可以覆蓋原來的 student1.xml 文件,但是我更喜歡寫入一個新的文件(從而可以比較異同)student2.xml。為此需要稍微修改
main() 方法,如清單 12 所示。
清單 12. 增加第二個參數(shù)作為輸出文件名
public static void main(String[] args) {
if (
args.length < 2) {
System.err.println("Incorrect arguments supplied!");
System.err.println("Usage: java com.ibm.dw.jaxme.example.JaxMeTester " +
"[input XML filename] [output XML filename]");
return;
}
try {
JaxMeTester tester = new JaxMeTester(args[0]);
Students students = tester.readXML();
System.out.println("Students after reading in from disk...");
tester.printStudents(students, System.out);
tester.modifyStudents(students);
System.out.println("\n\nStudents after in-memory modifications...");
tester.printStudents(students, System.out);
tester.writeStudents(students, args[1]);
} catch (Exception e) {
System.err.println("Error occurred: " + e.getMessage());
e.printStackTrace(System.err);
}
}
|
增加序列化代碼
現(xiàn)在剩下的只有新的
writeStudents() 方法了,這個方法如此簡單,我找不到任何理由來進(jìn)一步解釋,如清單 13 所示。
清單 13. 序列化 XML
public void writeStudents(Students students, String outputFile)
throws IOException, JAXBException {
// Serialize
JAXBContext ctx = JAXBContext.newInstance(
"com.ibm.dw.jaxme.student");
Marshaller m = ctx.createMarshaller();
FileWriter writer = new FileWriter(outputFile);
m.marshal(students, writer);
writer.close();
}
|
 |
丟失的 import 語句
現(xiàn)在還需要增加幾個 import 語句。我不準(zhǔn)備列出整個文件,但建議您下載本文的代碼,其中包括完整的
JaxMeTester 源代碼。
|
|
這些代碼看起來與解組過程非常相似。通過 JAXB 創(chuàng)建了一個新的
Marshaller ,同樣使用指定位置的 jaxb.properties 文件。然后將輸出文件名包裝在一個 writer 中,執(zhí)行序列化并關(guān)閉 writer(
千萬不要忘記關(guān)閉 writer?。?。嗚啦!編碼、編譯然后運(yùn)行。
還記得“往返”嗎?
結(jié)束之前讓我們看一看輸出文件(如果您使用了我給出的名稱應(yīng)該是 student2.xml),如清單 14 所示。
清單 14. student2.xml
<stu:students xmlns:stu="http://dw.ibm.com/jaxme/student">
<stu:student>
<stu:firstName>Brett</stu:firstName>
<stu:lastName>McLaughlin</stu:lastName>
<stu:collegeId>NBBS</stu:collegeId>
<stu:address type="home">
<stu:street>1029 Burlingham</stu:street>
<stu:city>Waco</stu:city>
<stu:state>TX</stu:state>
<stu:zip>87610</stu:zip>
</stu:address>
</stu:student>
<stu:student>
<stu:firstName>Gary</stu:firstName>
<stu:lastName>Greathouse</stu:lastName>
<stu:collegeId>LBU</stu:collegeId>
<stu:address type="home">
<stu:street>9098 Townhall Drive</stu:street>
<stu:city>Waco</stu:city>
<stu:state>TX</stu:state>
<stu:zip>87621</stu:zip>
</stu:address>
</stu:student>
<stu:college id="LBU">
<stu:name>Louisiana Baptist University</stu:name>
<stu:address type="home">
<stu:street>6301 Westport Avenue</stu:street>
<stu:city>Shreveport</stu:city>
<stu:state>LA</stu:state>
<stu:zip>71129</stu:zip>
</stu:address>
</stu:college>
<stu:college id="NBBS">
<stu:name>Norris Bible Baptist Seminary</stu:name>
<stu:address>
<stu:street>724 North Jim Wright Freeway</stu:street>
<stu:city>Ft. Worth</stu:city>
<stu:state>TX</stu:state>
<stu:zip>76108</stu:zip>
</stu:address>
</stu:college>
</stu:students>
|
您馬上就會注意到所有的元素都正確使用了名稱空間,而這個名稱空間用前綴
stu 給出。因此該文件在語義上與輸入是等價的(當(dāng)然包含了新增加的信息),雖然看起來非常不同。這需要回顧
本系列文章的第一篇中所討論的問題 —— 往返,XML 允許這樣做,如果不習(xí)慣的話可能會令您感到困惑。如果您完全迷惑了,請?jiān)僮x一讀第一篇文章。但是在明確指出這種差別之前,我還不想結(jié)束本文。
再論工具 API
還
記得前面對工具 API 的討論嗎?我曾經(jīng)說過數(shù)據(jù)綁定和工具 API 不同。我希望您注意到了這句話,因?yàn)檫@一點(diǎn)非常重要。數(shù)據(jù)綁定
API(JaxMe
僅是其中之一)的不利之處是很容易彌漫到所有的代碼中。我曾經(jīng)見過一些項(xiàng)目,雖然采用的體系結(jié)構(gòu)相對不錯,但是數(shù)據(jù)綁定出現(xiàn)在所有能夠想像得到的地方,并
且有幾個地方我 從來都沒有想像過。結(jié)果如果需要修改代碼,升級和 API 轉(zhuǎn)換是完全不可能的,因?yàn)閼?yīng)用程序的很多部分必須重新實(shí)現(xiàn)、重新測試、重新部署。
作為一名程序員應(yīng)盡量避免出現(xiàn)這種情況。按照經(jīng)驗(yàn)法則,應(yīng)用程序?qū)哟沃g應(yīng)保持
無關(guān)性而非
依賴性。
為此,應(yīng)使用數(shù)據(jù)綁定 API 隔離業(yè)務(wù)層和編組解組 XML 的代碼之間的交互。可以增加一個數(shù)據(jù)綁定層,防止直接調(diào)用 JaxMe(或者 JAXB
以及所用的其他 API)。我曾經(jīng)看到過各種各樣的解決方案 —— 我自己也曾提出幾種方法 ——
只要您愿意隔離這些代碼。這意味著升級或者修改只影響到隔離的少量代碼,應(yīng)用程序的其他部分仍然可以運(yùn)行。記住這一點(diǎn),您就不會煩惱纏身了。
結(jié)束語
掌握了 JaxMe 的基本用法之后,下一篇文章將介紹該 API 較難的地方,分析 JaxMe 為什么比它的表兄弟 JAXB 提供了
更多的功能。具體來說,我將關(guān)注數(shù)據(jù)庫支持,詳細(xì)探討如何使用 JaxMe 向數(shù)據(jù)庫中插入數(shù)據(jù),如 MySQL。然后如果時間和空間允許的話(只能希望),我還將說明同樣的技術(shù)如何用于 XML 數(shù)據(jù)庫。
再后面呢?只有我的想像力才知道。:)我確實(shí)有一些想法,不過您要堅(jiān)持讀下去才會知道到底是什么。到那時候希望能再看到您。
參考資料
關(guān)于作者
 |
|
 |
Brett
McLaughlin 從 Logo 時代(還記得那個小三角嗎?)就開始從事計(jì)算機(jī)。最近幾年,他已經(jīng)成為 Java 技術(shù)和 XML
社區(qū)最知名的作家和程序員之一。他曾經(jīng)在 Nextel Communications 實(shí)現(xiàn)過復(fù)雜的企業(yè)系統(tǒng),在 Lutris
Technologies 實(shí)際編寫應(yīng)用程序服務(wù)器,最近又在 O‘Reilly Media, Inc. 繼續(xù)撰寫和編輯這方面的書籍。他的新著 Java 1.5 Tiger: A Developer‘s Notebook是關(guān)于新版本 Java 技術(shù)的第一本參考書,經(jīng)典巨著
Java and XML仍然是在 Java 技術(shù)中使用 XML 技術(shù)的權(quán)威參考。
|
|