CodeGym /Java Blog /Toto sisi /Java 中的 OOP 概念
John Squirrels
等級 41
San Francisco

Java 中的 OOP 概念

在 Toto sisi 群組發布
Java 的最大優勢之一是面向對象編程 (OOP)。這就是為什麼這種語言變得如此流行並且非常適合任何規模的項目的原因。什麼是面向對象編程?這不是魔法,但如果你真的投入其中,它會看起來很神奇。OOP 是關於如何構建軟件的。它是一個概念,或者更確切地說是 Java 中的一堆 oop 概念,允許您在 Java 對象之間創建一些特定的交互和關係,以便有效地開發和使用軟件。Java 中的 OOP 概念 - 1經典 OOP 包括 3 + 1 個主要概念。讓我們從經典開始。

物體

Java 對象和真實世界的對像一樣具有兩個特徵:狀態和行為。

例如,一個 Human 對象具有狀態(姓名、性別、是否睡覺……)和行為(學習 Java、散步、談話……)。任何 Java 對像都將其狀態存儲在字段中,並通過方法公開其行為。

封裝

數據封裝是對外部世界隱藏內部數據,並且只能通過公開暴露的方法訪問它。這意味著什麼?什麼數據?躲著誰?隱藏意味著限制對類的數據成員(字段)的直接訪問。

它在 Java 中是如何工作的:

  1. 字段設為私有
  2. 類中的每個字段都有兩個特殊方法:getter 和 setter。Getter 方法返回字段的值。Setter 方法允許您以間接但允許的方式更改字段的值。

Java代碼封裝示例:


public class Student {
private int age;
private String name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
// The name field is private, so you can no longer do this:  firstStudent.name = "John"; 
}
}

為什麼要使用封裝?

主要原因是為了更容易更改代碼。假設您有一個曲棍球學校的申請,並且有一個HockeyStudent類,其中有兩個字段存儲學生的姓名和他或她在學校註冊時的年齡。是這樣的:

public class HockeyStudent {
public String name;
public  int ageOfEnrollment;
}
ageOfEnrollment字段是公共的,沒有 getter 或 setter……這個類被許多其他類使用,一切正常直到一些開發人員認為單個 int 字段不夠用。隊列中的一些曲棍球運動員比同齡人大將近一歲,因此根據他們的出生月份將他們分成兩組會更方便。所以ageOfEnrollment字段應該更改為一個int 數組 (int[][]):第一個數字是完整的年份,第二個是月份。現在你需要重構所有使用Student類的代碼!但是如果你的ageOfEnrollmentfield 是私有的,你有 getters 和 setters,那麼一切就更容易了。如果設置學生年齡的要求發生變化,只需更新setAgeOfEnrollment() setter 方法中的邏輯,您的班級就可以繼續使用Student而不會出現任何問題!這個例子有點做作,但我希望它能解釋為什麼使用封裝是個好主意。

遺產

即使沒有任何實踐經驗,這個原理也更容易理解。不要重複自己 (DRY) 可能是繼承概念的座右銘。繼承使您可以創建子類,該子類繼承父類的字段和方法而無需重新定義它們。當然,你可以在子類中覆蓋父類的字段和方法,但這不是必須的。更重要的是,您可以在子類中添加新的狀態和行為。父類有時稱為超類或基類,子類稱為子類。Java的extends關鍵字就是用來在代碼中實現繼承原理的。

它在 Java 中是如何工作的:

  1. 創建父類。
  2. 使用extends關鍵字創建子類。
  3. 在 Child 類的構造函數中,使用super(parentField1, parentField2, ...)方法設置父類的字段。

構造函數是用於初始化新創建的對象的特殊方法。構造函數與其類名同名。有兩種類型的構造函數:默認(無參數構造函數)和參數化構造函數。一個類必須至少有一個構造函數(如果沒有定義其他構造函數,它有默認構造函數)並且它可以有很多。

每次創建新對象時,都會調用其構造函數。在上面的示例中,您在這一行中執行此操作:


Student firstStudent = new Student();

您使用new關鍵字來調用Student類的默認構造函數:tudent()

一些規則:

  1. 一個班級只能有一個父級。
  2. 一個父類可以有多個子類。
  3. 子類可以有自己的子類。

Java代碼中的繼承示例

讓我們創建一個電話類。

public class Phone {
    int price;
    double weight;

// Constructor
public Phone(int price, double weight) {
        this.price = price;
        this.weight = weight;
    }

    void orderPhone(){
        System.out.println("Ordering phone...");
    }
}
當然,有不同類型的手機,所以讓我們創建兩個子類:一個用於 Android 手機,另一個用於 iPhone。然後我們將添加一些父級沒有的字段和方法。我們將使用super()來調用構造函數來初始化父類確實具有的字段。

Java中的繼承示例


public class Android extends Phone {

// Some new fields     
String androidVersion;
int screenSize;

