抽象方法和抽象類
抽象方法和抽象類必須使用abstract修飾符來定義,有抽象方法的類只能被定義成抽象類,抽象類裏可以沒有抽象方法。
抽象方法和抽象類的規則如下:
- 抽象類必須使用abstract修飾符來修飾,抽象方法也必須使用abstract修飾符來修飾,抽象方法不能有方法體。
- 抽象類不能被實例化,無法使用new關鍵字來調用抽象類的構造器創建抽象類的實例。即使抽象類裏不包含抽象方法,這個抽象類也不能創建實例。
- 抽象類可以包含成員變量、方法(普通方法和抽象方法都可以)、構造器、初始化塊、內部類(接口、枚舉)五種成分。抽象類的構造器不能用於創建實例,主要是用於被其子類調用。
- 含有抽象方法的類(包括直接定義了一個抽象方法;或繼承了一個抽象父類,但沒有完全實現父類包含的抽象方法;或實現了一個接口,但沒有完全實現接口包含的抽象方法三種情況)只能被定義成抽象類。
注意:
歸納起來,抽象類可用“有得有失”4個字來描述。“得”指的是抽象類多了一個能力:抽象類可以包含抽象方法;“失”指的是抽象類失去了一個能力:抽象類不能用於創建實例。
定義抽象方法只需在普通方法上增加abstract修飾符,並把普通方法的方法體(也就是方法後花括號括起來的部分)全部去掉,並在方法後面增加分號即可。
注意:
抽象方法和空方法體的方法不是同一個概念。例如,public abstract void test();是一個抽象方法,它根本沒有方法體,即方法後面沒有一對花括號;但public void test(){}方法是一個普通方法,它已經定義了方法體,只是方法體爲空,即它的方法體什麼也不做,因此這個方法是不可使用abstract來修飾。
定義抽象類只需在普通類上增加abstract修飾符即可。甚至一個普通類(沒有包含抽象方法的類)增加abstract修飾符後也將變成抽象類。
下面定義一個Shape抽象類。
public abstract class Shape {
{
System.out.println("執行Shape的初始化塊。。。");
}
private String color;
//定義一個計算周長的抽象方法
public abstract double calPerimeter();
//定義一個返回形狀的抽象方法
public abstract String getType();
//定義Shape的構造器,該構造器並不是用於創建Shape對象
//而是被子類調用
public Shape(){
}
public Shape(String color){
System.out.println("執行Shape的構造器。。。");
this.color=color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
上面的Shape類裏包含了兩個抽象方法:calPerimeter()和getType(),所以這個Shape類只能被定義成抽象類。Shape類裏既包含了初始化塊,也包含了構造器,這些都不是在創建Shape對象時被調用的,而是在創建其子類的實例時被調用。
抽象類不能用於創建實例,只能當做父類被其他子類繼承。
下面定義一個三角形類,三角形類被定義成普通類,因此必須實現Shape類裏的所有抽象方法。
public class Triangle extends Shape {
//定義三角形的三邊
private double a;
private double b;
private double c;
public Triangle(String color,double a,double b,double c){
super(color);
this.setSides(a,b,c);
}
public void setSides(double a,double b,double c){
if(a>=b+c||b>a+c||c>a+b){
System.out.println("三角形的兩邊之和必須大於第三邊");
return;
}
this.a=a;
this.b=b;
this.c=c;
}
//重寫Shape類的計算周長的抽象方法
public double calPerimeter(){
return a+b+c;
}
//重寫Shape類的返回形狀的抽象方法
public String getType(){
return "三角形";
}
}
上面的Triangle類繼承了Shape抽象類,並實現了Shape類中的兩個抽象方法,是一個普通類,因此可以創建Triangle類的實例,可以讓一個Shape類型的引用變量指向Triangle對象。
下面再定義一個Circle普通類,Circle類也是Shape的一個子類。
public class Circle extends Shape {
private double radius;
public Circle(String color,double radius){
super(color);
this.radius=radius;
}
public void SetRadius(){
this.radius=radius;
}
//重寫Shape類的計算周長的抽象方法
public double calPerimeter(){
return 2*Math.PI*radius;
}
//重寫Shape類的返回形狀的抽象方法
public String getType(){
return getColor()+"圓形";
}
public static void main(String[] args) {
Shape s1=new Triangle("黑色", 3, 4, 5);
Shape s2=new Circle("黃色",3);
System.out.println(s1.getType());
System.out.println(s1.calPerimeter());
System.out.println(s2.getType());
System.out.println(s2.calPerimeter());
}
}
上面的main()方法中定義了兩個Shape類型的引用變量,它們分別指向Triangle對象和Circle對象。由於在Shape類中定義了calPerimeter()方法和getType()方法,所以程序可以直接調用s1變量和s2變量的calPerimeter()方法和getType()方法,無須強制類型轉換爲其子類類型。
利用抽象類和抽象方法的優勢,可以更好的發揮多態的優勢,使得程序更加靈活。
當使用abstract修飾類時,表明這個類只能被繼承;當使用abstract修飾方法時,表明這個方法必須由子類提供實現(即重寫)。而final修飾的類不能被繼承,final修飾的方法不能被重寫。因此final和abstract永遠不能同時使用。
注意:
abstract不能用於修飾成員變量,不能用於修飾局部變量,即沒有抽象變量、沒有抽象成員變量等說法;abstract也不能用於修飾構造器,沒有抽象構造器,抽象類裏定義的構造器只能是普通構造器。
除此之外,當使用static修飾一個方法時,表明這個方法屬於該類本身,即通過類就可以調用該方法,如果過該方法被定義成抽象方法,則將導致通過該類來調用該方法時出現錯誤(調用了一個沒有方法體的方法肯定會引起錯誤)。因此static和abstract不能同時修飾某個方法,即沒有所謂的類抽象方法。
注意:
static和abstract並不是絕對互斥的,static和abstract雖然不能同時修飾某個方法,但他們可以同時修飾內部類。
abstract關鍵字修飾的方法必須被其子類重寫纔有意義,否則這個方法將永遠不會有方法體,因此abstract方法不能被定義爲private訪問權限,即private和abstract不能同時修飾方法