類與對象是在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多繼承的工具箱里,還有一個內部類的概念。
使用接口來實現俗語“見人說人話,見鬼說鬼話"
新建說人話接口:PersonToneAblepublic interface PersonToneAble {
public abstract void personSpeak();
}
新建說鬼話接口:GhostToneAblepublic 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();
}
}
上面這個例子用到了接口向上溯型的概念,其實跟子類通過父類偽裝是一樣的道理,通過實現的接口來獲得執行此功能或行為的對象。
三、多態
最后再來看一下多態。 多態其實就是通過重載和重寫來實現,它其實就是說在運行時來確定狀態的,上面也已經提到了重載和重寫這兩個概念,這里略過。
綜上可以看到,面向對象其實就是這三個概念的組合,而在實際編程實現時將根據不同的命題使用不同的組合方式,這也就是設計模式(后面會寫到)的使用。而對於面向對象容易出現的誤區就是濫用繼承,這部分內容頁是設計模式這門學科的主要研究話題。