    String secretDeviceCode;

// Constructor 
    public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
        super(price, weight); // Android inherits Phone’s fields

        //this - reference to the current object
        //super - reference to the parent object

        this.androidVersion = androidVersion;
        this.screenSize = screenSize;
        this.secretDeviceCode = secretDeviceCode;
    }

	// New Android-specific method, does not exist in the Phone class 
    void installNewAndroidVersion() {
        System.out.println("installNewAndroidVersion invoked...");

    }

}

public class IPhone extends Phone {
   
    boolean fingerPrint;

    public IPhone(int price, double weight, boolean fingerPrint) {
        super(price, weight);
        System.out.println("IPhone constructor was invoked...");
        this.fingerPrint = fingerPrint;
    }

    void deleteIPhoneFromDb() {
        System.out.println("deleteIPhoneFromDb invoked...");
    }

@Override // This is about polymorphism, see below
void orderPhone(){
        System.out.println("Ordering my new iPhone and deleting the old one...");
    }
}
因此,重複一遍:在 Java 中,繼承允許您使用繼承父類的字段和方法的子類來擴展一個類。這是實現代碼可重用性的絕佳方式。

多態性

多態性是對像變形的能力,採用不同的形式或以不同的方式起作用。在 Java 中,多態性通常發生在使用父類引用來引用子類對象時。

這意味著什麼以及它在 Java 中的工作原理:

Java 中的多態性是什麼?通常,這意味著您可以為不同的目的使用相同的方法名稱。Java中有兩種多態:方法覆蓋(動態多態)和方法重載(靜態多態)。

方法覆蓋

您可以在子類中覆蓋父類的方法,強制它以不同的方式工作。讓我們用play()方法創建一個Musician父類。

Java代碼中的多態性示例


   public class Musician {
    String name;
    int age;

    // Default constructor
    public Musician() {
    }

    // Parameterized constructor
    public Musician(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void play() {
        System.out.println("I am playing my instrument...");
    }
}
不同的音樂家使用不同的樂器。讓我們創建兩個子類:PianistViolinist。多虧了多態性,每個人都會執行自己版本的play()方法。覆蓋時,可以使用@Override註解,但不是必須的。

public class Pianist extends Musician {
    
    String favoritePianoType;

    public Pianist(String name, int age, String favoritePianoType) {
        super(name, age);
        this.favoritePianoType = favoritePianoType;
    }


    @Override
void play(){
        System.out.println("I am playing the piano...");
    }
}
小提琴手可以是獨奏者或管弦樂隊的成員。讓我們在覆蓋play()方法時考慮到這一點。

public class Violinist extends Musician { 
    boolean isSoloist; 

public Violinist(String name, int age, boolean isSoloist) {
            super(name, age);
            this.isSoloist = isSoloist;
        }


    @Override
void play(){
if (isSoloist) 
        System.out.println("I am playing the violin solo...");
else 
System.out.println("I am playing the violin in an orchestra...");

    }
}
讓我們創建一個Demo類,我們將在其中創建三個對象,每個對像是之前創建的類的一個實例。我們會看到我們得到什麼結果。

public class Demo {
  public static void main(String[] args) {
  Musician musician = new Musician();
  Violinist violinist = new Violinist("John", 32, true);
  Pianist pianist = new Pianist("Glen", 30, "Acoustic"); 

  System.out.println("Musician said:");
  musician.play();
  System.out.println("Violinist said:");
  violinist.play();
  System.out.println("Pianist said:");
  pianist.play();
    }
}
這是我們得到的:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo…
Pianist said:
I am playing the piano...
每個小提琴家和鋼琴家都是音樂家,但不是每個音樂家都是中提琴家或鋼琴家。這意味著如果您不需要創建新的演奏方法,您可以使用音樂家的演奏方法。或者您可以使用super關鍵字從子項調用父項的方法。讓我們在 Pianist 的代碼中這樣做:

public class Pianist extends Musician {

    String favoritePianoType;
    
    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
}
現在讓我們在Demo類中調用我們的main()方法。結果如下:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...

方法重載

方法重載是指在同一個類中使用不同的同名方法。它們在參數的數量、順序或類型方面必須不同。假設一位鋼琴家可以彈奏原聲鋼琴和電鋼琴。要演奏電子琴,音樂家需要電。讓我們創建兩個不同的play()方法。第一個沒有參數,用於原聲鋼琴,第二個帶有指示是否可用的參數。

public class Pianist extends Musician {

    String name;
    int age;
    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
    void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            System.out.println("I am playing the piano...");
        }
        else System.out.println("I can't play this without electricity.");
    }
}
順便說一下,您可以通過這種方式在第二個play(boolean)方法中使用第一個play()方法:

void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            play();
        }
        else System.out.println("I can't play this without electricity.");
    }
讓我們在Demo類中添加一些行來演示我們的重載:

