類與對象是在OO變成裏面經常出現的字眼,簡單的可以理解爲類是一類對象的抽象,對象是某一個類的具體實現。面向對象的編程,其實主要就體現在封裝、繼承與多態這三個方面。下面將逐個的介紹這三個名詞。
一、封裝
面向對象裏面類的提出就是爲了實現封裝,我們將一類同樣的對象的一些相同或相似的屬性及方法抽象出來,封裝在一起就實現了封裝。封裝的目的就是爲了實現一些數據及方法的內部隱藏(通過修飾符來實現),並提供一些通用的方法供外部使用,而具體的內部實現細節無需對外公開,加強了安全性。封裝類要注意一下幾點:
- 類裏面一般都要提供一個或多個構造函數,構造函數可以重載。一旦提供了有參數的構造函數,那麼最好提供一個無參的構造函數,即使是空函數也好。因爲下面講到的繼承發生時,總是先運行父類的構造函數,如果需要運行無參的父類構造函數而找不到的話就會報錯。
- 在類裏面可以通過this關鍵字來指向自身。
- 類裏的屬性一般都設置成private隱藏,如果需要外部訪問或設置,可以通過get/set方法實現。
比如我封裝了一個汽車類Car,供後面使用,示例代碼如下:
package com.mct.main;
public class Car {
protected String name; // 一般不將類的屬性設置爲public,這裏設置爲protected,因爲這兩個屬性肯定是需要被子類繼承的。
protected String color;
public String getName() { // 通過get,set方法讓外部訪問並設置屬性。
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Car(){} // 如果提供了有參數的構造函數,則最好再提供一個無參的構造函數,即使是空函數也好。
public Car(String name, String color){
this.name = name;
this.color = color;
}
public void speedUp(){
System.out.println("運行這裏加速 ");
}
public void speedDown(){
System.out.println("運行這裏減速");
}
}
二、繼承
繼承是使用已經存在的類爲基礎實現新的類,繼承使用extends關鍵字。在Java裏面只允許實現單繼承,子類將完全繼承父類中允許繼承的屬性和方法(具體依據修飾符的訪問權限而定),另外子類也可添加自己的屬性和方法,但不能選擇性的繼承父類。如果父類的某些功能無法滿足子類要求,可以使用下面要講到的覆寫或重載(有些著作中認爲父類與子類之間不存在重載關係)的方法來實現自己特有的方法。關於繼承需要注意一下幾點:
- 子類的構造方法一定會調用父類的構造方法。如果子類沒有顯式的調用父類的構造方法,那麼將默認調用父類的無參方法(這就是上面說的需要添加無參構造的原因)。當然構造方法如果不實現,則系統會默認提供一個無參構造方法。另外子類的構造方法中可以通過super()方法調用父類的某個構造方法,也可以通過this()方法調用本類中的某個構造方法,這兩種方式的調用語句都必須位於構造函數的第一行,所以它們是無法並存的。
- 父類構造方法是無法被繼承的,只能通過在子類中使用super來調用。
- Java中所有的類都直接或間接的繼承自Object類。如上面的Car沒有通過extends關鍵字繼承,那麼系統 會默認給其添加上extends Object,讓它繼承自Object類。
- 子類對象的引用可以通過僞裝賦值給父類對象的引用,無需進行類型轉換;而父類對象引用需要賦值給子類對象時需要經過強制類型轉換,當然前提是這個對象在構造時就是此子類類型,否則運行時肯定會因爲類型不匹配而報錯。這裏對一個對象引用的類型檢查使用 instanceof關鍵字。
- 說到繼承就不得不說一下抽象類和抽象方法,Java中使用abstract關鍵字修飾。抽象方法時一個沒有類體的方法,它用來描述某種功能或行爲,但具體的實現過程需要子類來實現。而抽象類一般都做爲某個功能或行爲的超類出現,抽象類無法實例化對象,只能被繼承。面向對象編程的很多思路都是建立在抽象的基礎上,所以這部分需要仔細的理解體會。
- 這裏再提一下接口,從概念上講它是不屬於繼承範疇的,但是Java不支持多繼承,所以通過接口也可以解決部分多繼承的問題。接口中只能出現抽象類,屬性也必須是靜態常量。Java中的接口常用來實現某個功能,以實現對類的規範以及對行爲的封裝。另外接口還有助於對象的行爲安全性,正所謂對人說人話,對鬼說鬼話,下面會有個例子來實現此功能。
- 在Java多繼承的工具箱裏,還有一個內部類的概念。
- 新建說人話接口:PersonToneAble
public interface PersonToneAble {
public abstract void personSpeak();
}
- 新建說鬼話接口:GhostToneAble
public interface GhostToneAble {
public abstract void ghostSpeak();
}
- 實現一個超人類:SuperMan,他既能說人話,也能說鬼話
public class SuperMan implements PersonToneAble, GhostToneAble {
@Override
public void ghostSpeak() {
// TODO Auto-generated method stub
System.out.println("@#$%*^%");
}
@Override
public void personSpeak() {
// TODO Auto-generated method stub
System.out.println("Hello");
}
}
- 創建一個機構,僱傭一個超人,分別派他去地球和地獄執行任務
public class Agency {
public void gotoEarth(PersonToneAble p){
p.personSpeak();
}
public void gotoEvil(GhostToneAble p){
p.ghostSpeak();
}
public void action(){
SuperMan superMan = new SuperMan();
gotoEarth(superMan);
gotoEvil(superMan);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Agency agency = new Agency();
agency.action();
}
}
上面這個例子用到了接口向上溯型的概念,其實跟子類通過父類僞裝是一樣的道理,通過實現的接口來獲得執行此功能或行爲的對象。