1. 簡介
????????面向對象編程 (OOP) 是一種強大的編程范式,它通過將程序組織成對象的集合來簡化軟件設計和開發。與傳統的程序設計方法相比,OOP 提供了一種更自然、更易于理解和維護的方式來構建復雜的軟件系統。OOP 的核心概念包括:對象、類、繼承、多態、封裝。本文將深入探討這些概念,重點講解繼承、多態、抽象類和接口,以及它們在 Java 中的應用。
2. 棧空間和堆空間
????????在深入了解 OOP 之前,我們需要先了解 Java 中的內存管理機制,尤其是棧空間和堆空間。
2.1 棧空間 (Stack)
- 定義和作用:?棧空間用于存儲局部變量、方法參數和函數調用信息。 它是 Java 程序運行時最基本的內存區域之一。
- LIFO 原理:?棧空間遵循先進后出 (LIFO) 的原則,就像一個疊放盤子的架子,最后放進去的盤子最先被拿出來。
- 存儲數據類型:?棧空間主要存儲以下數據類型:
- 局部變量: 定義在方法內部的變量,例如?
int age = 25;
。 - 方法參數: 傳遞給方法的變量,例如?
void calculateSum(int a, int b) { ... }
?中的?a
?和?b
。 - 函數調用信息: 包括方法調用時的局部變量地址、返回地址等。
- 局部變量: 定義在方法內部的變量,例如?
- 特點:
- 棧空間大小通常較小,而且速度較快。
- 棧空間的分配和回收由 Java 虛擬機 (JVM) 自動管理,程序員不需要手動進行操作。
2.2 堆空間 (Heap)
- 定義和作用:?堆空間用于存儲對象和數組。它是 Java 程序中用于動態內存分配的主要區域。
- 特點:
- 堆空間的空間大小通常比棧空間大得多。
- 堆空間的分配和回收由垃圾回收器 (Garbage Collector) 自動管理,程序員一般不需要手動進行操作。
- 堆空間通常比棧空間速度慢。
2.3 棧空間和堆空間的交互
當創建一個對象時,會發生以下步驟:
- 棧空間:?分配一個引用變量,該變量指向堆空間中對象的地址。例如:
Car myCar = new Car();
?中的?myCar
。 - 堆空間:?為新創建的對象分配一塊內存塊,其中包含對象的屬性和方法。
- 連接:?引用變量指向堆空間中分配的內存塊,這樣就可以通過引用變量訪問對象。
// 1. 棧空間:
// 創建一個 Car 類對象,并分配了一個引用變量 myCar。
Car myCar = new Car(); // 2. 堆空間:
// 將新創建的 Car 對象的屬性和方法存儲在堆空間中。
// 這里假設 Car 有兩個屬性:color 和 model。
// myCar 的值指向堆空間中對象的地址。
// 此時 myCar 就是一個指向堆空間對象的引用。
// 堆空間
// +-----+-----+---------+ //
// | color| model| ... | //
// +-----+-----+---------+
// ^
// |
// myCar(棧空間)
3. 面向對象編程的基礎
3.1 對象和類
- 對象:?現實世界中事物的抽象表示。在編程中,對象是數據(屬性)和操作數據的方法(行為)的封裝。例如,一個 "汽車" 對象可以包含屬性(例如顏色、品牌、型號、速度)和方法(例如啟動、加速、剎車)。
- 類:?創建對象的模板或藍圖。它定義了對象的屬性和方法。例如,"汽車" 類可以定義所有汽車共有的屬性和方法,然后通過這個類創建多個不同的汽車對象。
用 Java 代碼表示:
class Car {String color;String brand;String model;int speed;void start() {System.out.println("汽車啟動");}void accelerate() {System.out.println("汽車加速");}void brake() {System.out.println("汽車剎車");}
}public class Main {public static void main(String[] args) {Car myCar = new Car();myCar.color = "紅色";myCar.brand = "寶馬";myCar.model = "3系";myCar.start();myCar.accelerate();}
}
3.2 封裝
- 概念:?將對象的屬性和方法結合起來,并隱藏對象的內部實現細節,只暴露接口供外部訪問。
- 目的:
- 提高代碼安全性: 防止外部代碼直接修改對象的私有屬性。
- 提升代碼可維護性: 修改對象的內部實現細節不會影響外部代碼的使用。
- 增強代碼可重用性: 可以根據需要創建不同的對象,而無需關注其內部實現細節。
訪問修飾符:
修飾符 | 描述 | 訪問范圍 |
---|---|---|
public | 任何地方都可以訪問 | 整個程序 |
private | 只有類內部可以訪問 | 類內部 |
protected | 繼承的類和同一個包內的類可以訪問 | 繼承類和同一個包 |
default ?(無修飾符) | 只有同一個包內的類可以訪問 | 同一個包 |
用 Java 代碼示例:
class Person {private String name; // 私有屬性,只能在 Person 類內部訪問private int age; // 私有屬性,只能在 Person 類內部訪問public String getName() { // 公共方法,可以在任何地方訪問return name;}public void setName(String name) { // 公共方法,可以在任何地方訪問this.name = name;}public int getAge() { // 公共方法,可以在任何地方訪問return age; }public void setAge(int age) { // 公共方法,可以在任何地方訪問this.age = age;}
}
4. 繼承
- 概念:?繼承是 OOP 中一個重要的特性,它允許一個類繼承另一個類的屬性和方法。繼承建立了 “is-a” 關系,子類擁有父類的所有屬性和方法,并且可以添加自己的屬性和方法。
- 目的:
- 代碼復用: 避免重復編寫相同的功能代碼。
- 可擴展性: 通過繼承,可以在父類的基礎上創建新的子類,實現新的功能。
- 代碼組織: 將相關的功能組織到不同的層次結構中,使代碼更易于理解和維護。
4.1 繼承的概念
- 父類 (基類或超類):?被繼承的類。
- 子類 (派生類或擴展類):?繼承自另一個類的類。
class Animal {void eat() {System.out.println("動物在吃");}
}class Dog extends Animal { // Dog 繼承了 Animal 類void bark() {System.out.println("狗在叫");}
}
4.2 繼承的優勢
- 代碼復用:?繼承允許子類復用父類的代碼,避免重復編寫相同的功能。例如,在?
Dog
?類中,eat
?方法不需要重新編寫,可以直接繼承自父類?Animal
。 - 可擴展性:?繼承使得添加新的功能變得更容易。例如,我們可以通過繼承?
Animal
?類,創建新的子類,比如?Cat
?類,來添加貓的相關功能,例如?meow()
?方法。 - 代碼組織:?繼承可以幫助我們更好地組織代碼,將相關的類組織到不同的層次結構中。例如,我們可以創建一個?
Pet
?類作為父類,然后創建?Dog
、Cat
?等子類,這樣可以使代碼更加清晰、易于維護。
4.3 繼承的類型
- 單繼承:?在 Java 中,一個子類只能繼承一個父類。
- 多繼承:?一個子類可以繼承多個父類。 Java 不支持真正的多繼承,但可以使用接口來實現類似功能。
- 層次化繼承:?父類可以有子類,子類可以有孫類,形成層次結構。
4.4 繼承中的方法重寫 (Overriding)
- 概念:?子類可以選擇重寫父類的方法,以便在子類中提供不同的實現。
- 條件:?方法重寫必須滿足以下條件:
- 方法名相同。
- 參數列表相同。
- 返回值類型相同(或者返回值類型是父類返回值類型的子類)。
- 訪問修飾符的權限不能比父類更嚴格。
class Animal {void sound() {System.out.println("動物發出聲音");}
}class Dog extends Animal {@Overridevoid sound() { // 重寫父類的 sound 方法System.out.println("狗叫");}
}
4.5 繼承中的構造函數
- 子類構造函數:?子類構造函數必須調用父類構造函數,才能初始化父類繼承的屬性。
super()
?方法:?在子類構造函數中,可以使用?super()
?方法調用父類的構造函數。
4.6 繼承中的方法隱藏
- 概念:?子類的方法與父類的某個方法具有相同的方法名,但參數列表不同,這種情況稱為方法隱藏。
- 區別:?方法隱藏與方法重寫不同,方法隱藏不會改變方法的實現,而方法重寫會改變方法的實現。
class Animal {void sound() { // 父類方法System.out.println("Animal makes a sound");}
}class Dog extends Animal {void sound(int age) { // 子類方法,與父類方法同名,但參數不同System.out.println("Dog barks, age: " + age);}
}
4.7 繼承中?final
?關鍵字
final
?關鍵字:?用于修飾類、方法和變量,表示它們是最終的,不能被繼承或重寫。final
?類:?表示該類不能被繼承。final
?方法:?表示該方法不能被子類重寫。final
?變量:?表示該變量是一個常量,其值一旦被賦值就不能再改變。
5. 多態
- 概念:?多態是指同一個操作在不同的對象上會產生不同的行為。
- 目的:
- 代碼靈活性和可擴展性: 可以通過父類引用指向子類對象,調用不同的實現方法。
- 提高代碼可讀性: 可以使用更簡潔、更靈活的方式來編寫代碼。
5.1 多態的概念
- 父類引用:?可以使用父類類型的變量來引用子類對象。
- 方法調用:?當調用父類引用中的方法時,實際執行的是子類重寫后的方法。
class Animal { void sound() {System.out.println("動物發出聲音");}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("狗叫");}
}public class Main {public static void main(String[] args) {Animal myDog = new Dog(); // 父類引用指向子類對象myDog.sound(); // 調用的是 Dog 類的 sound 方法}
}
5.2 多態的類型
- 方法重載 (Overloading):?同一個類中,方法名相同,參數列表不同。 編譯器會根據參數類型和數量選擇合適的重載方法。
- 方法重寫 (Overriding):?子類重寫父類的方法。 當父類引用指向子類對象時,調用的是子類重寫后的方法。
5.3 多態的優勢
- 代碼靈活性和可擴展性:?多態可以使代碼更加靈活和易于擴展。
- 代碼可讀性:?多態可以使代碼更加簡潔和易于理解。
5.4 多態的應用場景
????????多態在實際開發中有很多應用場景,例如:
- 工廠模式:?通過工廠類創建不同類型的對象。
- 策略模式:?定義一組算法,并將它們封裝為獨立的類,以便在運行時選擇合適的算法。
5.5 抽象類
- 概念:?抽象類是用?
abstract
?關鍵字修飾的類,它不能被直接實例化,只能被子類繼承。抽象類可以包含抽象方法和普通方法。 - 特點:
- 抽象方法: 沒有方法體,以?
abstract
?關鍵字修飾。 子類必須重寫抽象方法才能實例化。 - 不能被直接實例化: 只能通過子類來實例化。
- 抽象方法: 沒有方法體,以?
abstract class Shape { // 抽象類abstract void draw(); // 抽象方法,沒有方法體void print() { // 普通方法System.out.println("這是一個形狀");}
}class Circle extends Shape {@Overridevoid draw() {System.out.println("畫一個圓形");}
}public class Main {public static void main(String[] args) {Shape circle = new Circle(); // 實例化子類 Circlecircle.draw(); // 調用子類重寫的 draw 方法circle.print(); // 調用父類的 print 方法}
}
5.6 接口
- 概念:?接口是使用?
interface
?關鍵字聲明的,它是一種特殊的抽象類,其中只包含抽象方法和常量。接口不能被直接實例化,只能被類實現。 - 特點:
- 只能包含抽象方法和常量。
- 可以被多個類實現。
- 提高代碼的可擴展性和靈活性。
interface Drawable { // 接口void draw(); // 抽象方法
}class Circle implements Drawable { // 實現接口@Overridepublic void draw() {System.out.println("畫一個圓形"); }
}public class Main {public static void main(String[] args) {Drawable circle = new Circle(); // 實例化 Circle 對象circle.draw();}
}
6. 總結
概念 | 描述 | 優勢 |
---|---|---|
對象 | 現實世界中事物的抽象表示,包含屬性和方法 | 提供了一種更自然、更易于理解和維護的編程方式 |
類 | 創建對象的模板,定義對象的屬性和方法 | 定義了對象的結構和行為 |
封裝 | 將對象的屬性和方法結合起來,隱藏實現細節 | 提高代碼安全性、可維護性和可重用性 |
繼承 | 允許子類繼承父類的屬性和方法 | 代碼復用、可擴展性、代碼組織 |
多態 | 同一個操作在不同的對象上會產生不同的行為 | 代碼靈活性和可擴展性、提高代碼可讀性 |
抽象類 | 用?abstract ?修飾的類,不能被直接實例化,只能被子類繼承 | 定義公共方法和屬性,并強制子類實現抽象方法 |
接口 | 用?interface ?關鍵字定義,包含抽象方法和常量,可以被多個類實現 | 提高代碼的可擴展性和靈活性 |