public class Demo {
    public static void main(String[] args) {

        Musician musician = new Musician();
        Violinist violinist = new Violinist("John", 23, true);
        Pianist pianist = new Pianist("Glen", 30, "Acoustic"); 

        System.out.println("Musician said:");
        musician.play();
        System.out.println("Violinist said:");
        violinist.play();
        System.out.println("Pianist said:");
        pianist.play();
        System.out.println("The pianist will now try the electric piano:");
        pianist.play(true);
        System.out.println("The electricity has been shut off. Now when trying the electric piano, the pianist says:");
        pianist.play(false);
    }
}
這是結果:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...
The pianist will now try the electric piano:
The electricity is on.
I am playing my instrument...
I am playing the piano...
The electricity has been shut off. Now when trying the electric piano, the pianist says:
I can't play this without electricity.
Java 根據其參數和對像類型知道應該使用哪個方法。這就是多態。

抽象

當我們定義一個類時,我們正在嘗試構建某種事物的模型。例如,假設我們正在用不同的賽車編寫一個名為 MyRacer 的視頻遊戲。玩家可以選擇其中之一,然後更新它或購買不同的。那麼……什麼是汽車?汽車是一件相當複雜的事情,但如果我們正在嘗試創建賽車視頻遊戲(而不是駕駛模擬器),那麼我們不需要描述它包含的所有數千個齒輪和墊圈。我們需要它的型號、最高速度、機動性、價格、顏色……也許這就足夠了。這是我們遊戲的汽車模型。稍後在 MyRacer 2 中,假設我們決定添加影響道路操控性的輪胎。這裡的模型不同,因為我們添加了更多細節。讓' s 將數據抽象定義為僅識別對象的重要(或必要)特徵並忽略任何不相關細節的過程。有不同層次的抽象。例如,如果您是公共汽車上的乘客,您需要知道您的公共汽車長什麼樣以及它要去哪裡,但您不需要知道如何駕駛它。如果您是公交車司機,您不需要知道如何創建一輛新公交車——您只需要知道如何駕駛它。但是如果你是一個總線製造商,你需要去更底層的抽象層次,因為總線設計的細節對你來說非常重要。我希望你明白我的意思。您需要知道您的公共汽車長什麼樣以及它要去哪裡,但您不需要知道如何駕駛它。如果您是公交車司機,您不需要知道如何創建一輛新公交車——您只需要知道如何駕駛它。但是如果你是一個總線製造商,你需要去更底層的抽象層次,因為總線設計的細節對你來說非常重要。我希望你明白我的意思。您需要知道您的公共汽車長什麼樣以及它要去哪裡,但您不需要知道如何駕駛它。如果您是公交車司機,您不需要知道如何創建一輛新公交車——您只需要知道如何駕駛它。但是如果你是一個總線製造商,你需要去更底層的抽象層次,因為總線設計的細節對你來說非常重要。我希望你明白我的意思。

它在 Java 中是如何工作的:

讓我們在 Java 或 OOP 中構建四個抽象級別 — 從最低(最具體)到最高(最抽象)。
  1. 最低的抽象層次是一個特定的對象。它是具有一組屬於特定類別的特徵的實體。它具有特定的字段值

  2. 用於創建對象的模板是一個類。它是對一組具有相似屬性和內部結構的對象的描述。

  3. 抽像類是對一組類的特性的抽象描述(它作為其他類繼承的模板)。它具有很高的抽象層次,所以不可能直接從一個抽像類中創建對象。只有抽像類的子類可以用來創建對象。抽像類可以包含具有實現的方法,但這不是必需的。

  4. 接口是 Java 編程語言構造的構造,它僅包含抽象公共方法和靜態常量字段(最終靜態)。也就是說,抽像類和接口都不能用來生成對象。

順便說一句,在 Java 8 或更高版本中,接口不僅可以有抽象方法和常量,還可以有默認方法和靜態方法。在 Java 中,接口定義行為,而抽像類用於創建層次結構。一個接口可以由多個類實現。

Java 代碼中的接口示例


interface Human {
	public void struggle();
	public void protect();
}

interface Vulcan {
	int angleOfPointyEars; 
	public void turnOffEmotions(boolean isOn);
	public void telepathy();
}
您可以實現多個接口

The Spock class implements Human and Vulcan {
public void struggle() {
System.out.println("I am struggling...");
}
	public void protect() {
System.out.println("You are under my protection!”);
}
public void turnOffEmotions(boolean isOn){
If (isOn) {
System.out.println("I am turning off my emotions.");
isOn= !isOn;
}
}
	public void telepathy() {
System.out.println("Connecting to your brain...");
}

}
對於初學者,它涵蓋了 Java 中面向對象編程的所有主要概念。除了 4 個主要的 OOP 原則外,Java 還有關聯、聚合和組合。您可以稱它們為“附加的 OOP 原則”。他們值得擁有自己的單獨文章。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION