JAVA程序員必讀:基礎篇(2)面向對象編程概念 如果你以前從來沒有使用面向對象語言,你需要在開始編寫JAVA代碼之前先理解這個概念。你需要理解什么是對象、什么是類、對象和類的關系怎樣以及使用消息怎樣在對象之間進行通訊。本教程的前面部分將描述面向對象編程的概念,而后面的教程將教你怎樣將這個概念編成代碼。 2.1什么是對象 對象是一些相關的變量和方法的軟件集。軟件對象經常用于模仿現實世界中我們身邊的一些對象。對象是理解面向對象技術的關鍵。你在學習之前可以看看現實生活中的對象,比如狗、桌子、電視、自行車等等。你可以發現現實世界中的對象有兩個共同特征:它們都有狀態和行為。比如狗有自己的狀態(比如名字、顏色、生育以及饑餓等等)和行為(比如搖尾巴等等)。同樣自行車也有自己的狀態(比如當前檔位、兩個輪子等等)和行為(比如剎車、加速、減速以及改變檔位等等)。 而軟件對象實際上是現實世界對象的造型,因為它同樣有狀態和行為。一個軟件對象利用一個或者多個變量來維持它的狀態。變量是由用戶標識符來命名的數據項。軟件對象用它的方法來執行它的行為。方法是跟對象有關聯的函數(子程序)。 你可以利用軟件對象來代表現實世界中的對象。你可能想用一個動畫程序來代表現實世界中的狗,或者用可以控制電子自行車的程序來代表現實世界的自行車。同樣你可以使用軟件對象來造型抽象的概念,比如,事件是一個用在GUI窗口系統的公共對象,它可以代表用戶按下鼠標按鈕或者鍵盤上的按鍵的反應。 如圖1是一個軟件對象的公共可視代表。 ![]() (圖1) 軟件對象的狀態和行為都可以用在對象中的變量和方法來表達。構造現實世界的自行車的軟件對象要有指示自行車的當前狀態的變量:速度為20mph,它的當前檔位為第三檔。這些變量就是我們熟知的實例變量,因為它們包含了用于特殊自行車對象的狀態,并且在面向對象技術中,特殊的對象稱為實例。 如圖2所示,是作為軟件對象的自行車造型。 ![]() (圖2) 除了變量,軟件自行車同樣有用于剎車、改變踏板步調以及改變檔位的方法。這些方法就是熟知的實例方法因為它們檢查或者改變特殊自行車實例的狀態。 以上的對象圖顯示了對象的變量組成了圓心部分。方法處在變量的四周并且在程序中從其它對象隱藏了對象的核心。用保護方法的方法來包裝對象的變量稱為封裝。這個對象圖就是對象理想的表示法,也是面向對象系統設計者努力的最后目標。然而這并不是全部的內容。通常,出于某種現實的理由,對象可能暴露它的一些變量或者隱藏一些方法。在JAVA編程語言中,一個對象可以為變量和方法指定四種訪問等級中的一種。這個訪問等級決定哪個對象和類可以訪問變量或者方法。在JAVA中訪問變量和方法可以轉換為控制訪問類的成員函數。封裝相關的變量和方法到一個簡潔的軟件集是一個簡單而且強有力的方法,它為軟件開發者提供了兩個主要好處: 模塊性:對象的源代碼可以獨立于其它的對象源代碼來進行編寫和維護。同樣,對象可以很容易地在系統中傳遞。你可以將你的自行車對象給其它的對象,它仍然可以正常工作。 信息隱藏:一個對象如果有一個公共的界面,那么其它的對象就可以與之進行通訊。這個對象可以維護私人的信息和方法,它可以在任何時候被改變而不影響依耐于它的其它對象。所以你不必理解自行車中齒輪的機理就可以使用它。 2.2什么是消息 軟件對象之間進行交互作用和通訊是利用消息的。 單一的一個對象通常不是很有用的。相反,一個對象通常是一個包含了許多其它對象的更大的程序或者應用程序。通過這些對象的交互作用,程序員可以獲得高階的功能以及更為復雜的行為。你的自行車如果不使用它的時候,它就是一堆鋁合金和橡膠,它沒有任何的活動。而只有當有其它的對象來和它交互的時候才是有用的。 軟件對象與其它對象進行交互與通訊是利用發送給其它對象來實現的。當對象A想對象B來執行一個B中的方法,對象A就會消息給對象B。如圖3所示。 ![]() (圖3) 有時候,接收的對象需要更多的信息就至于它可以正確知道該如何做。比如,當你想改變自行車的齒輪,你就必須指出哪個齒輪。這個信息是將信息作為參數來傳遞的。如圖4所示的現實了一個信息由三個組件組成: - 被尋址消息的對象(YourBicycle)
- 要執行方法的名字(changeGears)
- 這個方法需要的所有參數(lowerGear)
![]() (圖4) 上面的三個組件對于接收方的對象執行相應的方法是給出了充分的信息。再也不需要其它的信息或者上下文了。 消息提供了兩個重要的好處: - 對象的行為是通過它的方法來表達的,因此消息傳遞支持所有在對象之間的可能的交互。
- 對象不需要在相同的進程或者相同的機器上來發送和接收消息給其它的對象
2.3什么是類 類實際上是對某種類型的對象定義變量和方法的原型。 在現實世界中,你經常看到相同類型的許多對象。比如 ,你的自行車只是現實世界中許多自行車的其中一輛。使用面向對象技術,我們可以說你的自行車是自行車對象類的一個實例。通常,自行車有一些狀態(當前檔位、兩個輪子等等)以及行為(改變檔位、剎車等等)。但是,每輛自行車的狀態都是獨立的并且跟其它自行車不同。 當廠家制造自行車的時候,廠商利用了自行車共有的特性來根據相同的藍圖制造許多自行車。如果制造一輛自行車就要產生一個新藍圖,那效率就太低了。 在面向對象軟件中,同樣地,可以讓相同種類地許多對象來共有一些特性,比如矩形、雇員記錄、視頻夾等等。就象自行車制造商人,你可以利用相同種類的對象是相似的事實并且你可以為這些對象創建一個藍圖。對對象的軟件藍圖叫做類。 自行車的類需要定義一些實例變量來包括當前檔位、當前速度等等。這個類將為實例方法定義和提供實施方法,它允許騎車者改變檔位、剎車以及改變腳踏板的節奏,如圖5所示: ![]() (圖5) 當你創建了自行車類以后,你可以從這個類創建任意個自行車對象。當你創建了一個類的實例后,系統將為這個對象和的實例變量分配內存。每個實例將給所有實例變量的副本定義在類中。如圖6所示: ![]() (圖6) 除了實例變量,類還要定義類的變量。類變量包含了被類所有實例共享的信息。比如,假設所有的自行車有相同的檔位數。在本例子中,要定義一個實例變量來容納檔位數。每一個實例都會有變量的副本,但是在每一個實例中數值都是相同的。在這樣的情況下,你可以定義一個類變量來包含檔位數,這樣所有的類的實例都共享這個變量。如果一個對象改變了變量,它就為改變那個類的所有對象。類同樣可以定義類方法。你可以直接從類中調用類方法,然而你必須在特定的實例中調用實例方法。如圖7所示。 ![]() (圖7)
2.4實例和類成員 2.4.1理解實例和類成員 下面詳細討論一下實例和類成員,具體涉及變量和方法以及類變量和方法: 你這樣聲明一個成員變量,比如在類Myclass中有一個float型的aFloat: class MyClass { float aFloat; } 這樣你就聲明一個實例變量。每次你創建一個類的實例的時候,系統就為實例創建了類的每一個實例變量的副本。你可以從對象中訪問對象的實例變量。 實例變量跟類變量是不一樣的,類變量示使用靜態修改量來聲明的。不管類創建了多少個實例,系統為每個類變量分配了類變量。系統為類變量分配的內存是在它第一次調用類的時候發生的。所有的實例共享了類的類變量的相同副本。你可以通過實例或者通過類本身來訪問類變量。 它們的方法是類似的:你的類可以有實例方法和類方法。實例方法是對當前對象的實例變量進行操作的,而且訪問類變量。另外一個方法,類方法不能訪問定義在類中的實例變量,除非它們創建一個新的對象并通過對象來訪問它們。同樣,類方法可以在類中被調用,你不必需要一個實例來調用一個類方法。 缺省地,除非其它的成員被指定,一個定義在類中成員就是一個實例成員。這個在下面定義的類有一個實例變量,有一個整型的x,兩個實例方法x和setX,它們設置其它對象以及查詢x的數值。 class AnIntegerNamedX { int x; public int x() { return x; } public void setX(int newX) { x = newX; } } 每次你從一個類實例化一個新的對象,你可以得到每個類的實例變量的副本。這些副本都是跟新對象有關系的。因此,每次你從這個類實例化一個新的AnIntegerNamedX對象的時候,你得以得到跟新的AnIntegerNamedX對象有關的新副本。 一個類的所有實例共享一個實例方法的相同的實行;所有的AnIntegerNamedX實例都共享x和setX的相同執行。這里注意,兩個方法x和setX是指對象的實例變量x。但是,你可能會問:如果所有AnIntergerNamedX共享x和setX的相同執行,會不會造成模棱兩可的狀態?答案當然是:不是。在實例方法中,實例變量的名字是指當前對象的實例變量,假如實例變量不是由一個方法參數來隱藏的。這樣在x和setX中,x就等價于這個x,而不會造成混亂。 對于AnIntegerNamedX外部的對象如果想訪問x,它必須通過特定的AnIntegerNamedX的實例來實現。假如這個代碼片段處在其它對象的方法中。它創建了兩種不同類型的AnIntegerNamedX,它設置了x為不同的數值,然后顯示它們: AnIntegerNamedX myX = new AnIntegerNamedX(); AnIntegerNamedX anotherX = new AnIntegerNamedX(); myX.setX(1); anotherX.x = 2; System.out.println("myX.x = " + myX.x()); System.out.println("anotherX.x = " + anotherX.x()); 這里注意,代碼使用setX來為myX設置x的值,而直接給anotherX.x指定一個數值。不管用什么方法,代碼是在操作兩個不同的x副本:一個包含在myX對象中一,另外一個包含在anotherX對象中。其輸出是用以下代碼片段來實現的: myX.x = 1 anotherX.x = 2 上面代碼顯示了類AnIntegerNamedX的每一個實例有自己實例變量x的副本以及每個x有自己的數值。 你可以在聲明成員變量的時候,指定變量是一個類變量而不是一個實例變量。相似地,你可以指定方法是一個類方法而不是一個實例方法。系統在第一次調用類來定義變量的時候創建了一個類變量的副本。所有的類實例共享了類變量的相同副本。類方法可以只操作類變量,它們不能訪問定義在類中的實例變量。 為了指定一個成員變量為一個類變量,你可以使用static關鍵字。比如,我們可以修改一下上面的AnIntegerNamedX類,使得x變量現在是一個類變量: class AnIntegerNamedX { static int x; public int x() { return x; } public void setX(int newX) { x = newX; } } 現在設置它們的x數值并顯示不同的輸出: myX.x = 2 anotherX.x = 2 這次的輸出不同,是因為x現在是一個類變量,所以就只有這個變量的副本,它是被AnIntegerNamedX的所有實例所共享的,包括myX和anotherX。當你在其它實例中調用setX的時候,你可以為所有的AnIntegerNamedX的所有實例改變x的數值。 同樣,當我們聲明一個方法的時候,你可以指定方法為類方法而不是實例方法。類方法只可以在類變量中進行操作,并且不能訪問定義在類中的所有實例變量。 為了指定方法為類方法,你可以在方法聲明處使用static關鍵字。下面,我們再次來修改AnIntegerNamedX類,使它的成員變量x為一個實例變量,以及它的兩個方法為類方法: class AnIntegerNamedX { int x; static public int x() { return x; } static public void setX(int newX) { x = newX; } } 當你想編譯這個版本的AnIntegerNamedX,編譯器就會顯示如下的錯誤: AnIntegerNamedX.java:4: Can't make a static reference to nonstatic variable x in class AnIntegerNamedX. return x; ^ 出現這些錯誤的原因是類方法不能訪問實例變量,除非方法先創建AnIntegerNamedX的一個實例并且通過它來訪問變量。 下面我們修改一下AnIntegerNamedX,讓x變量成為類變量: class AnIntegerNamedX { static int x; static public int x() { return x; } static public void setX(int newX) { x = newX; } } 現在為x設置數值,并打印出x數值: myX.x = 2 anotherX.x = 2 再一次,我們通過myX來改變x,并將它改變為AnIntegerNamedX的其它實例。 實例成員和類成員之間的另外一個差別是類成員可以從類本身進行訪問。你不必實例化類來訪問它的類成員。下面讓我們編寫一段代碼來直接從AnIntegerNamedX類中訪問x和setX: . . . AnIntegerNamedX.setX(1); System.out.println("AnIntegerNamedX.x = " + AnIntegerNamedX.x()); . . . 值得一提的是,你現在已經不用再創建myX和anotherX了。你可以設置x并直接AnIntegerNamedX類中檢索x。你不能利用實例成員來處理它,你只能從一個對象來調用實例方法并且只可以從對象中訪問實例變量。而你可以從類的實例或者從類本身來訪問類變量和方法。 2.4.2初始化實例和類成員 下面講講初始化實例和類成員: 你可以在類中定義它們的時候,使用static初始化程序和實例初始化程序來為類和實例成員提供初始化數值: class BedAndBreakfast { static final int MAX_CAPACITY = 10; boolean full = false; } 這個對于原始數據類型是沒有問題的。有時候,它可以用在創建數組和對象。但是這個初始化表單有它的限制,如下: - 初始化程序只可以執行用賦值語句表達的初始化 。
- 初始化程序不能調用任何的包含異常錯誤的方法。
- 如果初始化程序調用一個包含異常錯誤的方法,它不能進行錯誤恢復。
如果你有一些初始化要完成,可能有些不能在初始化程序實現,因為出現了上面的限制之一,這時你不得不將初始化代碼隨意放置了。為了初始化類成員,在static初始化塊中放置初始化代碼。為了初始化實例成員,就要在構造函數中放置初始化代碼了。 2.4.3 Static初始化塊 下面再講講Static初始化塊 下面舉一個例子,如圖8所示: ![]() (圖8) errorStrings源代碼必須在static初始化塊中被初始化。這是因為錯誤恢復必須在源代碼沒有被找到得時候才被執行。同時,errorStrings是一個類成員,它不能在構造函數中被初始化。在前面得例子中一,一個static初始化塊是以static關鍵字開頭得,并且JAVA代碼是用大括號“{}”括起來的。 一 個類可以有許多static初始化塊,它可以出現在類中任何地方。系統保證static輸出化塊以及static初始化程序是按它們在源代碼中的順序被調用的。
2.4.4 初始化實例成員 如果你想初始化一個實例變量而且不能在變量聲明處來處理它,那么就只能在構造函數中來為這個類初始化了。假如errorStrings是一個實例變量而不是一個類變量,你就可以使用以下的代碼來初始化它: import java.util.ResourceBundle; class Errors { ResourceBundle errorStrings; Errors() { try { errorStrings = ResourceBundle. getBundle("ErrorStrings"); } catch (java.util.MissingResourceException e) { // error recovery code here } } } 現在代碼是在構造函數中為類來初始化這個errorStrings的。 有時,類包含了許多構造函數并且每個構造函數允許調用者為新對象的不同實例變量提供初始化數值。比如,java.awt.Rectangle有以下的三個構造函數: Rectangle(); Rectangle(int width, int height); Rectangle(int x, int y, int width, int height); Rectangle()構造函數沒有任何的參數,所以它不能讓用戶大小或者原點和大小提供初始化數值;而其它的兩個構造函數,它可以讓用戶設置初始數值。 然而,所有的實例變量(原點和大小)都必須初始化。在這個例子中,類經常有一個構造函數來完成所有的初始化。其它的構造函數調用這個構造函數并且提供給它參數或者缺省數值。比如下面是以上所說的三個構造函數,它們初始化如下: Rectangle() { this(0,0,0,0); } Rectangle(int width, int height) { this(0,0,width,height); } Rectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } JAVA語言支持實例初始化塊,你可以放心使用它。這里建議使用構造函數來初始化,主要有以下三個原因: - 所有的初始化代碼處在一個地方,這樣使得代碼更容易維護和閱讀。
- 缺省值可以清除地知道。
- 構造函數廣泛被JAVA程序設計人員所熟悉,包括相對新的JAVA程序設計人員,而實例初始化程序不能,而且他可能導致其它JAVA程序設計員的困惑。
2.4.5 對象和類 你可能會注意到對象和類看起來很相似。在現實世界中,類和對象之間的區別經常是讓程序員困惑的源泉。在現實世界中,很明顯,類不能是它們描述的對象本身。然而,在軟件中很困難來區分類和對象。有部分原因是軟件對象只是現實世界中的電子模型或者是抽象概念。但是也因為對象通常有時是指類和實例。 2.5什么是繼承 一個類可以從它的父類繼承狀態和行為。繼承為組織和構造軟件程序提供了一個強大的和自然的機理。 總得說來,對象是以類得形式來定義得。你可能現在已經可以從它類知道許多對象了。即使你如知道,如果我告訴你它是一輛自行車,你就會知道它有兩個輪子和腳踏板等等。面向對象系統就更深入一些了,它允許類在其它類中定義。比如,山地自行車、賽車以及串座雙人自行車都是各種各樣的自行車。在面向對象技術中,山地自行車、賽車以及串座雙人自行車都是自行車類的子類。同樣地,自行車類是山地自行車、賽車以及串座雙人自行車的父類。這個父子關系可以如圖9所示: ![]() (圖9) 每一個子例從父類中繼承了狀態。山地自行車、賽車以及串座雙人自行車共享了這些狀態:速度等。同樣,每一個子類繼承類從父類的方法,山地自行車、賽車以及串座雙人自行車共享了這些行為:剎車、改變腳踏速度等等。 然而,子類不能受到父類提供的狀態和行為的限制。子類可以增加變量和方法到從父類繼承而來的變量和方法。比如,串座雙人自行車有兩個座位,這是它的父類沒有的。 子類同樣可以重載繼承的方法并且為這些方法提供特殊執行方法。比如 ,如果你有一個山地自行車有額外 的齒輪設置,你就可以重載改變齒輪方法來使騎車者可以使用這些新的齒輪。 你也不能受限于繼承的一個層次。繼承樹或者類的分級結構可以是很深。方法和變量是逐級繼承的。總的來說,在分級結構的越下方,就有越多的行為。 如果對象類處于分級結構的頂端,那么每個類都是它的后代(直接地或者是間接地)。一種類型的對象保留任何對象的一個引用,比如類或者數組的一個實例。對象提供了行為,這些行為是運行在JAVA虛擬機所需要的。比如,所有類繼承了對象的toString方法,它返回了代表對象的字符串。 下面說說我們為什么要使用繼承,它到底有哪些好處呢?好處是有的: - 子類提供了特殊的行為,這是在父類中所沒有的。通過使用繼承,程序員可以多次重新使用在父類中的代碼。
- 程序員可以執行父類(稱為抽象類)來定義總的行為。這個抽象的父類可以定義并且部分執行行為,但是絕大多數的父類是未定義和未執行的。其它的部分由程序員來實現特殊的子類。
2.6什么是接口 接口是一個收集方法和常數表單的契約。當類執行一個接口,它就許諾聲明在那個接口中執行所有的方法。 接口是一個設備或者一個系統,它是用于交互的無關的實體。根據這個定義,遠程控制是一個在你和電視的接口;而英語是兩個人之間的接口;強制在軍事中的行為協議是不同等價人之間的接口。在JAVA語言中,接口是一個設備,它是用來與其它對象交互的設備。一個接口可能對一個協議是類似的。實際上,其它面向對象語言有接口的功能,但它們調用它們的接口協議。 自行車類和它的類分級結構定義了什么是自行車。但是自行車在其它方面與現實世界交互作用,例如,倉庫中的自行車可以由一個存貨程序來管理。一個存貨程序不關心管理項目的哪一類只要項目提供某一信息,比如價格和跟蹤數字。取代強迫類與其它無關項的關系,存貨程序建立了通訊的協議。這個協議是由包含在接口中的常數和方法定義組成的。這個存貨清單接口將要定義(但不執行)方法來設置和得到零售價格,指定跟蹤數字等等。 為了在存貨清單程序中操作,自行車類必須在執行接口的時候遵守這個協議。當一個了執行一個接口的時候,類遵守定義在接口中的所有方法。因此,自行車將為這些設置和獲得零售價格并指定跟蹤數值等等的方法提供執行。 你可以使用接口來定義一個行為的協議,這個行為可以有在類分級結構中任何類來執行。接口的主要好處有一下幾點: - 不用人工強迫類關系在無關類中截獲相似處。
- 聲明想執行的一個或者更多類的方法。
- 在不暴露對象的類的前提下,暴露對象的編程接口。
2.7怎樣將這些面向對象的概念轉換為代碼 2.7.1ClickMe的源代碼和Applet標簽 這一小節將給你展現創建對象、執行類、發送消息、創建一個父類以及執行一個接口的代碼。 以下是一個applet(applet是用JAVA編程語言編寫的程序,它可以運行在兼容JAVA平臺的網絡瀏覽器,比如HotJava或者Netscape Navigator)的程序,名為ClickMe。如圖10所示,當你點擊方框內任何地方,一個紅點就會出現。 ![]() (圖10) 提示:上面的applet需要JDK1.1。如果你使用老的不支持JDK1.1的瀏覽器,你將不能運行這個applet。相反,你需要在一個1.1瀏覽器上來看這個網頁,比如在HotJava、JDK Applect瀏覽器(appletviewer)或者某個版本的Netscape Navigator和Internet Explorer。 下面具體解釋一下這個Applet。 ClickMe Applet是一個相對簡單的程序因此它的代碼就短了多了。但是,如果你還沒有太多的編程經驗,你可以發現這些代碼也不是那么容易的。我們不要求你馬上理解程序中的每個問題,并且這節教程也不是講了十分詳細的。這里的目的示暴露一些源代碼給你并且跟你剛才所學道的概念和技術聯系上。你將在以后的教程中學到更詳細的內容。 2.7.1ClickMe的源代碼和Applet標簽 為了編譯這個applet你需要兩個源文件:ClickMe.java和Spot.java。為了運行這個applet你需要利用這個applet標簽來創建一個html文件: 然后裝載網頁到瀏覽器或者appletviewer工具。并且確保所有必要的文件都在相同的目錄中。 如圖11所示: ![]() (圖11)
2.7.2 ClickMe Applet中的對象 在這個applet中有許多對象。兩個最明顯的是:applet本身和紅點。 瀏覽器在包含applet的HTML代碼中碰到applet標簽的時候就創建了applet對象。這個applet標簽從創建applet對象的地方提供類的名字。在本例子中,這個類的名字為ClickMe。 ClickME.applet將創建一個對象來在屏幕上畫出點。每次你在applet中點擊鼠標的時候,applet就將通過改變對象的x和y坐標來移動紅點。這個點不是自己畫出來的,它是applet畫出的,它是根據包含在點對象中的信息畫出的。 除了前面兩個明顯的對象,另外還有一些看不見的對象呢。有代表三種顏色(黑、白、紅)的三個對象以及代表點擊鼠標的用戶動作的事件對象等等。
2.7.3ClickMe Applet中的類 因為代表在屏幕上點的對象是很簡單,接下來讓我們看看這個名為spot的類吧。它聲明了三個實例變量:包括點半徑的size,包含點當前水平位置的x坐標以及包含點當前垂直位置的y坐標: public class Spot { //實例變量 public int size; public int x, y; //構造函數 public Spot(int intSize) { size = intSize; x = -1; y = -1; } } 另外,類有一個構造函數,它用來初始化由類創建的新對象。構造函數跟類有相同的名字。這個構造函數初始化所有三個對象的變量。Size的初始化數值是在調用的時候座位參數提供的。x和y變量都被設置為-1,這里-1的目的是為了讓點在開始的時候處于屏幕的外面,即產生假不可視的效果。 這個applet是在applet初始化的時候創建了一個新的點對象。下面是applet類的相關代碼: private Spot spot = null; private static final int RADIUS = 7; ... spot = new Spot(RADIUS); 第一行聲明了一個名為spot的變量,它是Spot數據類型,并且初始化這個變量為NULL。第二行聲明了一個整型變量,名為RADIUS,它的值為7。最后一行是創建一個對象。New關鍵字為對象分配了內存空間。Spot(RADIUS)調用了上面已經描述了的構造函數并且傳遞RADIUS數值,這樣點對象的size就被設置為7。如圖12所示的左圖代表了Spot類,而右邊的是代表了spot對象。 ![]() (圖12) 2.7.4ClickMe Applet中的消息 就如所知道的,對象A可以使用消息來請求對象B做一些事情,一個消息有三個組成部分: - 消息被尋址的對象
- 要執行執行方法的名字
- 方法需要的任何參數
在ClickMe applet種有以下兩行這樣的代碼: g.setColor(Color.white); g.fillRect(0, 0, getSize().width - 1, getSize().height - 1); 這兩個消息都是從applet到名為g的對象。其中g是一個圖形對象,它知道怎樣在屏幕上簡單畫一些形狀或者文本。這個對象在瀏覽器指示applet來畫的時候提供了applet。上面代碼的第一行設置顏色為白色,第二行是填充一個指定大小的矩形區域,它的顏色為白色。如圖13所示,是一個消息的三個組成部分: ![]() (圖13)
2.7.5 ClickMe Applet中的繼承 為了在瀏覽器種運行,對象必須是一個applet。這就意味著對象必須是類的一個實例,這個類是從由JAVA平臺提供的Applet類派生而來的。 ClickMe applet對象是一個ClickMe類的一個實例,它是這樣聲明的: public class ClickMe extends Applet implements MouseListener { ... } 上面的語句就產生了Applet的一個子類。ClickMe繼承了父類的許多功能,包括初始化、由瀏覽器來開始和結束、在瀏覽器區域畫圖以及對接收到的鼠標事件注冊。除了有了這些功能,ClickMe類還要實現以下的功能:它的畫圖代碼在paint的方法中,初始化代碼必須在init方法中等等。 public void init() { ... // 這里加入ClickMe的初始化代碼 } public void paint(Graphics g) { ... // 這里加入ClickMe的畫圖代碼 } 2.7.6 ClickMe Applet中的接口 ClickMe applet是通過在鼠標點擊出顯示一個紅點來響應鼠標的點擊事件。如果對象想通知鼠標點擊,JAVA平臺事件系統需要對象執行MouseListener接口。這個對象必須同時作為鼠標監聽器來注冊。 這個MouseListener接口聲明了五種不同的志芋工,每種方法是用在鼠標被點擊的時候對不同鼠標事件的調用。當鼠標被點擊的時候,或者當鼠標移動到applet外面的時候等等。 下面是ClickMe applet完整的代碼。其中mousePressed是處理鼠標事件的: 2.8 面向對象概念的問題和練習 本節教程測試一下你對對象、類、消息等等的理解,我們是通過做一些練習以及回答一些問題來進行的。 2.8.1 問題 你可以使用API文檔來回答這些問題: - ClickMe applet使用Color.red來設置畫圖顏色為紅色。其它有什么顏色可以象這樣來使用?
- 怎樣設置顏色為紫色(purple)?
2.8.2 練習 現在,利用你從API文檔中學到的知識來修改ClickMe applet。為了編譯這個程序,你同樣需要Spot.java文件。具體修改如下: - 修改這個applet來畫一個綠色的方框而不是一個紅點。
- 修改這個applet來用紫色顯示你的名字而不是一個紅點。
|