Java基礎:物件導向程式設計
1. 物件導向程式設計簡介
物件導向程式設計(Object-Oriented Programming,OOP)是一種以「物件」為核心的程式設計範式。它將資料和操作資料的方法組織在一起,形成了所謂的「物件」。這種方法有助於創建更加模組化、可重用和易於維護的程式碼。
Java 作為一種純物件導向的程式設計語言,擁抱 OOP 的概念。在 Java 中,除了基本資料型別外,幾乎所有東西都是物件。Java 的物件導向特性包括:
- 封裝:通過存取修飾符控制資料的可見性。
- 繼承:允許新類別基於現有類別創建,促進程式碼重用。
- 多型:同一個介面可以有多個實現,增加了程式的靈活性。
2. 類別與物件
在 Java 中,類別和物件是物件導向程式設計的基本構建塊。類別是物件的藍圖或模板,而物件則是類別的實例。
類別的定義與結構
類別在 Java 中的基本結構如下:
public class Car {
// 屬性(成員變數)
private String brand;
private String model;
private int year;
// 建構子
public Car(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
// 方法
public void startEngine() {
System.out.println("引擎啟動!");
}
// Getter 和 Setter 方法
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
// 其他 getter 和 setter 方法...
}
物件的創建與使用
一旦定義了類別,我們就可以創建該類別的物件並使用它:
public class Main {
public static void main(String[] args) {
// 創建 Car 類別的物件
Car myCar = new Car("Toyota", "Corolla", 2022);
// 使用物件的方法
myCar.startEngine();
// 使用 getter 方法
System.out.println("我的車是 " + myCar.getBrand());
}
}
建構子
建構子是一種特殊的方法,用於初始化新創建的物件。它的名稱必須與類別名稱相同,且沒有回傳型別。
public class Person {
private String name;
private int age;
// 無參數建構子
public Person() {
this.name = "未知";
this.age = 0;
}
// 帶參數建構子
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
3. 封裝
封裝是物件導向程式設計的四大基本概念之一,它指的是將資料和操作資料的方法綁定在一起,並對外部隱藏內部的實現細節。在 Java 中,封裝主要通過存取修飾符和 getter/setter 方法來實現。
存取修飾符
Java 提供了四種存取修飾符,用於控制類別成員的可見性:
- private:只能在同一個類別中存取
- default(無修飾符):同一個套件中的類別可以存取
- protected:同一個套件中的類別和所有子類別可以存取
- public:任何類別都可以存取
Getter 和 Setter 方法
為了實現封裝,我們通常將類別的屬性設為 private,然後提供公開的 getter 和 setter 方法來存取和修改這些屬性。
public class Student {
private String name;
private int age;
// Getter 方法
public String getName() {
return name;
}
// Setter 方法
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0 && age < 120) { // 加入驗證邏輯
this.age = age;
} else {
System.out.println("無效的年齡");
}
}
}
資料隱藏的重要性
封裝的主要優點包括:
- 資料保護:防止外部直接存取和修改物件的內部狀態。
- 靈活性:可以改變內部實現而不影響外部程式碼。
- 可維護性:更容易管理和維護程式碼。
例如,我們可以在 setter 方法中加入驗證邏輯,確保資料的正確性:
public class BankAccount {
private double balance;
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
System.out.println("存款金額必須大於零");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
System.out.println("提款金額無效或餘額不足");
}
}
}
通過封裝,我們可以確保物件的內部狀態始終保持一致和有效,同時為未來的修改和擴展提供了靈活性。
4. 繼承
繼承是物件導向程式設計中的一個核心概念,它允許我們基於現有的類別創建新的類別。在 Java 中,繼承使用 extends
關鍵字來實現。
繼承的概念與語法
基本語法如下:
例如:
public class Animal {
protected String name;
public void eat() {
System.out.println(name + " 正在吃東西");
}
}
public class Dog extends Animal {
public void bark() {
System.out.println(name + " 汪汪叫");
}
}
在這個例子中,Dog
類別繼承了 Animal
類別,因此 Dog
類別擁有 Animal
類別的所有公開和受保護的成員。
方法覆寫
子類別可以覆寫(override)父類別的方法,提供自己的實現:
public class Cat extends Animal {
@Override
public void eat() {
System.out.println(name + " 優雅地吃東西");
}
}
使用 @Override
註解可以確保我們確實在覆寫父類別的方法。
super 關鍵字
super
關鍵字用於呼叫父類別的方法或建構子:
public class Bird extends Animal {
public Bird(String name) {
super(name); // 呼叫父類別的建構子
}
@Override
public void eat() {
super.eat(); // 呼叫父類別的 eat 方法
System.out.println(name + " 啄食");
}
}
繼承的優點與注意事項
繼承的主要優點包括:
1. 程式碼重用
2. 建立類別層次結構
3. 多型的基礎
繼承要注意:
1. 避免過度使用繼承,可能導致類別層次過於複雜
2. 遵循「組合優於繼承」的原則
3. Java 不支援多重繼承,但可以通過介面實現類似的功能
5. 多型
多型是物件導向程式設計的另一個重要概念,允許我們以一致的方式處理不同類別的物件。在 Java 中,多型主要通過方法覆寫和介面實現。
多型的概念
多型允許一個介面有多種實現。這意味著可以使用父類別的參考變數來引用子類別的物件。
方法多型
方法多型是通過方法覆寫實現的。例如:
public class Animal {
public void makeSound() {
System.out.println("動物發出聲音");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("狗:汪汪!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("貓:喵喵!");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // 輸出:狗:汪汪!
animal2.makeSound(); // 輸出:貓:喵喵!
}
}
運算子多型
Java 也支援某些運算子的多型,如 +
運算子可用於數字相加或字串連接。
向上轉型與向下轉型
- 向上轉型(Upcasting):將子類別的參考賦值給父類別的變數,這是自動完成的。
- 向下轉型(Downcasting):將父類別的參考轉換為子類別的參考,需要明確的類型轉換。
Animal animal = new Dog(); // 向上轉型
Dog dog = (Dog) animal; // 向下轉型
if (animal instanceof Cat) {
Cat cat = (Cat) animal; // 使用 instanceof 檢查類型安全
}
6. 抽象類別與介面
抽象類別和介面是 Java 中實現抽象化的兩種重要機制,它們在設計複雜系統時扮演著關鍵角色。
抽象類別的定義與用途
抽象類別是一種不能被實例化的類別,它可以包含抽象方法(沒有實作的方法)和具體方法。
abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// 抽象方法
public abstract double getArea();
// 具體方法
public void displayColor() {
System.out.println("顏色是:" + color);
}
}
class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
抽象類別的主要用途: 1. 為一組相關的類別提供一個共同的基類 2. 定義模板方法,子類別可以根據需要覆寫這些方法
介面的定義與實現
介面是一種完全抽象的類型,只包含抽象方法的宣告。
interface Drawable {
void draw();
}
class Rectangle extends Shape implements Drawable {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
@Override
public void draw() {
System.out.println("繪製一個矩形");
}
}
介面的主要特點:
1. 可以實現多重繼承
2. 定義一組相關方法的規範
3. 默認方法(default method)和靜態方法(static method)(Java 8+)
默認方法(Default Method)
定義位置:默認方法是在接口中定義的,使用 default 關鍵字。
目的:允許接口添加新方法而不影響實現該接口的類。這樣可以保持向後兼容性。
實現:默認方法可以有具體的實現,實現類可以選擇使用默認實現或重寫該方法。
靜態方法(Static Method)
定義位置:靜態方法可以在類中或接口中定義,但在接口中使用 static 關鍵字。
目的:靜態方法屬於類本身,而不是類的實例。可以直接通過類名調用。
無法重寫:靜態方法不能被實現類重寫,因為它們不屬於對象的實例。
抽象類別 vs 介面
選擇使用抽象類別還是介面取決於具體需求:
使用抽象類別
- 當你想要在相關的類別之間共享程式碼
- 需要存取非公開的成員變數或方法
- 需要宣告非靜態或非 final 的成員
使用介面
- 當你想要定義一個可由不相關類別實現的類型
- 需要實現多重繼承
- 想要指定特定行為,而不關心誰來實現它
7. 物件導向設計原則
在物件導向程式設計中,遵循一些設計原則可以幫助我們創建更加靈活、可維護和可擴展的系統。以下是一些重要的設計原則:
SOLID 原則簡介
SOLID 是五個物件導向設計原則的縮寫:
單一職責原則(Single Responsibility Principle, SRP)
- 一個類別應該只有一個改變的理由。
開放封閉原則(Open-Closed Principle, OCP)
- 軟體實體應該對擴展開放,對修改封閉。
里氏替換原則(Liskov Substitution Principle, LSP)
- 子類別必須能夠替換其父類別而不影響程式的正確性。
介面隔離原則(Interface Segregation Principle, ISP)
- 多個特定客戶端介面優於一個通用介面。
依賴反轉原則(Dependency Inversion Principle, DIP)
- 高層模組不應該依賴於低層模組,兩者都應該依賴於抽象。
設計模式概述
設計模式是解決常見設計問題的可重用解決方案。一些常見的設計模式包括:
- 創建型模式:如單例模式、工廠模式、建造者模式等。
- 結構型模式:如適配器模式、裝飾者模式、代理模式等。
- 行為型模式:如觀察者模式、策略模式、命令模式等。
物件導向設計的最佳實踐
-
組合優於繼承:盡可能使用組合來實現程式碼重用,而不是過度依賴繼承。
-
程式設計到介面,而不是具體實現:這增加了程式的靈活性。
-
保持類別的高內聚性和低耦合性:每個類別應該專注於單一任務,並儘量減少對其他類別的依賴。
-
遵循 DRY 原則(Don't Repeat Yourself):避免程式碼重複。
-
YAGNI 原則(You Ain't Gonna Need It):只實現當前需要的功能,避免過度設計。
-
使用設計模式,但不要濫用:設計模式是工具,不是目標。