Time of Update: 2017-02-27
到這個時候,大家或許會陷入一種困境之中,懷疑是否存在IO流的另一種設計方案,並可能要求更大的代碼量。還有人能提出一種更古怪的設計嗎?事實上,Java 1.1對IO流庫進行了一些重大的改進。看到Reader和Writer類時,大多數人的第一個印象(就象我一樣)就是它們用來替換原來的InputStream和OutputStream類。但實情並非如此。儘管不建議使用未經處理資料流庫的某些功能(如使用它們,會從編譯器收到一條警告訊息),但原來的資料流依然得到了保留,以便維持向後相容,而且:(1)
Time of Update: 2017-02-27
由於以前採用的一些典型形式都涉及到檔案處理,所以大家也許會懷疑為什麼要進行那麼多的代碼輸入——這正是裝飾器方案一個缺點。本部分將向大家展示如何建立和使用典型檔案讀取和寫入配置的快捷版本。這些快捷版本均置入packagecom.bruceeckel.tools中(自第5章開始建立)。為了將每個類都添加到庫內,只需將其置入適當的目錄,並添加對應的package語句即可。7.
Time of Update: 2017-02-27
兩類主要的輸出資料流是按它們寫入資料的方式劃分的:一種按人的習慣寫入,另一種為了以後由一個DataInputStream而寫入。RandomAccessFile是獨立的,儘管它的資料格式相容於DataInputStream和DataOutputStream。5.
Time of Update: 2017-02-27
當然,我們經常想做的一件事情是將格式化的輸出列印到控制台,但那已在第5章建立的com.bruceeckel.tools中得到了簡化。第1到第4部分示範了輸入資料流的建立與使用(儘管第4部分展示了將輸出資料流作為一個測試載入器的簡單應用)。1.
Time of Update: 2017-02-27
儘管庫記憶體在大量IO流類,可通過多種不同的方式組合到一起,但實際上只有幾種方式才會經常用到。然而,必須小心在意才能得到正確的組合。下面這個相當長的例子展示了典型IO配置的建立與使用,可在寫自己的代碼時將其作為一個參考使用。注意每個配置都以一個注釋形式的編號起頭,並提供了適當的解釋資訊。 //: IOStreamDemo.java// Typical IO Stream Configurationsimport java.io.*;import
Time of Update: 2017-02-27
這一類別包括的類決定了我們的輸入往何處去:一個位元組數組(但沒有String;假定我們可用位元組數組建立一個);一個檔案;或者一個“管道”。除此以外,FilterOutputStream為“破壞器”類提供了一個基礎類,它將屬性或者有用的介面同輸出資料流串連起來。這將在以後討論。表10.2 OutputStream的類型 Class
Time of Update: 2017-02-27
“對語言設計人員來說,建立好的輸入/輸出系統是一項特別困難的任務。”由於存在大量不同的設計方案,所以該任務的困難性是很容易證明的。其中最大的挑戰似乎是如何覆蓋所有可能的因素。不僅有三種不同的種類的IO需要考慮(檔案、控制台、網路連接),而且需要通過大量不同的方式與它們通訊(順序、隨機訪問、二進位、字元、按行、按字等等)。Java庫的設計者通過建立大量類來攻克這個難題。事實上,Java的IO系統採用了如此多的類,以致剛開始會產生不知從何處入手的感覺(具有諷刺意味的是,Jav
Time of Update: 2017-02-27
覆蓋一個方法時,只能產生已在方法的基礎類版本中定義的違例。這是一個重要的限制,因為它意味著與基礎類協同工作的代碼也會自動應用於從基礎類衍生的任何對象(當然,這屬於基本的OOP概念),其中包括違例。下面這個例子示範了強加在違例身上的限制類型(在編譯期): //: StormyInning.java// Overridden methods may throw only the // exceptions specified in their base-class // versions,
Time of Update: 2017-02-27
Java包含了一個名為Throwable的類,它對可以作為違例“擲”出的所有東西進行了描述。Throwable對象有兩種常規類型(亦即“從Throwable繼承”)。其中,Error代表編譯期和系統錯誤,我們一般不必特意捕獲它們(除在特殊情況以外)。Exception是可以從任何標準Java庫的類方法中“擲”出的基本類型。此外,它們亦可從我們自己的方法以及運行期輕負荷事件中“擲”出。為獲得違例的一個綜
Time of Update: 2017-02-27
在Java中,對那些要調用方法的客戶程式員,我們要通知他們可能從自己的方法裡“擲”出違例。這是一種有禮貌的做法,只有它才能使客戶程式員準確地知道要編寫什麼代碼來捕獲所有潛在的違例。當然,若你同時提供了源碼,客戶程式員甚至能全盤檢查代碼,找出相應的throw語句。但儘管如此,通常並不隨同源碼提供庫。為解決這個問題,Java提供了一種特殊的文法格式(並強迫我們採用),以便禮貌地告訴客戶程式員該方法會“擲”出什麼違例,令對方方便地加以控制。這便是我們在這
Time of Update: 2017-02-27
下面複習一下由標準Java(1.0和1.1)庫提供的集合(BitSet未包括在這裡,因為它更象一種負有特殊使命的類):(1) 數組包含了對象的數字化索引。它容納的是一種已知類型的對象,所以在尋找一個對象時,不必對結果進行造型處理。數組可以是多維的,而且能夠容納基礎資料型別 (Elementary Data Type)。但是,一旦把它建立好以後,大小便不能變化了。(2)
Time of Update: 2017-02-27
調用Object.clone()時,實際發生的是什麼事情呢?當我們在自己的類裡覆蓋clone()時,什麼東西對於super.clone()來說是最關鍵的呢?根類中的clone()方法負責建立正確的儲存容量,並通過“按位複製”將二進位位從原始對象中複製到新對象的儲存空間。也就是說,它並不只是預留儲存空間以及複製一個對象——實際需要調查出欲複製之對象的準確大小,然後複製那個對象。由於所有這些工作都是在由根類定義之clone()方法的內部代碼中進行的(根
Time of Update: 2017-02-27
理解了實現clone()方法背後的所有細節後,便可建立出能方便複製的類,以便提供了一個本機複本: //: LocalCopy.java// Creating local copies with clone()import java.util.*;class MyObject implements Cloneable { int i; MyObject(int ii) { i = ii; } public Object clone() { Object o = null;
Time of Update: 2017-02-27
若需修改一個對象,同時不想改變調用者的對象,就要製作該對象的一個本機複本。這也是本機複本最常見的一種用途。若決定製作一個本機複本,只需簡單地使用clone()方法即可。Clone是“複製”的意思,即製作完全一模一樣的副本。這個方法在基礎類Object中定義成“protected”(受保護)模式。但在希望複製的任何衍生類中,必須將其覆蓋為“public”模式。例如,標準庫類Vector覆蓋了clone(),所以能為Vector
Time of Update: 2017-02-27
利用RTTI可根據一個匿名的基礎類控制代碼調查出類型資訊。但正是由於這個原因,新手們極易誤用它,因為有些時候多型方法便足夠了。對那些以前習慣程式化編程的人來說,極易將他們的程式組織成一系列switch語句。他們可能用RTTI做到這一點,從而在代碼開發和維護中損失多型技術的重要價值。Java的要求是讓我們儘可能地採用多型,只有在極特別的情況下才使用RTTI。但為了利用多型,要求我們擁有對基礎類定義的控制權,因為有些時候在程式範圍之內,可能發現基礎類並未包括我們想要的方法。若基礎類來自一個庫,或者由
Time of Update: 2017-02-27
很少需要直接使用反射工具;之所以在語言中提供它們,僅僅是為了支援其他Java特性,比如對象序列化(第10章介紹)、Java Beans以及RMI(本章後面介紹)。但是,我們許多時候仍然需要動態提取與一個類有關的資料。其中特別有用的工具便是一個類方法提取器。正如前面指出的那樣,若檢視類定義源碼或者聯機文檔,只能看到在那個類定義中被定義或覆蓋的方法,基礎類那裡還有大量資料拿不到。幸運的是,“反射”做到了這一點,可用它寫一個簡單的工具,令其自動展示整個介面。下面便是具體的程式:
Time of Update: 2017-02-27
Java用Class對象實現自己的RTTI功能——即便我們要做的只是象造型那樣的一些工作。Class類也提供了其他大量方式,以方便我們使用RTTI。首先必須獲得指向適當Class對象的的一個控制代碼。就象前例示範的那樣,一個辦法是用一個字串以及Class.forName()方法。這是非常方便的,因為不需要那種類型的一個對象來擷取Class控制代碼。然而,對於自己感興趣的類型,如果已有了它的一個對象,那麼為了取得Class控制代碼,可調用屬於Object根類一部分的一個方法
Time of Update: 2017-02-27
迄今為止,我們已知的RTTI形式包括:(1) 經典造型,如"(Shape)",它用RTTI確保造型的正確性,並在遇到一個失敗的造型後產生一個ClassCastException違例。(2) 代表物件類型的Class對象。可查詢Class對象,擷取有用的運行期資料。在C++中,經典的"(Shape)"造型並不執行RTTI。它只是簡單地告訴編譯器將對象當作新類型處理。而Java要執行類型檢查,這通常叫作“型別安全”的下溯造型。之所以叫&
Time of Update: 2017-02-27
為理解RTTI在Java裡如何工作,首先必須瞭解類型資訊在運行期是如何表示的。這時要用到一個名為“Class對象”的特殊形式的對象,其中包含了與類有關的資訊(有時也把它叫作“元類”)。事實上,我們要用Class對象建立屬於某個類的全部“常規”或“普通”對象。對於作為程式一部分的每個類,它們都有一個Class對象。換言之,每次寫一個新類時,同時也會建立一個Class對象(更恰當地說,是儲存在一個完全同名
Time of Update: 2017-02-27
請考慮下面這個熟悉的類結構例子,它利用了多型。常規類型是Shape類,而特別衍生出來的類型是Circle,Square和Triangle。這是一個典型的類結構示意圖,基礎類位於頂部,衍生類向下延展。物件導向編程的基本目標是用大量代碼控制基礎類型(這裡是Shape)的控制代碼,所以假如決定添加一個新類(比如Rhomboid,從Shape衍生),從而對程式進行擴充,那麼不會影響到原來的代碼。在這個例子中,Shape介面中的動態Binder