首先講清楚類和對象的區別。 類是廣泛的概念,表示一個有共同性質的群體,而對象指的是具體的一個實實在在的東西。例如,“人”是一個類,它可以表示地球上所有的人;而“張三”、“李四”、“愛因斯坦”等則是一個個的對象,或者說它們是“人”這個類的一個個執行個體。在 Java 中,我們可以定義類,然後建立類的對象。 例如: // 聲明一個類“Human” class Human{ private String name; public String getName(){ return name; } public void setName(String value){ this.name = value; } //...... } 建立一個類: Human human = new Human(); 其次,很多人對對象和對象的引用認識模糊 引用是程式操作對象的控制代碼,相當於C和C++中的指標。 前面說了,對象是一個實實在在的東西,比如前面的代碼: Human human = new Human(); 程式執行到這裡之後,java虛擬機器將會在記憶體中建立一個 Human 對象,並將這個對象的引用賦給 human 變數。這裡有兩步,首先是建立 Human 對象,然後把建立的對象的引用賦給 human 變數。 如果聲明了一個對象的引用,但沒有將對象賦值給它,則這個引用指向了空的對象,或者說引用了不存在的對象。這時如果想通過這個引用訪問對象,則會拋出null 指標異常,例如: Human human; //...... human.setName("張三"); 下面重點談一談類、抽象類別、介面和繼承之間的關係 不少細心的初學者在論壇上問類似這樣的問題: 1、介面不實現方法,但我卻在程式中可以調用介面的方法,這是為什嗎?比如 java.sql 包中的 Connection、Statement、ResultSet 等都是介面,怎麼可以調用 它們的方法呢? 2、抽象類別不能執行個體化,但是jdk中卻有很多抽象類別的對象,這是為什嗎?比如 System.in 是一個 InputStream 類型對象,但 InputStream 是抽象類別,怎麼可以得到它的對象呢? 不管怎麼樣,大家應該明白一點:不管是抽象類別中的抽象方法,還是介面中定義的方法,都是需要被調用的,否則這些方法定義出來就沒有意義了。 可能有很多書上沒有提到,或者提到了而讀者沒有注意到這一點: 一個子類如果繼承了它的基類,則表示這個類也是其基類的一種類型,這個子類的一個對象是子類類型,並且同時也是其基類的一個對象,它也具有基其類的類型;一個類如果實現了一個介面,則表示這個類的一個對象也是這個介面的一個對象。 可能這樣說不太好懂,又是子類、基類、類型、介面什麼的,容易搞混。其實舉個現實的例子你就會覺得其實很簡單: 如果“人”是一個基類,則“男人”是“人”的一個子類。如果“張三”是一個“男人”,也就是說“張三”是“男人”的一個對象,那麼顯然“張三”也是“人”這個基類的一個對象。 明白了這一點,就容易理解為什麼我們可以得到抽象類別的對象了:原來我們得到的抽象類別的對象其實是它的已經實現了抽象方法的子類或子孫類的一個對象,但我們拿它當它的抽象類別的基類來用。比如“人”這個類,每個人都會“悲傷”,男人悲傷的時候抽煙、喝酒,女人悲傷的時候哭泣、流淚。由於不同的子類在“悲傷”時所進行的動作不一樣,因此這個動作(方法)在基類中不好實現,但基類中又需要有這個方法,因此,“人”這個類就可以定義一個抽象方法“悲傷”,由其子類“男人”和“女人”來實現“悲傷”這個方法。但是調用者只把男人和女人的對象當作其基類“人”的一個對象,調用它的“悲傷”方法。 讀者可以去體驗一下 jdk 的抽象類別 java.lang.Process : Runtime runtime = Rumtime.getRuntime(); Process process = rumtime.exec("notepad.exe"); Class cls = process.getClass(); System.out.println(cls.getName()); 這時會列印出 process 類的名字,如果在 Windows 下它會是一個類似於 *Win32* 的名字,它是 Process 的一個子類。因為 process 類用於管理開啟的進程,而在不同的作業系統上都有不同的實現,因此它把方法定義為 Process 的抽象方法,而具體的操作只能由對應在不同作業系統下的子實現。 下面來談介面,我們知道介面只定義了一些方法,而沒有實現這些方法。而其實,介面是一個規範,它規定了實現這個介面所要做的事情,或者說規定了實現介面的類必須具備的能力(也就是方法)。 那麼我們可以這樣對比: 某種類型的駕駛執照,規定了拿到這個駕照的人必須能夠“開小汽車”和“開公用汽車”。那麼我們認為這個駕照是一個介面,它規定了實現它的類所必須有的能力。 我們可以定義一個類 Driver,繼承自 Human,然後實現“駕照持有人”這個介面: public interface DriverHolder{ public void driverCar(); public void driverBus(); } public class Driver extends Human implements DriverHolder{ public void driverCar(){ // ...... } public void driverBus(){ // ...... } } 這樣一來,一個“Driver”對象,它同時也是一個 DrivreHolder 對象。即一個司機(Driver)同時是一個駕照執持有人對象。在程式中我們可以這樣: DriverHolder driverholder = new Driver(); driverholder.driverCar(); 這樣我們就解釋了為什麼“介面沒有實現方法,卻可以得到介面類的對象”的問題。 但是這樣一來,肯定有人會問:為什麼要定義一個介面呢,為什麼不直接把這個方法定義到 Driver 類中去,然後 Driver driver = new Driver(); 一樣可以調用它的 driverCar(); 和 driverBus() 方法,這樣做豈不是方便得多? 這是因為java是單繼承的,它只能繼承於一個類,這樣它的類型就只限於其基類或者基類的基類。但是java可以實現多個介面,這樣它就可以有很多個介面的類型。就象一個人,它繼承自“脊椎動物”這個類,而“脊椎動物”又繼承自“動物”這個類,因此“張三”是個人,他是一個“脊椎動物”,當然他也是一個“動物”。但他可以繼承很多個介面,比如拿駕駛執照之後,他就是“駕照持有人”類型,他也可以拿英語六級認證,這樣他就是一個六級認證持有人等等。 明白這一點之後,我們來看一看 java 的事件機制。 java.awt.Button 類有一個 addActionListener(ActionListener l);方法。這個方法傳入的是一個介面類型:ActionListerner,在實際中,我們需要實現 ActionListener 介面,並且把實現這個介面的類的對象引用作為參數傳入。這樣,Button對象就得到了一個 ActionListener 對象,它知道這個 ActionListener 對象有一個 actionPerformed 方法,或者說它有處理 Action 事件的能力,當 Action 事件發生時,它就可以調用這個對象的 actionPerformed 方法。 比如一般我們會這樣做: public class TestButton extends Frame implements ActionListener{ private Button btn1 = new Button(); //...... public TestButton(){ btn.addActionListener(this); this.add(btn); } public void actionPerformed(ActionEvent e){ } } 現在我們假設 ActionListener 不是介面,而是一個類。那麼我們只能繼承 ActionListener 類,並且重寫 actionPerformed 方法。但是java是單繼承的,如果類繼承了 ActionListener 類,那麼它就不能繼承其它的類(Frame 類)了,而不從 Frame 類繼承的話,又怎麼建立表單,怎麼把 Button 放到表單中去呢? 其實介面不完全是為瞭解決 java 的單繼承問題,它在某種程度上可以達到調用和實現細節的分離。 比如說,中國的民用電規範是一個介面:平均電壓220V、50Hz、Sin 交流電,水力發電廠、火力發電廠、核電廠,還有小型的柴油發電機如果按這個規範發電,則表示它們實現了這個民用電源的介面;冰箱、電視、洗衣機等家用電器使用這些電源,表示它們在調用這個介面。在這裡,家用電器不管電從哪裡來,只要它符合民用電源的規範就好,電源也不管它發的電用於什麼工作,只管提供電源。 再回過頭來看看 Button 的事件機制。要知道,Button 要保證所有的 action 事件發生時,程式員都可以在他的代碼中處理它,圖書館管理系統、收銀系統、進銷存等等等等等等。而介面就可以做到這一點:找一個類實現 ActionListener 介面,並且讓 Button 得到這個類的對象的引用( 調用 addActionListener 方法),從而當Action事件發生時,button 建立一個包含了事件資訊的對象(ActionEvent),然後調用這個介面對象的方法,到底怎麼處理這次事件,這就是實現介面的類的事情了。在這裡,Button 絲毫不瞭解 actionPerformed 方法中到底幹了什麼事情,也不應該知道。Button 與具體的商務邏輯完全分離開了,它可以應用到所有的場合。
|