抽象類只是在普通類的基礎上擴充了一些抽象方法而已,所謂的抽象方法指的是隻聲明而未實現的方法(即沒有方法體)。
所有抽象方法要求使用abstract關鍵字來定義,並且抽象方法所在的類也一定要使用abstract關鍵字來定義,表示抽象類。
由於抽象方法沒有具體的方法實現,所以其抽象類是不能直接實例化的,使用抽象類的唯一途徑是使用它的實現子類。
抽象類使用原則:
- 所有抽象類必須有其實現子類。否則抽象類的存在毫無價值。
- 抽象類的子類必須覆寫抽象類的所有抽象方法。但如果實現子類也是抽象類,那麼就不用了,但是最終,繼承關係中所有的抽象方法都要被實現類所覆寫。
- 抽象類的對象雖然不能直接實例化,但是可以利用對象的多態性,實現子類向上轉型爲抽象父類實例化。
- private不能修飾抽象類或抽象方法。這很好理解,private修飾對象或方法後,表示對外界和子類不可見,那麼對象和方法就不能被實現,那麼該對象和方法有什麼存在的價值呢。
下面的例子是利用抽象類實現抽象類子類的封裝性。
abstract class Person {
private String name;
public abstract void printInstanceInfo();
public static Person getInstance() {
// 在方法中創建內部類
class Student extends Person {
@Override
public void printInstanceInfo() {
System.out.println("i am student");
}
}
return new Student();
}
}
public class Demo {
public static void main(String[] args) {
Person person = Person.getInstance();
person.printInstanceInfo();
}
}
上面提到過,抽象類只是比普通類多了一些抽象方法而已,因此在抽象類也存在着構造方法,並且子類對象實例化流程和普通父子類實例化流程一致,實例化子類時也先調用父類構造方法。
如果父類沒有無參構造,那麼子類構造必須使用super明確指出使用父類哪個構造方法。
看下面的例子:
abstract class A{
public A(){ // 3.調用父類構造
this.print() ; // 4.調用被子類覆寫的方法,這裏注意,父類方法如果被子類覆寫,那麼就會調用子類覆寫了的方法,但是如果有多個子類覆寫了不同的方法,具體調用哪個,我也不清楚
}
public abstract void print() ;
}
class B extends A{
private int num = 100 ;
public B(int num) { // 2.調用B的構造方法
super() ; // 3.子類的構造方法第一行會隱式調用父類的無參構造,父類五無參構造,要指定構造方法,在super調用期間,子類的屬性並沒有得到初始化
this.num = num ; // 7.父類的構造調用結束後,子類屬性自動裝載爲指定值,然後執行構造裏的賦值爲類中屬性初始化
}
@Override
public void print() { // 5.此時子類對象的屬性還沒有被初始化,爲默認值0
System.out.println(this.num) ; // 6. 打印子類num的默認值
}
}
public class Demo {
public static void main(String[] args) {
new B(30) ; // 1.實例化B
}
}
值得注意的是,抽象類中也可以不定義任何的抽象方法,就與普通類沒有什麼區別,但是就算這樣,抽象類也不能直接進行實例化。
java中有一個關鍵字final,當它聲明類時,表示不允許該類存在子類,而抽象類必須有子類,所以,final也不能用來修飾抽象類。
抽象類也分爲內部抽象類和外部抽象類。可以用static修飾內部類,表示該內部類爲外部類。但是不能用static修飾外部類。
抽象類的作用在於強制規定了子類的實現結構,主要用於模板設計模式中。
抽象類在實際的使用過程中會定義一些固化的模式,它只能接收幾種特定的指令,但是每種指令的具體實現由子類完成,父類只給出了行爲的約定。最具有代表性的就是javaweb的Servlet。
抽象類雖然定義了子類必須要做的事情,但是抽象類依然會有單繼承侷限。