1. 什么是抽象類?
1.1 定義:
抽象類是一個不能實例化的類,它是用來作為其他類的基類的。抽象類可以包含抽象方法和非抽象方法。抽象方法沒有方法體,子類必須重寫這些方法并提供具體的實現。抽象類可以有構造方法、成員變量、靜態方法和默認方法等。
1.2 特點:
- 抽象類不能實例化,即不能創建抽象類的對象。
- 抽象類可以有抽象方法和非抽象方法。
- 子類必須實現抽象類中的所有抽象方法,除非子類本身是抽象類。
2. 抽象類的聲明
在Java中,使用abstract
關鍵字來聲明抽象類和抽象方法。
abstract class Animal {// 抽象方法abstract void sound();// 非抽象方法(普通方法)void eat() {System.out.println("Animal is eating");}
}
3. 抽象類與繼承
3.1 解釋:
子類繼承抽象類時,必須實現抽象類中的所有抽象方法,除非子類也是抽象類。如果子類沒有實現抽象類的所有抽象方法,子類也必須被聲明為抽象類。
3.2 示例:
abstract class Animal {abstract void sound(); // 抽象方法
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}class Cat extends Animal {@Overridevoid sound() {System.out.println("Cat meows");}
}
在這個例子中,Dog
和Cat
類分別實現了Animal
類中的抽象方法sound
。
4. 抽象類的構造方法
4.1 解釋:
抽象類可以有構造方法,構造方法在子類創建對象時被調用。雖然不能直接實例化抽象類,但可以通過子類的構造方法來調用父類的構造方法。
4.2 示例:
abstract class Animal {Animal() {System.out.println("Animal constructor");}abstract void sound();
}class Dog extends Animal {Dog() {super(); // 調用父類的構造方法System.out.println("Dog constructor");}@Overridevoid sound() {System.out.println("Dog barks");}
}class abstract_Main{public static void main(String args[]){Animal dog = new Dog();dog.sound();}}
輸出:
Animal constructor
Dog constructor
Dog barks
5. 抽象類的成員變量
5.1 解釋:
抽象類可以包含成員變量,它們可以是private
、protected
、public
等不同的訪問修飾符。成員變量可以在抽象類中進行初始化,也可以在子類中進行修改。
5.2 示例:
abstract class Animal {String name; // 成員變量Animal(String name) {this.name = name;}abstract void sound();
}class Dog extends Animal {Dog(String name) {super(name); // 調用父類構造方法}@Overridevoid sound() {System.out.println(name + " barks");}
}
6. 抽象類的訪問修飾符
6.1 解釋:
抽象類的訪問修飾符(如public
、protected
等)與普通類相同。可以根據需要控制訪問級別,確保只有適當的類可以訪問抽象類。
6.2 示例:
abstract class Animal {abstract void sound();
}public class Main {public static void main(String[] args) {// Animal animal = new Animal(); // 錯誤:不能實例化抽象類Dog dog = new Dog();dog.sound(); // 輸出:Dog barks}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}
7. 抽象類與接口的區別
7.1 區別:
抽象類和接口都可以用來定義類的行為規范,但它們有一些關鍵的區別:
特性 | 抽象類 | 接口 |
---|---|---|
是否可以有方法體 | 可以有抽象方法和非抽象方法 | 只能有抽象方法(Java 8之后,可以有默認方法和靜態方法) |
是否可以有成員變量 | 可以有成員變量 | 不可以有成員變量 |
是否支持多繼承 | 只能繼承一個類 | 可以實現多個接口 |
構造方法 | 可以有構造方法 | 沒有構造方法 |
繼承的限制 | 只能繼承一個抽象類 | 可以實現多個接口 |
7.2 選擇使用抽象類還是接口:
- 抽象類:適用于類之間有“是一個”關系且需要共享代碼的情況。比如,多個類共享一些通用的屬性和方法時,可以使用抽象類。
- 接口:適用于不同類之間有“行為”上的共性時。接口更強調行為規范的定義,可以在多個不相關的類中實現。
8. 抽象類的多態
8.1 解釋:
和其他類一樣,抽象類也支持多態。當父類類型的引用指向子類對象時,可以調用子類的重寫方法。這使得抽象類可以與子類一起使用,支持多態特性。
8.2 示例:
abstract class Animal {abstract void sound();
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}class Cat extends Animal {@Overridevoid sound() {System.out.println("Cat meows");}
}public class Main {public static void main(String[] args) {Animal animal1 = new Dog();Animal animal2 = new Cat();animal1.sound(); // 輸出:Dog barksanimal2.sound(); // 輸出:Cat meows}
}
通過多態,Animal
引用可以指向不同的子類對象,并調用子類的sound
方法。
9. 抽象類的局限性
盡管抽象類具有許多優勢,但它也有一些局限性:
- 只能繼承一個抽象類:Java中不支持多重繼承,因此如果一個類繼承了某個抽象類,它就不能再繼承其他類。這可能會在設計中造成一些限制。
- 不能實例化:抽象類不能直接實例化,因此必須通過子類來實例化。
10. 抽象類的應用場景
10.1 抽象類通常用于以下情況:
- 提供模板方法:通過在抽象類中定義一個具體的框架方法,然后讓具體的子類提供實現細節。比如,常見的模板方法模式就是用抽象類實現的。
- 共享代碼:如果多個類有相似的功能,可以將這些功能放在抽象類中,由子類繼承。
- 多態支持:通過抽象類和繼承機制,支持多態,讓同一類型的對象能夠表現出不同的行為。
10.2 示例:模板方法模式
// 抽象類,定義了模板方法
abstract class Template {// 模板方法,定義了執行步驟的順序public final void execute() {step1();step2();step3();}// 步驟1(抽象方法,由具體子類實現)abstract void step1();// 步驟2(抽象方法,由具體子類實現)abstract void step2();// 步驟3(具體方法,子類可以直接使用)void step3() {System.out.println("Step 3: Common step");}
}// 具體類A,實現了模板方法中定義的步驟
class ConcreteClassA extends Template {@Overridevoid step1() {System.out.println("ConcreteClassA: Step 1");}@Overridevoid step2() {System.out.println("ConcreteClassA: Step 2");}
}// 具體類B,實現了模板方法中定義的步驟
class ConcreteClassB extends Template {@Overridevoid step1() {System.out.println("ConcreteClassB: Step 1");}@Overridevoid step2() {System.out.println("ConcreteClassB: Step 2");}
}public class Main {public static void main(String[] args) {// 使用ConcreteClassA來執行模板方法Template classA = new ConcreteClassA();System.out.println("Executing Template in ConcreteClassA:");classA.execute(); // 調用模板方法System.out.println("\n-------------------");// 使用ConcreteClassB來執行模板方法Template classB = new ConcreteClassB();System.out.println("Executing Template in ConcreteClassB:");classB.execute(); // 調用模板方法}
}
輸出:
Executing Template in ConcreteClassA:
ConcreteClassA: Step 1
ConcreteClassA: Step 2
Step 3: Common step-------------------Executing Template in ConcreteClassB:
ConcreteClassB: Step 1
ConcreteClassB: Step 2
Step 3: Common step
?解釋:
-
Template
類:- 這個類定義了一個模板方法
execute()
,它規定了執行步驟的順序:step1()
、step2()
和step3()
。 step1()
和step2()
是抽象方法,要求具體子類去實現它們。step3()
是一個具體方法,所有子類都可以直接使用,子類不需要重寫。
- 這個類定義了一個模板方法
-
ConcreteClassA
和ConcreteClassB
類:- 這兩個類都繼承了
Template
類,并實現了step1()
和step2()
方法,但它們的具體實現是不同的。 step3()
由父類Template
提供,不需要在子類中實現。
- 這兩個類都繼承了
-
模板方法的使用:
- 在
Main
類的main()
方法中,我們創建了ConcreteClassA
和ConcreteClassB
的實例,并調用了它們的execute()
方法。 - 每次調用
execute()
方法時,都會執行Template
中定義的步驟順序,但實際執行的內容是由ConcreteClassA
或ConcreteClassB
提供的step1()
和step2()
方法。
- 在
11. 抽象類與接口的結合使用
11.1解釋:
在Java中,抽象類和接口可以結合使用,一個類可以同時實現接口并繼承抽象類。這可以讓你充分利用接口的多繼承特性,同時還能通過抽象類共享代碼。
11.2 示例:
// 定義Animal接口,要求實現sound方法
interface Animal {void sound(); // 聲明發出聲音的方法
}// 定義Mammal抽象類,繼承自Animal接口
abstract class Mammal implements Animal {// 聲明抽象方法walk,表示哺乳動物的步態abstract void walk();
}// Dog類繼承自Mammal,并實現sound和walk方法
class Dog extends Mammal {// 實現Animal接口的sound方法@Overridepublic void sound() {System.out.println("Dog barks");}// 實現Mammal類的walk方法@Overridevoid walk() {System.out.println("Dog walks on four legs");}
}// 主類,用于測試Dog類的功能
public class Main {public static void main(String[] args) {// 創建一個Dog對象Dog dog = new Dog();// 調用Dog類的sound方法dog.sound(); // 輸出:Dog barks// 調用Dog類的walk方法dog.walk(); // 輸出:Dog walks on four legs}
}
解釋:
-
Animal
接口:Animal
接口聲明了一個方法sound()
,所有實現了Animal
接口的類必須提供對該方法的具體實現。
-
Mammal
抽象類:Mammal
是一個抽象類,它實現了Animal
接口,但并沒有提供sound()
方法的實現,子類Dog
需要提供該方法的實現。Mammal
類中還聲明了一個抽象方法walk()
,表示哺乳動物的行走方式。具體的walk()
方法由子類實現。
-
Dog
類:Dog
類繼承自Mammal
,并實現了sound()
和walk()
方法,具體定義了狗的行為:叫聲和走路方式。
-
Main
類:Main
類創建了Dog
類的實例,并調用了sound()
和walk()
方法,輸出狗的行為。
12. 抽象類中的靜態方法
12.1 解釋:
抽象類也可以包含靜態方法,靜態方法屬于類本身,而不是類的實例。抽象類中的靜態方法可以直接通過類名調用。但靜態方法不能被子類重寫。靜態方法屬于類本身,不屬于實例化的對象,因此它們的調用不受多態的影響。
12.2 示例:
abstract class Animal {static void info() {System.out.println("This is an animal");}
}class Dog extends Animal {// 不能重寫靜態方法// static void info() {} // 錯誤,不能重寫靜態方法
}public class Main {public static void main(String[] args) {Animal.info(); // 調用Animal類的靜態方法}
}
盡管Animal
是一個抽象類,但它仍然可以有靜態方法。靜態方法不能被子類重寫,它們可以通過類名直接訪問。
13. 抽象類的默認實現
13.1 解釋:
抽象類不僅可以聲明抽象方法,還可以提供默認實現。子類可以選擇繼承這個默認實現,也可以重寫這些方法。這種機制與接口的默認方法(default
)類似。
13.2 示例:
// 定義一個抽象類Animal,包含一個默認實現的方法eat()和一個抽象方法sound()
abstract class Animal {// 默認實現:吃東西的方法void eat() {System.out.println("Animal is eating");}// 抽象方法:每個子類必須實現它來發出聲音abstract void sound();
}// Dog類繼承Animal類,只需要實現sound()方法
class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}// Main類,用于執行和測試代碼
public class Main {public static void main(String[] args) {// 創建一個Dog對象Dog dog = new Dog();// 調用Dog類繼承的eat()方法(沒有被重寫,直接使用父類的默認實現)dog.eat(); // 輸出:Animal is eating// 調用Dog類自己實現的sound()方法dog.sound(); // 輸出:Dog barks}
}
在這個例子中,eat()
方法在Animal
類中有默認實現,Dog
類繼承了eat()
方法,但沒有重寫它。子類只需要實現sound()
方法即可。
14. 抽象類中訪問父類的方法
14.1 子類通過super訪問父類中的成員方法和成員變量:
子類可以通過super
關鍵字來訪問父類的成員方法和成員變量。對于抽象類中的方法,子類可以使用super
來調用抽象類中已實現的非抽象方法。
示例:
abstract class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridevoid sound() {super.sound(); // 調用父類的sound方法System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Dog dog = new Dog();dog.sound();}
}
/*
輸出:
Animal makes a sound
Dog barks
*/
在上面的代碼中,Dog
類通過super.sound()
調用了Animal
類中已實現的sound()
方法。
14.2 子類通過super訪問父類中的構造方法:
在子類的構造方法中,super()
可以用來調用父類的構造方法。如果父類沒有無參構造方法,子類的構造方法必須顯式調用父類的構造方法。
示例:
abstract class Animal {Animal(String name) {System.out.println("Animal constructor with name: " + name);}abstract void sound();
}class Dog extends Animal {Dog(String name) {super(name); // 調用父類的構造方法System.out.println("Dog constructor");}@Overridevoid sound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Dog dog = new Dog("Buddy");dog.sound();}
}
輸出:
Animal constructor with name:Buddy
Dog constructor
Dog barks
15. 抽象類與構造方法的調用
盡管抽象類不能被直接實例化,但它可以有構造方法。當子類創建對象時,會先調用父類的構造方法。子類的構造方法可以使用super()
調用父類的構造方法。
示例:
abstract class Animal {Animal() {System.out.println("Animal constructor");}abstract void sound();
}class Dog extends Animal {Dog() {super(); // 調用父類構造方法System.out.println("Dog constructor");}@Overridevoid sound() {System.out.println("Dog barks");}
}public class abstract_Main2{public static void main(String args[]){Animal dog = new Dog();dog.sound(); }}
輸出:
Animal constructor
Dog constructor
Dog barks
16. 抽象類與final
關鍵字的結合
final
類:如果一個類被聲明為final
,它不能被繼承。因此,抽象類不能是final
類,因為抽象類必須被繼承才能實現。final
方法:如果一個方法被聲明為final
,它不能被重寫。雖然抽象類中的方法是抽象的,不能直接定義為final
,但是如果子類實現了該方法,可以將其標記為final
,防止進一步重寫。
示例:
// 定義一個抽象類Animal,包含一個final方法eat()和一個抽象方法sound()
abstract class Animal {// final方法,子類不能重寫final void eat() {System.out.println("Animal is eating");}// 抽象方法,子類必須實現abstract void sound();
}// Dog類繼承自Animal類,并實現sound()方法
class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}// 不能重寫eat()方法,因為它是final方法// 如果你嘗試重寫eat()方法,編譯器會報錯// final void eat() {// System.out.println("Dog is eating");// }
}// 主類,用于執行和測試代碼
public class Main {public static void main(String[] args) {// 創建一個Dog對象Dog dog = new Dog();// 調用Dog類繼承的eat()方法(它來自Animal類,不能被重寫)dog.eat(); // 輸出:Animal is eating// 調用Dog類自己實現的sound()方法dog.sound(); // 輸出:Dog barks}
}
在這個例子中,eat()
方法在Animal
類中被聲明為final
,所以子類Dog
不能重寫eat()
方法。