<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 8.2 集合 現在總結一下我們前面學過的東西:為容納一組對象,最適宜的選擇應當是數組。而且假如容納的是一系列基本數據類型,更是必須采用數組。在本章剩下的部分,大家將接觸到一些更常規的情況。當我們編寫程序時,通常并不能確切地知道最終需要多少個對象。有些時候甚至想用更復雜的方式來保存對象。為解決這個問題,Java提供了四種類型的“集合類”:`Vector`(向量)、`BitSet`(位集)、`Stack`(棧)以及`Hashtable`(散列表)。與擁有集合功能的其他語言相比,盡管這兒的數量顯得相當少,但仍然能用它們解決數量驚人的實際問題。 這些集合類具有形形色色的特征。例如,`Stack`實現了一個LIFO(先入先出)序列,而`Hashtable`是一種“關聯數組”,允許我們將任何對象關聯起來。除此以外,所有Java集合類都能自動改變自身的大小。所以,我們在編程時可使用數量眾多的對象,同時不必擔心會將集合弄得有多大。 ## 8.2.1 缺點:類型未知 使用Java集合的“缺點”是在將對象置入一個集合時丟失了類型信息。之所以會發生這種情況,是由于當初編寫集合時,那個集合的程序員根本不知道用戶到底想把什么類型置入集合。若指示某個集合只允許特定的類型,會妨礙它成為一個“常規用途”的工具,為用戶帶來麻煩。為解決這個問題,集合實際容納的是類型為`Object`的一些對象的引用。這種類型當然代表Java中的所有對象,因為它是所有類的根。當然,也要注意這并不包括基本數據類型,因為它們并不是從“任何東西”繼承來的。這是一個很好的方案,只是不適用下述場合: (1) 將一個對象引用置入集合時,由于類型信息會被拋棄,所以任何類型的對象都可進入我們的集合——即便特別指示它只能容納特定類型的對象。舉個例子來說,雖然指示它只能容納貓,但事實上任何人都可以把一條狗扔進來。 (2) 由于類型信息不復存在,所以集合能肯定的唯一事情就是自己容納的是指向一個對象的引用。正式使用它之前,必須對其進行轉換,使其具有正確的類型。 值得欣慰的是,Java不允許人們濫用置入集合的對象。假如將一條狗扔進一個貓的集合,那么仍會將集合內的所有東西都看作貓,所以在使用那條狗時會得到一個“異常”錯誤。在同樣的意義上,假若試圖將一條狗的引用“轉換”到一只貓,那么運行期間仍會得到一個“異常”錯誤。 下面是個例子: ``` //: CatsAndDogs.java // Simple collection example (Vector) import java.util.*; class Cat { private int catNumber; Cat(int i) { catNumber = i; } void print() { System.out.println("Cat #" + catNumber); } } class Dog { private int dogNumber; Dog(int i) { dogNumber = i; } void print() { System.out.println("Dog #" + dogNumber); } } public class CatsAndDogs { public static void main(String[] args) { Vector cats = new Vector(); for(int i = 0; i < 7; i++) cats.addElement(new Cat(i)); // Not a problem to add a dog to cats: cats.addElement(new Dog(7)); for(int i = 0; i < cats.size(); i++) ((Cat)cats.elementAt(i)).print(); // Dog is detected only at run-time } } ///:~ ``` 可以看出,`Vector`的使用是非常簡單的:先創建一個,再用`addElement()`置入對象,以后用`elementAt()`取得那些對象(注意`Vector`有一個`size()`方法,可使我們知道已添加了多少個元素,以便防止誤超邊界,造成異常錯誤)。 `Cat`和`Dog`類都非常淺顯——除了都是“對象”之外,它們并無特別之處(倘若不明確指出從什么類繼承,就默認為從`Object`繼承。所以我們不僅能用`Vector`方法將`Cat`對象置入這個集合,也能添加`Dog`對象,同時不會在編譯期和運行期得到任何出錯提示。用`Vector`方法`elementAt()`獲取原本認為是`Cat`的對象時,實際獲得的是指向一個`Object`的引用,必須將那個對象轉換為`Cat`。隨后,需要將整個表達式用括號封閉起來,在為`Cat`調用`print()`方法之前進行強制轉換;否則就會出現一個語法錯誤。在運行期間,如果試圖將`Dog`對象轉換為`Cat`,就會得到一個異常。 這些處理的意義都非常深遠。盡管顯得有些麻煩,但卻獲得了安全上的保證。我們從此再難偶然造成一些隱藏得深的錯誤。若程序的一個部分(或幾個部分)將對象插入一個集合,但我們只是通過一次異常在程序的某個部分發現一個錯誤的對象置入了集合,就必須找出插入錯誤的位置。當然,可通過檢查代碼達到這個目的,但這或許是最笨的調試工具。另一方面,我們可從一些標準化的集合類開始自己的編程。盡管它們在功能上存在一些不足,且顯得有些笨拙,但卻能保證沒有隱藏的錯誤。 (1) 錯誤有時并不顯露出來 在某些情況下,程序似乎正確地工作,不轉換回我們原來的類型。第一種情況是相當特殊的:`String`類從編譯器獲得了額外的幫助,使其能夠正常工作。只要編譯器期待的是一個`String`對象,但它沒有得到一個,就會自動調用在`Object`里定義、并且能夠由任何Java類覆蓋的`toString()`方法。這個方法能生成滿足要求的`String`對象,然后在我們需要的時候使用。 因此,為了讓自己類的對象能顯示出來,要做的全部事情就是覆蓋`toString()`方法,如下例所示: ``` //: WorksAnyway.java // In special cases, things just seem // to work correctly. import java.util.*; class Mouse { private int mouseNumber; Mouse(int i) { mouseNumber = i; } // Magic method: public String toString() { return "This is Mouse #" + mouseNumber; } void print(String msg) { if(msg != null) System.out.println(msg); System.out.println( "Mouse number " + mouseNumber); } } class MouseTrap { static void caughtYa(Object m) { Mouse mouse = (Mouse)m; // Cast from Object mouse.print("Caught one!"); } } public class WorksAnyway { public static void main(String[] args) { Vector mice = new Vector(); for(int i = 0; i < 3; i++) mice.addElement(new Mouse(i)); for(int i = 0; i < mice.size(); i++) { // No cast necessary, automatic call // to Object.toString(): System.out.println( "Free mouse: " + mice.elementAt(i)); MouseTrap.caughtYa(mice.elementAt(i)); } } } ///:~ ``` 可在`Mouse`里看到對`toString()`的重定義代碼。在`main()`的第二個`for`循環中,可發現下述語句: ``` System.out.println("Free mouse: " + mice.elementAt(i)); ``` 在`+`后,編譯器預期看到的是一個`String`對象。`elementAt()`生成了一個`Object`,所以為獲得希望的`String`,編譯器會默認調用`toString()`。但不幸的是,只有針對`String`才能得到象這樣的結果;其他任何類型都不會進行這樣的轉換。 隱藏轉換的第二種方法已在`Mousetrap`里得到了應用。`caughtYa()`方法接收的不是一個`Mouse`,而是一個`Object`。隨后再將其轉換為一個`Mouse`。當然,這樣做是非常冒失的,因為通過接收一個`Object`,任何東西都可以傳遞給方法。然而,假若轉換不正確——如果我們傳遞了錯誤的類型——就會在運行期間得到一個異常錯誤。這當然沒有在編譯期進行檢查好,但仍然能防止問題的發生。注意在使用這個方法時毋需進行轉換: ``` MouseTrap.caughtYa(mice.elementAt(i)); ``` (2) 生成能自動判別類型的`Vector` 大家或許不想放棄剛才那個問題。一個更“健壯”的方案是用`Vector`創建一個新類,使其只接收我們指定的類型,也只生成我們希望的類型。如下所示: ``` //: GopherVector.java // A type-conscious Vector import java.util.*; class Gopher { private int gopherNumber; Gopher(int i) { gopherNumber = i; } void print(String msg) { if(msg != null) System.out.println(msg); System.out.println( "Gopher number " + gopherNumber); } } class GopherTrap { static void caughtYa(Gopher g) { g.print("Caught one!"); } } class GopherVector { private Vector v = new Vector(); public void addElement(Gopher m) { v.addElement(m); } public Gopher elementAt(int index) { return (Gopher)v.elementAt(index); } public int size() { return v.size(); } public static void main(String[] args) { GopherVector gophers = new GopherVector(); for(int i = 0; i < 3; i++) gophers.addElement(new Gopher(i)); for(int i = 0; i < gophers.size(); i++) GopherTrap.caughtYa(gophers.elementAt(i)); } } ///:~ ``` 這前一個例子類似,只是新的`GopherVector`類有一個類型為`Vector`的`private`成員(從`Vector`繼承有些麻煩,理由稍后便知),而且方法也和`Vector`類似。然而,它不會接收和產生普通`Object`,只對`Gopher`對象感興趣。 由于`GopherVector`只接收一個`Gopher`(地鼠),所以假如我們使用: ``` gophers.addElement(new Pigeon()); ``` 就會在編譯期間獲得一條出錯消息。采用這種方式,盡管從編碼的角度看顯得更令人沉悶,但可以立即判斷出是否使用了正確的類型。 注意在使用`elementAt()`時不必進行轉換——它肯定是一個`Gopher`。 (3) 參數化類型 這類問題并不是孤立的——我們許多時候都要在其他類型的基礎上創建新類型。此時,在編譯期間擁有特定的類型信息是非常有幫助的。這便是“參數化類型”的概念。在C++中,它由語言通過“模板”獲得了直接支持。至少,Java保留了關鍵字`generic`,期望有一天能夠支持參數化類型。但我們現在無法確定這一天何時會來臨。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看