寫在前面的話
引言:
第1章 對象入門
1.1 抽象的進步
1.2 對象的接口
1.3 實現方案的隱藏
1.4 方案的重復使用
1.5 繼承:重新使用接口
1.5.1 改善基礎類:
盡管extends關鍵字暗示著我們要為接口“擴展”新功能,但實情並非肯定如此。為區分我們的新類,第二個辦法是改變基礎類一個現有函數的行為。我們將其稱作“改善”那個函數。
為改善一個函數,只需為衍生類的函數建立一個新定義即可。我們的目標是:“盡管使用的函數接口未變,但它的新版本具有不同的表現”。
1.5.2 等價和類似關系
1.6 多形對象的互換使用
1.6.1 動態綁定
1.6.2 抽象的基礎類和接口
1.7 對象的創建和存在時間
1.7.1 集合與繼承器
1.7.2 單根結構
1.7.3 集合庫與方便使用集合
1.7.4 清除時的困境:由誰負責清除?
1.8 違例控制:解決錯誤
1.9 多線程
1.10 永久性
1.11 Java和因特網:
既然Java不過另一種類型的程序設計語言,大家可能會奇怪它為什麼值得如此重視,為什麼還有這麼多的人認為它是計算機程序設計的一個裡程碑呢?如果您來自一個傳統的程序設計背景,那麼答案在剛開始的時候並不是很明顯。Java除了可解決傳統的程序設計問題以外,還能解決World Wide Web(萬維網)上的編程問題。
1.11.1 什麼是Web?
1.11.2 客戶端編程
1.11.3 服務器端編程
1.11.4 一個獨立的領域:應用程序
1.12 分析和設計
1.12.1 不要迷失:
在整個開發過程中,最重要的事情就是:不要將自己迷失!但事實上這種事情很容易發生。大多數方法都設計用來解決最大范圍內的問題。當然,也存在一些特別困難的項目,需要作者付出更為艱辛的努力,或者付出更大的代價。但是,大多數項目都是比較“常規”的,所以一般都能作出成功的分析與設計,而且只需用到推薦的一小部分方法。但無論多麼有限,某些形式的處理總是有益的,這可使整個項目的開發更加容易,總比直接了當開始編碼好!
也就是說,假如你正在考察一種特殊的方法,其中包含了大量細節,並推薦了許多步驟和文檔,那麼仍然很難正確判斷自己該在何時停止。時刻提醒自己注意以下幾個問題:
(1) 對象是什麼?(怎樣將自己的項目分割成一系列單獨的組件?)
(2) 它們的接口是什麼?(需要將什麼消息發給每一個對象?)
在確定了對象和它們的接口後,便可著手編寫一個程序。出於對多方面原因的考慮,可能還需要比這更多的說明及文檔,但要求掌握的資料絕對不能比這還少。
整個過程可劃分為四個階段,階段0剛剛開始采用某些形式的結構。
1.12.2 階段0:擬出一個計劃:
1.12.3 階段1:要制作什麼?:
1.12.4 階段2:如何構建?
1.12.5 階段3:正式創建
1.12.6 階段4:校訂
1.12.7 計劃的回報
1.13 Java還是C++?
第2章 一切都是對象
“盡管以C++為基礎,但Java是一種更純粹的面向對象程序設計語言”。
無論C++還是Java都屬於雜合語言。但在Java中,設計者覺得這種雜合並不象在C++裡那麼重要。雜合語言允許采用多種編程風格;之所以說C++是一種雜合語言,是因為它支持與C語言的向後兼容能力。由於C++是C的一個超集,所以包含的許多特性都是後者不具備的,這些特性使C++在某些地方顯得過於復雜。
Java語言首先便假定了我們只希望進行面向對象的程序設計。也就是說,正式用它設計之前,必須先將自己的思想轉入一個面向對象的世界(除非早已習慣了這個世界的思維方式)。只有做好這個准備工作,與其他OOP語言相比,才能體會到Java的易學易用。在本章,我們將探討Java程序的基本組件,並體會為什麼說Java乃至Java程序內的一切都是對象。
2.1 用句柄操縱對象
2.2 必須創建所有對象:
創建句柄時,我們希望它同一個新對象連接。通常用new關鍵字達到這一目的。new的意思是:“把我變成這些對象的一種新類型”。所以在上面的例子中,可以說:
String s = new String("asdf");
它不僅指出“將我變成一個新字串”,也通過提供一個初始字串,指出了“如何生成這個新字串”。
當然,字串(String)並非唯一的類型。Java配套提供了數量眾多的現成類型。對我們來講,最重要的就是記住能自行創建類型。事實上,這應是Java程序設計的一項基本操作,是繼續本書後余部分學習的基礎。
2.2.1 保存在什麼地方
2.2.2 特殊情況:主類型
2.2.3 Java中的數組
2.3 絕對不要清除對象:在大多數程序設計語言中,變量的“存在時間”(Lifetime)一直是程序員需要著重考慮的問題。變量應持續多長的時間?如果想清除它,那麼何時進行?在變量存在時間上糾纏不清會造成大量的程序錯誤。在下面的小節裡,將闡示Java如何幫助我們完成所有清除工作,從而極大了簡化了這個問題。
2.3.1 作用域
2.3.2 對象的作用域
2.4 新建數據類型:類
2.4.1 字段和方法
2.5 方法、自變量和返回值
2.5.1 自變量列表
2.6 構建Java程序:正式構建自己的第一個Java程序前,還有幾個問題需要注意。
2.6.1 名字的可見性
2.6.2 使用其他組件
2.6.3 static關鍵字
2.7 我們的第一個Java程序
2.8 注釋和嵌入文檔
2.8.1 注釋文檔
2.8.2 具體語法
2.8.3 嵌入HTML
2.8.4 @see:引用其他類
2.8.5 類文檔標記
2.8.6 變量文檔標記:變量文檔只能包括嵌入的HTML以及@see引用。
2.8.7 方法文檔標記
2.8.8 文檔示例:
下面還是我們的第一個Java程序,只不過已加入了完整的文檔注釋:
92頁程序
第一行:
//: Property.java
采用了我自己的方法:將一個“:”作為特殊的記號,指出這是包含了源文件名字的一個注釋行。最後一行也用這樣的一條注釋結尾,它標志著源代碼清單的結束。這樣一來,可將代碼從本書的正文中方便地提取出來,並用一個編譯器檢查。這方面的細節在第17章講述。
2.9 編碼樣式
2.10 總結:
通過本章的學習,大家已接觸了足夠多的Java編程知識,已知道如何自行編寫一個簡單的程序。此外,對語言的總體情況以及一些基本思想也有了一定程度的認識。然而,本章所有例子的模式都是單線形式的“這樣做,再那樣做,然後再做另一些事情”。如果想讓程序作出一項選擇,又該如何設計呢?例如,“假如這樣做的結果是紅色,就那樣做;如果不是,就做另一些事情”。對於這種基本的編程方法,下一章會詳細說明在Java裡是如何實現的。
2.11 練習:
(1) 參照本章的第一個例子,創建一個“Hello,World”程序,在屏幕上簡單地顯示這句話。注意在自己的類裡只需一個方法(“main”方法會在程序啟動時執行)。記住要把它設為static形式,並置入自變量列表——即使根本不會用到這個列表。用javac編譯這個程序,再用java運行它。
(2) 寫一個程序,打印出從命令行獲取的三個自變量。
(3) 找出Property.java第二個版本的代碼,這是一個簡單的注釋文檔示例。請對文件執行javadoc,並在自己的Web浏覽器裡觀看結果。
(4) 以練習(1)的程序為基礎,向其中加入注釋文檔。利用javadoc,將這個注釋文檔提取為一個HTML文件,並用Web浏覽器觀看。
第3章 控制程序流程:
“就象任何有感知的生物一樣,程序必須能操縱自己的世界,在執行過程中作出判斷與選擇。”
在Java裡,我們利用運算符操縱對象和數據,並用執行控制語句作出選擇。Java是建立在C++基礎上的,所以對C和C++程序員來說,對Java這方面的大多數語句和運算符都應是非常熟悉的。當然,Java也進行了自己的一些改進與簡化工作。
3.1 使用Java運算符
3.1.1 優先級
3.1.2 賦值
3.1.3 算術運算符
3.1.4 自動遞增和遞減
3.1.5 關系運算符
3.1.6 邏輯運算符
3.1.7 按位運算符
3.1.8 移位運算符
3.1.9 三元if-else運算符
3.1.10 逗號運算符:
在C和C++裡,逗號不僅作為函數自變量列表的分隔符使用,也作為進行後續計算的一個運算符使用。在Java裡需要用到逗號的唯一場所就是for循環,本章稍後會對此詳加解釋。
3.1.11 字串運算符+
3.1.12 運算符常規操作規則
3.1.13 造型運算符
3.1.14 Java沒有“sizeof”
3.1.15 復習計算順序
3.1.16 運算符總結
3.2 執行控制:
Java使用了C的全部控制語句,所以假期您以前用C或C++編程,其中大多數都應是非常熟悉的。大多數程序化的編程語言都提供了某種形式的控制語句,這在語言間通常是共通的。在Java裡,涉及的關鍵字包括if-else、while、do-while、for以及一個名為switch的選擇語句。然而,Java並不支持非常有害的goto(它仍是解決某些特殊問題的權宜之計)。仍然可以進行象goto那樣的跳轉,但比典型的goto要局限多了。
3.2.1 真和假:
所有條件語句都利用條件表達式的真或假來決定執行流程。條件表達式的一個例子是A==B。它用條件運算符“==”來判斷A值是否等於B值。該表達式返回true或false。本章早些時候接觸到的所有關系運算符都可拿來構造一個條件語句。注意Java不允許我們將一個數字作為布爾值使用,即使它在C和C++裡是允許的(真是非零,而假是零)。若想在一次布爾測試中使用一個非布爾值——比如在if(a)裡,那麼首先必須用一個條件表達式將其轉換成一個布爾值,例如if(a!=0)。
3.2.2 if-else
3.2.3 反復
3.2.4 do-while
3.2.5 for
3.2.6 中斷和繼續
3.2.7 切換
3.3 總結:
本章總結了大多數程序設計語言都具有的基本特性:計算、運算符優先順序、類型轉換以及選擇和循環等等。現在,我們作好了相應的准備,可繼續向面向對象的程序設計領域邁進。在下一章裡,我們將討論對象的初始化與清除問題,再後面則講述隱藏的基本實現方法。
3.4 練習:
(1) 寫一個程序,打印出1到100間的整數。
(2) 修改練習(1),在值為47時用一個break退出程序。亦可換成return試試。
(3) 創建一個switch語句,為每一種case都顯示一條消息。並將switch置入一個for循環裡,令其嘗試每一種case。在每個case後面都放置一個break,並對其進行測試。然後,刪除break,看看會有什麼情況出現。
第4章 初始化和清除:
“隨著計算機的進步,‘不安全’的程序設計已成為造成編程代價高昂的罪魁禍首之一。”
“初始化”和“清除”是這些安全問題的其中兩個。許多C程序的錯誤都是由於程序員忘記初始化一個變量造成的。對於現成的庫,若用戶不知道如何初始化庫的一個組件,就往往會出現這一類的錯誤。清除是另一個特殊的問題,因為用完一個元素後,由於不再關心,所以很容易把它忘記。這樣一來,那個元素占用的資源會一直保留下去,極易產生資源(主要是內存)用盡的後果。
C++為我們引入了“構建器”的概念。這是一種特殊的方法,在一個對象創建之後自動調用。Java也沿用了這個概念,但新增了自己的“垃圾收集器”,能在資源不再需要的時候自動釋放它們。本章將討論初始化和清除的問題,以及Java如何提供它們的支持。
4.1 由構建器保證初始化
4.2 方法過載
4.2.1 區分過載方法
4.2.2 主類型的過載
4.2.3 返回值過載
4.2.4 默認構建器
4.2.5 this關鍵字
4.3 清除:收尾和垃圾收集
4.3.1 finalize()用途何在
4.3.2 必須執行清除
4.4 成員初始化
4.4.1 規定初始化
4.4.2 構建器初始化
4.5 數組初始化
4.5.1 多維數組
4.6 總結
4.7 練習:
(1) 用默認構建器創建一個類(沒有自變量),用它打印一條消息。創建屬於這個類的一個對象。
(2) 在練習1的基礎上增加一個過載的構建器,令其采用一個String自變量,並隨同自己的消息打印出來。
(3) 以練習2創建的類為基礎上,創建屬於它的對象句柄的一個數組,但不要實際創建對象並分配到數組裡。運行程序時,注意是否打印出來自構建器調用的初始化消息。
(4) 創建同句柄數組聯系起來的對象,最終完成練習3。
(5) 用自變量“before”,“after”和“none”運行程序,試驗Garbage.java。重復這個操作,觀察是否從輸出中看出了一些固定的模式。改變代碼,使System.runFinalization()在System.gc()之前調用,再觀察結果。
第5章 隱藏實施過程
5.1 包:庫單元
5.1.1 創建獨一無二的包名
5.1.2 自定義工具庫
5.1.3 利用導入改變行為
5.1.4 包的停用:
大家應注意這樣一個問題:每次創建一個包後,都在為包取名時間接地指定了一個目錄結構。這個包必須存在(駐留)於由它的名字規定的目錄內。而且這個目錄必須能從CLASSPATH開始搜索並發現。最開始的時候,package關鍵字的運用可能會令人迷惑,因為除非堅持遵守根據目錄路徑指定包名的規則,否則就會在運行期獲得大量莫名其妙的消息,指出找不到一個特定的類——即使那個類明明就在相同的目錄中。若得到象這樣的一條消息,請試著將package語句作為注釋標記出去。如果這樣做行得通,就可知道問題到底出在哪兒。
5.2 Java訪問指示符:
5.2.1 “友好的”
5.2.2 public:接口訪問
5.2.3 private:不能接觸
5.2.4 protected:“友好的一種”
5.3 接口與實現
5.4 類訪問
5.5 總結
5.6 練習
第6章 類再生
6.1 合成的語法
6.2 繼承的語法
6.2.1 初始化基礎類
6.3 合成與繼承的結合
6.3.1 確保正確的清除
6.3.2 名字的隱藏
6.4 到底選擇合成還是繼承
6.5 protected
6.6 遞增開發
6.7 上溯造型
6.7.1 何謂“上溯造型”?
6.8 final關鍵字:
由於語境(應用環境)不同,final關鍵字的含義可能會稍微產生一些差異。但它最一般的意思就是聲明“這個東西不能改變”。之所以要禁止改變,可能是考慮到兩方面的因素:設計或效率。由於這兩個原因頗有些區別,所以也許會造成final關鍵字的誤用。
在接下去的小節裡,我們將討論final關鍵字的三種應用場合:數據、方法以及類。
6.8.1 final數據
6.8.2 final方法
6.8.3 final類
6.8.4 final的注意事項
6.9 初始化和類裝載
6.9.1 繼承初始化
6.10 總結
6.11 練習:
(1) 用默認構建器(空自變量列表)創建兩個類:A和B,令它們自己聲明自己。從A繼承一個名為C的新類,並在C內創建一個成員B。不要為C創建一個構建器。創建類C的一個對象,並觀察結果。
(2) 修改練習1,使A和B都有含有自變量的構建器,則不是采用默認構建器。為C寫一個構建器,並在C的構建器中執行所有初始化工作。
(3) 使用文件Cartoon.java,將Cartoon類的構建器代碼變成注釋內容標注出去。解釋會發生什麼事情。
(4) 使用文件Chess.java,將Chess類的構建器代碼作為注釋標注出去。同樣解釋會發生什麼。
第7章 多形性
7.1 上溯造型
7.1.1 為什麼要上溯造型
7.2 深入理解:
對於Music.java的困難性,可通過運行程序加以體會。輸出是Wind.play()。這當然是我們希望的輸出,但它看起來似乎並不願按我們的希望行事。請觀察一下tune()方法:
public static void tune(Instrument i) {
// ...
i.play(Note.middleC);
}
它接收Instrument句柄。所以在這種情況下,編譯器怎樣才能知道Instrument句柄指向的是一個Wind,而不是一個Brass或Stringed呢?編譯器無從得知。為了深入了理解這個問題,我們有必要探討一下“綁定”這個主題。
7.2.1 方法調用的綁定
7.2.2 產生正確的行為
7.2.3 擴展性
7.3 覆蓋與過載
7.4 抽象類和方法
7.5 接口
7.5.1 Java的“多重繼承”
7.5.2 通過繼承擴展接口
7.5.3 常數分組
7.5.4 初始化接口中的字段
7.6 內部類
7.6.1 內部類和上溯造型
7.6.2 方法和作用域中的內部類
7.6.3 鏈接到外部類
7.6.4 static內部類
7.6.5 引用外部類對象
7.6.6 從內部類繼承
7.6.7 內部類可以覆蓋嗎?
7.6.8 內部類標識符
7.6.9 為什麼要用內部類:控制框架
7.7 構建器和多形性:
同往常一樣,構建器與其他種類的方法是有區別的。在涉及到多形性的問題後,這種方法依然成立。盡管構建器並不具有多形性(即便可以使用一種“虛擬構建器”——將在第11章介紹),但仍然非常有必要理解構建器如何在復雜的分級結構中以及隨同多形性使用。這一理解將有助於大家避免陷入一些令人不快的糾紛。
7.7.1 構建器的調用順序
7.7.2 繼承和finalize()
7.7.3 構建器內部的多形性方法的行為
7.8 通過繼承進行設計
7.8.1 純繼承與擴展
7.8.2 下溯造型與運行期類型標識
7.9 總結
7.10 練習:
(1) 創建Rodent(嚙齒動物):Mouse(老鼠),Gerbil(鼹鼠),Hamster(大頰鼠)等的一個繼承分級結構。在基礎類中,提供適用於所有Rodent的方法,並在衍生類中覆蓋它們,從而根據不同類型的Rodent采取不同的行動。創建一個Rodent數組,在其中填充不同類型的Rodent,然後調用自己的基礎類方法,看看會有什麼情況發生。
(2) 修改練習1,使Rodent成為一個接口。
(3) 改正WindError.java中的問題。
(4) 在GreenhouseControls.java中,添加Event內部類,使其能打開和關閉風扇。
第8章 對象的容納
8.1 數組
8.1.1 數組和第一類對象
8.1.2 數組的返回
8.2 集合
8.2.1 缺點:類型未知
8.3 枚舉器(反復器)
8.4 集合的類型:
標准Java 1.0和1.1庫配套提供了非常少的一系列集合類。但對於自己的大多數編程要求,它們基本上都能勝任。正如大家到本章末尾會看到的,Java 1.2提供的是一套重新設計過的大型集合庫。
8.4.1 Vector
8.4.2 BitSet
8.4.3 Stack
8.4.4 Hashtable
8.4.5 再論枚舉器
8.5 排序
8.6 通用集合庫
8.7 新集合
8.7.1 使用Collections
8.7.2 使用Lists
8.7.3 使用Sets
8.7.4 使用Maps
8.7.5 決定實施方案
8.7.6 未支持的操作
8.7.7 排序和搜索
8.7.8 實用工具
8.8 總結
8.9 練習
第9章 違例差錯控制
9.1 基本違例
9.1.1 違例自變量
9.2 違例的捕獲:
若某個方法產生一個違例,必須保證該違例能被捕獲,並獲得正確對待。對於Java的違例控制機制,它的一個好處就是允許我們在一個地方將精力集中在要解決的問題上,然後在另一個地方對待來自那個代碼內部的錯誤。
為理解違例是如何捕獲的,首先必須掌握“警戒區”的概念。它代表一個特殊的代碼區域,有可能產生違例,並在後面跟隨用於控制那些違例的代碼。
9.2.1 try塊
9.2.2 違例控制器
9.2.3 違例規范
9.2.4 捕獲所有違例
9.2.5 重新“擲”出違例
9.3 標准Java違例
9.3.1 RuntimeException的特殊情況
9.4 創建自己的違例
9.5 違例的限制
9.6 用finally清除
9.6.1 用finally做什麼
9.6.2 缺點:丟失的違例
9.7 構建器
9.8 違例匹配
9.8.1 違例准則
9.9 總結:
通過先進的錯誤糾正與恢復機制,我們可以有效地增強代碼的健壯程度。對我們編寫的每個程序來說,錯誤恢復都屬於一個基本的考慮目標。它在Java中顯得尤為重要,因為該語言的一個目標就是創建不同的程序組件,以便其他用戶(客戶程序員)使用。為構建一套健壯的系統,每個組件都必須非常健壯。
在Java裡,違例控制的目的是使用盡可能精簡的代碼創建大型、可靠的應用程序,同時排除程序裡那些不能控制的錯誤。
違例的概念很難掌握。但只有很好地運用它,才可使自己的項目立即獲得顯著的收益。Java強迫遵守違例所有方面的問題,所以無論庫設計者還是客戶程序員,都能夠連續一致地使用它。
9.10 練習:
(1) 用main()創建一個類,令其擲出try塊內的Exception類的一個對象。為Exception的構建器賦予一個字串參數。在catch從句內捕獲違例,並打印出字串參數。添加一個finally從句,並打印一條消息,證明自己真正到達那裡。
(2) 用extends關鍵字創建自己的違例類。為這個類寫一個構建器,令其采用String參數,並隨同String句柄把它保存到對象內。寫一個方法,令其打印出保存下來的String。創建一個try-catch從句,練習實際操作新違例。
(3) 寫一個類,並令一個方法擲出在練習2中創建的類型的一個違例。試著在沒有違例規范的前提下編譯它,觀察編譯器會報告什麼。接著添加適當的違例規范。在一個try-catch從句中嘗試自己的類以及它的違例。
(4) 在第5章,找到調用了Assert.java的兩個程序,並修改它們,令其擲出自己的違例類型,而不是打印到System.err。該違例應是擴展了RuntimeException的一個內部類。
第10章 Java IO系統
10.1 輸入和輸出
10.1.1 InputStream的類型
10.1.2 OutputStream的類型
10.2 增添屬性和有用的接口
10.2.1 通過FilterInputStream從InputStream裡讀入數據
10.2.2 通過FilterOutputStream向OutputStream裡寫入數據
10.3 本身的缺陷:RandomAccessFile
10.4 File類:
File類有一個欺騙性的名字——通常會認為它對付的是一個文件,但實情並非如此。它既代表一個特定文件的名字,也代表目錄內一系列文件的名字。若代表一個文件集,便可用list()方法查詢這個集,返回的是一個字串數組。之所以要返回一個數組,而非某個靈活的集合類,是因為元素的數量是固定的。而且若想得到一個不同的目錄列表,只需創建一個不同的File對象即可。事實上,“FilePath”(文件路徑)似乎是一個更好的名字。本節將向大家完整地例示如何使用這個類,其中包括相關的FilenameFilter(文件名過濾器)接口。
10.4.1 目錄列表器
10.4.2 檢查與創建目錄
10.5 IO流的典型應用
10.5.1 輸入流
10.5.2 輸出流
10.5.3 快捷文件處理
10.5.4 從標准輸入中讀取數據
10.5.5 管道數據流:
本章已簡要介紹了PipedInputStream(管道輸入流)和PipedOutputStream(管道輸出流)。盡管描述不十分詳細,但並不是說它們作用不大。然而,只有在掌握了多線程處理的概念後,才可真正體會它們的價值所在。原因很簡單,因為管道化的數據流就是用於線程之間的通信。這方面的問題將在第14章用一個示例說明。
10.6 StreamTokenizer
10.6.1 StringTokenizer
10.7 Java 1.1的IO流
10.7.1 數據的發起與接收
10.7.2 修改數據流的行為
10.7.3 未改變的類:
顯然,Java庫的設計人員覺得以前的一些類毫無問題,所以沒有對它們作任何修改,可象以前那樣繼續使用它們:
沒有對應Java 1.1類的Java 1.0類
DataOutputStream
File
RandomAccessFile
SequenceInputStream
特別未加改動的是DataOutputStream,所以為了用一種可轉移的格式保存和獲取數據,必須沿用InputStream和OutputStream層次結構。
10.7.4 一個例子
10.7.5 重定向標准IO
10.8 壓縮
10.8.1 用GZIP進行簡單壓縮
10.8.2 用Zip進行多文件保存
10.8.3 Java歸檔(jar)實用程序
10.9 對象串聯
10.9.1 尋找類
10.9.2 序列化的控制
10.9.3 利用“持久性”
10.10 總結
10.11 練習
第11章 運行期類型鑒定:
運行期類型鑒定(RTTI)的概念初看非常簡單——手上只有基礎類型的一個句柄時,利用它判斷一個對象的正確類型。
然而,對RTTI的需要暴露出了面向對象設計許多有趣(而且經常是令人困惑的)的問題,並把程序的構造問題正式擺上了桌面。
本章將討論如何利用Java在運行期間查找對象和類信息。這主要采取兩種形式:一種是“傳統”RTTI,它假定我們已在編譯和運行期擁有所有類型;另一種是Java1.1特有的“反射”機制,利用它可在運行期獨立查找類信息。首先討論“傳統”的RTTI,再討論反射問題。
11.1 對RTTI的需要
11.1.1 Class對象
11.1.2 造型前的檢查
11.2 RTTI語法
11.3 反射:運行期類信息
11.3.1 一個類方法提取器
11.4 總結
11.5 練習:
(1) 寫一個方法,向它傳遞一個對象,循環打印出對象層次結構中的所有類。
(2) 在ToyTest.java中,將Toy的默認構建器標記成注釋信息,解釋隨之發生的事情。
(3) 新建一種類型的集合,令其使用一個Vector。捕獲置入其中的第一個對象的類型,然後從那時起只允許用戶插入那種類型的對象。
(4) 寫一個程序,判斷一個Char數組屬於基本數據類型,還是一個真正的對象。
(5) 根據本章的說明,實現clearSpitValve()。
(6) 實現本章介紹的rotate(Shape)方法,令其檢查是否已經旋轉了一個圓(若已旋轉,就不再執行旋轉操作)。
第12章 傳遞和返回對象
12.1 傳遞句柄
12.1.1 別名問題
12.2 制作本地副本
12.2.1 按值傳遞
12.2.2 克隆對象
12.2.3 使類具有克隆能力
12.2.4 成功的克隆
12.2.5 Object.clone()的效果
12.2.6 克隆合成對象
12.2.7 用Vector進行深層復制
12.2.8 通過序列化進行深層復制
12.2.9 使克隆具有更大的深度
12.2.10 為什麼有這個奇怪的設計
12.3 克隆的控制
12.3.1 副本構建器
12.4 只讀類
12.4.1 創建只讀類
12.4.2 “一成不變”的弊端
12.4.3 不變字串
12.4.4 String和StringBuffer類
12.4.5 字串的特殊性:
現在,大家已知道String類並非僅僅是Java提供的另一個類。String裡含有大量特殊的類。通過編譯器和特殊的覆蓋或過載運算符+和+=,可將引號字符串轉換成一個String。在本章中,大家已見識了剩下的一種特殊情況:用同志StringBuffer精心構造的“不可變”能力,以及編譯器中出現的一些有趣現象。
12.5 總結
12.6 練習
第13章 創建窗口和程序片
13.1 為何要用AWT?
13.2 基本程序片
13.2.1 程序片的測試
13.2.2 一個更圖形化的例子
13.2.3 框架方法的演示
13.3 制作按鈕
13.4 捕獲事件
13.5 文本字段
13.6 文本區域
13.7 標簽
13.8 復選框
13.9 單選鈕
13.10 下拉列表
13.11 列表框
13.11.1 handleEvent()
13.12 布局的控制
13.12.1 FlowLayout
13.12.2 BorderLayout
13.12.3 GridLayout
13.12.4 CardLayout
13.12.5 GridBagLayout
13.13 action的替用品
13.14 程序片的局限
13.14.1 程序片的優點
13.15 視窗化應用
13.15.1 菜單
13.15.2 對話框
13.16 新型AWT
13.16.1 新的事件模型
13.16.2 事件和接收者類型
13.16.3 用Java 1.1 AWT制作窗口和程序片
13.16.4 再探早期示例
13.16.5 動態綁定事件
13.16.6 將商業邏輯與UI邏輯區分開
13.16.7 推薦編碼方法
13.17 Java 1.1 UI API:
Java 1.1版同樣增加了一些重要的新功能,包括焦點遍歷,桌面色彩訪問,打印“沙箱內”及早期的剪貼板支持。
焦點遍歷十分的簡單,因為它顯然存在於AWT庫裡的組件並且我們不必為使它工作而去做任何事。如果我們制造我們自己組件並且想使它們去處理焦點遍歷,我們過載isFocusTraversable()以使它返回真值。如果我們想在一個鼠標單擊上捕捉鍵盤焦點,我們可以捕捉鼠標按下事件並且調用requestFocus()需求焦點方法。
13.17.1 桌面顏色
13.17.2 打印
13.17.3 剪貼板
13.18 可視編程和Beans
13.18.1 什麼是Bean
13.18.2 用Introspector提取BeanInfo
13.18.3 一個更復雜的Bean
13.18.4 Bean的封裝
13.18.5 更復雜的Bean支持
13.18.6 Bean更多的知識:
另外有關的爭議是Bean不能被編址。無論何時我們創建一個Bean,都希望它會在一個多線程的環境中運行。這意味著我們必須理解線程的出口,我們將在下一章中介紹。我們會發現有一段稱為“Java Beans的回顧”的節會注意到這個問題和它的解決方案。
13.19 Swing入門
13.19.1 Swing有哪些優點
13.19.2 方便的轉換
13.19.3 顯示框架
13.19.4 工具提示:
幾乎所有我們利用來創建我們用戶接口的來自於JComponent的類都包含一個稱為setToolTipText(string)的方法。因此,幾乎任何我們所需要表示的(對於一個對象jc來說就是一些來自JComponent的類)都可以安放在窗體中:
jc.setToolTipText("My tip");
並且當鼠標停在JComponent上一個超過預先設置的一個時間,一個包含我們的文字的小框就會從鼠標下彈出。
13.19.5 邊框
13.19.6 按鈕
13.19.7 按鈕組
13.19.8 圖標
13.19.9 菜單
13.19.10 彈出式菜單
13.19.11 列表框和組合框
13.19.12 滑桿和進度指示條
13.19.13 樹
13.19.14 表格
13.19.15 卡片式對話框
13.19.16 Swing消息框:
開窗的環境通常包含一個標准的信息框集,允許我們很快傳遞消息給用戶或者從用戶那裡捕捉消息。在Swing裡,這些信息窗被包含在JOptionPane裡的。我們有一些不同的可能實現的事件(有一些十分復雜),但有一點,我們必須盡可能的利用static JOptionPane.showMessageDialog()和 JOptionPane.showConfirmDialog()方法,調用消息對話框和確認對話框。
13.19.17 Swing更多的知識
13.20 總結
13.21 練習
第14章 多線程
14.1 反應靈敏的用戶界面
14.1.1 從線程繼承
14.1.2 針對用戶界面的多線程
14.1.3 用主類合並線程
14.1.4 制作多個線程
14.1.5 Daemon線程
14.2 共享有限的資源:
可將單線程程序想象成一種孤立的實體,它能遍歷我們的問題空間,而且一次只能做一件事情。由於只有一個實體,所以永遠不必擔心會有兩個實體同時試圖使用相同的資源,就象兩個人同時都想停到一個車位,同時都想通過一扇門,甚至同時發話。
進入多線程環境後,它們則再也不是孤立的。可能會有兩個甚至更多的線程試圖同時同一個有限的資源。必須對這種潛在資源沖突進行預防,否則就可能發生兩個線程同時訪問一個銀行帳號,打印到同一台計算機,以及對同一個值進行調整等等。
14.2.1 資源訪問的錯誤方法
14.2.2 Java如何共享資源
14.2.3 回顧Java Beans
14.3 堵塞
14.3.1 為何會堵塞
14.3.2 死鎖
14.4 優先級
14.4.1 線程組
14.5 回顧runnable
14.5.1 過多的線程
14.6 總結
14.7 練習
第15章 網絡編程
15.1 機器的標識
15.1.1 服務器和客戶機
15.1.2 端口:機器內獨一無二的場所
15.2 套接字
15.2.1 一個簡單的服務器和客戶機程序
15.3 服務多個客戶
15.4 數據報
15.5 一個Web應用
15.5.1 服務器應用
15.5.2 NameSender程序片
15.5.3 要注意的問題
15.6 Java與CGI的溝通
15.6.1 CGI數據的編碼
15.6.2 程序片
15.6.3 用C++寫的CGI程序
15.6.4 POST的概念
15.7 用JDBC連接數據庫
15.7.1 獲得學習示例
15.7.2 查找程序的GUI版本
15.7.3 JDBC API為何如何復雜
15.8 遠程方法:
為通過網絡執行其他機器上的代碼,傳統的方法不僅難以學習和掌握,也極易出錯。思考這個問題最佳的方式是:某些對象正好位於另一台機器,我們可向它們發送一條消息,並獲得返回結果,就象那些對象位於自己的本地機器一樣。Java 1.1的“遠程方法調用”(RMI)采用的正是這種抽象。本節將引導大家經歷一些必要的步驟,創建自己的RMI對象。
15.8.1 遠程接口概念
15.8.2 遠程接口的實施
15.8.3 創建根與干
15.8.4 使用遠程對象
15.8.5 RMI的替選方案
15.9 總結:
由於篇幅所限,還有其他許多涉及連網的概念沒有介紹給大家。Java也為URL提供了相當全面的支持,包括為因特網上不同類型的客戶提供協議控制器等等。
除此以外,一種正在逐步流行的技術叫作Servlet Server。它是一種因特網服務器應用,通過Java控制客戶請求,而非使用以前那種速度很慢、且相當麻煩的CGI(通用網關接口)協議。這意味著為了在服務器那一端提供服務,我們可以用Java編程,不必使用自己不熟悉的其他語言。由於Java具有優秀的移植能力,所以不必關心具體容納這個服務器是什麼平台。
所有這些以及其他特性都在《Java Network Programming》一書中得到了詳細講述。該書由Elliotte Rusty Harold編著,O'Reilly於1997年出版。
15.10 練習
第16章 設計范式
16.1 范式的概念
16.1.1 單子
16.1.2 范式分類
16.2 觀察器范式
16.3 模擬垃圾回收站
16.4 改進設計
16.4.1 “制作更多的對象”
16.4.2 用於原型創建的一個范式
16.5 抽象的應用
16.6 多重派遣
16.6.1 實現雙重派遣
16.7 訪問器范式
16.8 RTTI有害嗎
16.9 總結
16.10 練習:
(1) 將SingletonPattern.java作為起點,創建一個類,用它管理自己固定數量的對象。
(2) 為TrashVisitor.java添加一個名為Plastic(塑料)的類。
(3) 為DynaTrash.java同樣添加一個Plastic(塑料)類。
第17章 項目:
本章包含了一系列項目,它們都以本書介紹的內容為基礎,並對早期的章節進行了一定程度的擴充。
與以前經歷過的項目相比,這兒的大多數項目都明顯要復雜得多,它們充分演示了新技術以及類庫的運用。
17.1 文字處理
17.1.1 提取代碼列表
17.1.2 檢查大小寫樣式
17.2 方法查找工具
17.3 復雜性理論
17.4 總結:
通過本章的學習,大家知道運用Java可做到一些較復雜的事情。通過這些例子亦可看出,盡管Java必定有自己的局限,但受那些局限影響的主要是性能(比如寫好文字處理程序後,會發現C++的版本要快得多——這部分是由於IO庫做得不完善造成的;而在你讀到本書的時候,情況也許已發生了變化。但Java的局限也僅此而已,它在語言表達方面的能力是無以倫比的。利用Java,幾乎可以表達出我們想得到的任何事情。而與此同時,Java在表達的方便性和易讀性上,也做足了功夫。所以在使用Java時,一般不會陷入其他語言常見的那種復雜境地。使用那些語言時,會感覺它們象一個愛唠叨的老太婆,哪有Java那樣清純、簡練!而且通過Java 1.2的JFC/Swing庫,AWT的表達能力和易用性甚至又得到了進一步的增強。
17.5 練習:
(1) (稍微有些難度)改寫FieldOBeasts.java,使它的狀態能夠保持固定。加上一些按鈕,允許用戶保存和恢復不同的狀態文件,並從它們斷掉的地方開始繼續運行。請先參考第10章的CADState.java,再決定具體怎樣做。
(2) (大作業)以FieldOBeasts.java作為起點,構造一個自動化交通仿真系統。
(3) (大作業)以ClassScanner.java作為起點,構造一個特殊的工具,用它找出那些雖然定義但從未用過的方法和字段。
(4) (大作業)利用JDBC,構造一個聯絡管理程序。讓這個程序以一個平面文件數據庫為基礎,其中包含了名字、地址、電話號碼、E-mail地址等聯系資料。應該能向數據庫裡方便地加入新名字。鍵入要查找的名字時,請采用在第15章的VLookup.java裡介紹過的那種名字自動填充技術。
附錄A 使用非Java代碼
A.1 Java固有接口
A.1.1 調用固有方法
A.1.2 訪問JNI函數:JNIEnv自變量
A.1.3 傳遞和使用Java對象
A.1.4 JNI和Java違例
A.1.5 JNI和線程處理
A.1.6 使用現成代碼
A.2 微軟的解決方案
A.3 J/Direct
A.3.1 @dll.import引導命令
A.3.2 com.ms.win32包
A.3.3 匯集
A.3.4 編寫回調函數
A.3.5 其他J/Direct特性
A.4 本原接口(RNI)
A.4.1 RNI總結:
RNI與Microsoft JVM緊密集成這一事實既是它的優點,也是它的缺點。RNI比JNI復雜得多,但它也為我們提供了對JVM內部活動的高度控制;其中包括垃圾收集。此外,它顯然針對速度進行了優化,采納了C程序員熟悉的一些折衷方案和技術。但除了微軟的JVM之外,它並不適於其他JVM。
A.5 Java/COM集成
A.5.1 COM基礎
A.5.2 MS Java/COM集成
A.5.3 用Java設計COM服務器
A.5.4 用Java設計COM客戶
A.5.5 ActiveX/Beans集成
A.5.6 固有方法與程序片的注意事項
A.6 CORBA
A.6.1 CORBA基礎
A.6.2 一個例子
A.6.3 Java程序片和CORBA
A.6.4 比較CORBA與RMI
A.7 總結
附錄B 對比C++和Java
附錄C Java編程規則
附錄D 性能
附錄E 關於垃圾收集的一些話
附錄F 推薦讀物