深入理解java面向對象三大必殺技—封裝,繼承,多態

概述

Java是一種面向對象的語言,所以理解面向對象的編程思想對學習Java是相當重要的。

什麼是面向對象?

首先來看第一個問題, 對於初學者來說想要一次性完全理解面向對象是很困難的,要有一定代碼量之後再回頭看就會有新的理解。這裏就先灌輸一個概念,只要有印象就行。

面向對象時一種符合人類思維習慣的編程思想,比如現實生活中有各種各樣的事物,這些事物有着各種各樣的聯繫。程序中使用對象來映射現實中的事物,使用對象的關係來描述事物之間的聯繫,這種思想就是面向對象。

不能理解這段話沒關係,我們可以對比面向過程來看看。面向過程是C語言的特點,它就是分析解決問題所需要的步驟,然後用函數把這些步驟一一實現,使用的時候一個一個調用便OK。而面向對象則是把解決的問題按照一定的規則劃分爲多個獨立的對象,然後通過調用對象的方法來解決問題的。當然,一個程序會包含多個對象,通過多個對象相互配合來實現應用程序的功能,這樣當程序發生需求變動的時候,只需要改動相關的對象,從而使代碼更容易去維護。

如果用喫來做比喻,自己做則是面向過程,買菜、配菜、煮飯、炒菜,自己必須要依次實現這些過程,最後才能喫到美味可口的飯菜,面向對象則好比是下館子,只需點菜而做菜的過程已經被飯館封裝起來,這裏不需要關心廚師是如何做菜的只要品嚐菜是否美味可口。

面向對象的三個特點:封裝,繼承,多態。

一丶封裝

我們要如何理解封裝?

封裝是面向對象的核心思想,將對象的屬性和行爲封裝起來,不需要讓外界知道其實現細節,這就是封裝思想。我們來看下面這段代碼:

public class Test{

public static void main(String[] args){

Student stu = new Student();

stu.name = "妞妞";

stu.age = 1000;

stu.show();

}

}

這段代碼中將年齡賦值爲1000歲,這在程序中當然不會有任何問題,可是在現實生活中卻是違背常識的。爲了解決這個問題,在設計類的時候,應該對成員變量的訪問做出限制,不允許外界隨意訪問,這就需要實現類的封裝了。

從定義上來說,類的封裝主要指在定義一個類的時候,將類中的屬性私有化,即使用private關鍵字來修飾,被private修飾的屬性只能在它所在類中被訪問。同時,爲了讓外界訪問私有屬性,需要一些公共方法,其中就包括用於設置屬性值的setter()方法和獲取屬性值得getter()方法,下面的代碼就實現了類的封裝:

深入理解java面向對象三大必殺技—封裝,繼承,多態

類的封裝.JPG

在圖中的Student類中,使用private關鍵字將屬性name和age聲明爲私有不允許外界隨意訪問,只能通過setter方法來操作兩個實例變量的值。而在setter方法中可以增加自己的控制邏輯,從而保證Student對象的name和age兩個實例變量不會出現與實際不符合的情形。

另外在囉嗦兩句,進行程序設計的時候,應儘量避免一個模塊直接操作另一個模塊的數據,模塊設計追求的是高內聚(儘可能把模塊的內部數據、功能實現細節隱藏在模塊內部獨立完成,不允許外界直接干預)低耦合(僅暴露少量的方法給外部使用)。正如我們平常使用的手機APP,不需要去關心裏面的代碼實現細節,只要能滿足我們的需求便可以。

二丶繼承

繼承,從世俗的角度來解釋便是子女得到父輩的財產或物品。程序中,繼承是指兩個類的從屬關係,它可以使多種事物之間形成一種關係體系。例如貴賓和泰迪都是屬於寵物犬,程序中便可以描述爲貴賓和泰迪繼承自動物。這離我們主要理清三個問題:

1.繼承的概念是什麼?

2.繼承中如何重寫父類的方法?

3.關鍵字super如何使用?

先看第一個問題,在Java中類的繼承是指在一個現有類的基礎上去構建一個新的類,構建出來的類被稱作子類,現有的類被稱作父類,子類會自動擁有父類所有課繼承的屬性和方法。同時,程序中通常用extends關鍵字來聲明一個繼承另一個類,下面就通過一個案例來演示:

深入理解java面向對象三大必殺技—封裝,繼承,多態

繼承的基本使用.

上面代碼的運行結果是“動物的叫聲”和“我的名字叫貴賓”。其中Dog類通過extends繼承了Animal類,這樣Dog類便是Animal類的子類。從運行結果可以看出,子類雖然沒有定義name屬性和shout方法,但是卻能訪問這兩個成員,這也就說明了子類在繼承父類的時候,會自動擁有父類的所有成員。但也有一些問題需要注意下:

1.Java中,類只支持單繼承不允許多重繼承,也就是說一個類只能有一個父類。

2.多個類可以繼承同一個父類。

3.可以多層繼承,即一個類的父類可以去繼承另外的父類。

4.Java中的子列和父類只是一種相對概念。

接下來看第二個問題,繼承中重寫父類方法是怎麼回事。

我們知道繼承關係中,子類會自動繼承父類中定義的方法,但有時在子類需要對繼承的方法進行修改,即重寫父類方法。好比父親的理想,如果兒子不喜歡,可以去追求自己的夢想。

上面代碼中,Dog類繼承了Animal類中的shout方法,調用時會打印“動物的叫聲”,這明顯不是描述一種具體動物的叫聲,Dog發出的叫聲應該是“汪汪汪。。。”爲了解決這個問題,可以在Dog類中重寫父類Animal的shout()方法,具體代碼如下:

深入理解java面向對象三大必殺技—封裝,繼承,多態

繼承中重寫父類方法

運行結果是“汪汪汪。。。”。從運行結果可以看到,在調用Dog類對象的shout方法是,只會調用子類重寫後的方法。

最後來看第三個問題,如何正確使用super關鍵字?

從上一個代碼中可以看到,當子類重寫父類的方法後,子類對象將無法訪問父類被重寫的方法,爲了解決這個問題,Java中專門提供一個super關鍵字用於訪問父類的成員。super的作用主要體現在兩個方面:

1.使用super關鍵字調用父類的成員變量和方法,格式如下

super.成員變量;

super.成員方法({參數1,參數2...});

2.使用super關鍵字調用父類構造方法,格式如下

super.({參數1,參數2...});

java中多態性的實現

三丶什麼是多態

1.面向對象的三大特性:封裝、繼承、多態。從一定角度來看,封裝和繼承幾乎都是爲多態而準備的。這是我們最後一個概念,也是最重要的知識點。

2.多態的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而採用多種不同的行爲方式。(發送消息就是函數調用)

3.實現多態的技術稱爲:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。

4.多態的作用:消除類型之間的耦合關係。

5.現實中,關於多態的例子不勝枚舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 界面下彈出的就是 AS 3 的幫助文檔;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支持。同一個事件發生在不同的對象上會產生不同的結果。

(一)相關類

1.class A ...{

2. public String show(D obj)...{

3. return ("A and D");

4. }

5. public String show(A obj)...{

6. return ("A and A");

7. }

8.}

9.class B extends A...{

10. public String show(B obj)...{

11. return ("B and B");

12. }

13. public String show(A obj)...{

14. return ("B and A");

15. }

16.}

17.class C extends B...{}

18.class D extends B...{}

(二)問題:以下輸出結果是什麼?

1.A a1 = new A();

2. A a2 = new B();

3. B b = new B();

4. C c = new C();

5. D d = new D();

6. System.out.println(a1.show(b)); ①

7. System.out.println(a1.show(c)); ②

8. System.out.println(a1.show(d)); ③

9. System.out.println(a2.show(b)); ④

10. System.out.println(a2.show(c)); ⑤

11. System.out.println(a2.show(d)); ⑥

12. System.out.println(b.show(b)); ⑦

13. System.out.println(b.show(c)); ⑧

14. System.out.println(b.show(d)); ⑨

(三)答案

① A and A

② A and A

③ A and D

④ B and A

⑤ B and A

⑥ A and D

⑦ B and B

⑧ B and B

⑨ A and D

(四)分析

①②③比較好理解,一般不會出錯。④⑤就有點糊塗了,爲什麼輸出的不是"B and B”呢?!!先來回顧一下多態性。

運行時多態性是面向對象程序設計代碼重用的一個最強大機制,Java多態性的概念也可以被說成“一個接口,多個方法”。Java實現運行時多態性的基礎是動態方法調度,它是一種在運行時而不是在編譯期調用重載方法的機制。

方法的重寫Overriding和重載Overloading是Java多態性的不同表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫(Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱爲方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。方法的重寫Overriding和重載Overloading是Java多態性的不同表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中Java多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱爲方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。

當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。 (但是如果強制把超類轉換成子類的話,就可以調用子類中新添加而超類沒有的方法了。)

好了,先溫習到這裏,言歸正傳!實際上這裏涉及方法調用的優先問題 ,優先級由高到低依次爲:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。讓我們來看看它是怎麼工作的。

比如④,a2.show(b),a2是一個引用變量,類型爲A,則this爲a2,b是B的一個實例,於是它到類A裏面找show(B obj)方法,沒有找到,於是到A的super(超類)找,而A沒有超類,因此轉到第三優先級this.show((super)O),this仍然是a2,這裏O爲B,(super)O即(super)B即A,因此它到類A裏面找show(A obj)的方法,類A有這個方法,但是由於a2引用的是類B的一個對象,B覆蓋了A的show(A obj)方法,因此最終鎖定到類B的show(A obj),輸出爲"B and A”。

再比如⑧,b.show(c),b是一個引用變量,類型爲B,則this爲b,c是C的一個實例,於是它到類B找show(C obj)方法,沒有找到,轉而到B的超類A裏面找,A裏面也沒有,因此也轉到第三優先級this.show((super)O),this爲b,O爲C,(super)O即(super)C即B,因此它到B裏面找show(B obj)方法,找到了,由於b引用的是類B的一個對象,因此直接鎖定到類B的show(B obj),輸出爲"B and B”。

按照上面的方法,可以正確得到其他的結果。

問題還要繼續,現在我們再來看上面的分析過程是怎麼體現出藍色字體那句話的內涵的。它說:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。還是拿a2.show(b)來說吧。

a2是一個引用變量,類型爲A,它引用的是B的一個對象,因此這句話的意思是由B來決定調用的是哪個方法。因此應該調用B的show(B obj)從而輸出"B and B”纔對。但是爲什麼跟前面的分析得到的結果不相符呢?!問題在於我們不要忽略了藍色字體的後半部分,那裏特別指明:這個被調用的方法必須是在超類中定義過的,也就是被子類覆蓋的方法。B裏面的show(B obj)在超類A中有定義嗎?沒有!那就更談不上被覆蓋了。實際上這句話隱藏了一條信息:它仍然是按照方法調用的優先級來確定的。它在類A中找到了show(A obj),如果子類B沒有覆蓋show(A obj)方法,那麼它就調用A的show(A obj)(由於B繼承A,雖然沒有覆蓋這個方法,但從超類A那裏繼承了這個方法,從某種意義上說,還是由B確定調用的方法,只是方法是在A中實現而已);現在子類B覆蓋了show(A obj),因此它最終鎖定到B的show(A obj)。這就是那句話的意義所在,到這裏,我們可以清晰的理解Java的多態性了。

總結

到這裏深入理解java面向對象三大必殺技—封裝,繼承,多態。結束了,不足之處還望大家多多包涵!!覺得收穫的話可以點個關注收藏轉發一波喔,謝謝大佬們支持。(吹一波,233~~)

下面和大家交流幾點編程的經驗:

1、多寫多敲代碼,好的代碼與紮實的基礎知識一定是實踐出來的

2丶 測試、測試再測試,如果你不徹底測試自己的代碼,那恐怕你開發的就不只是代碼,可能還會聲名狼藉。

3丶 簡化算法,代碼如惡魔,在你完成編碼後,應回頭並且優化它。從長遠來看,這裏或那裏一些的改進,會讓後來的支持人員更加輕鬆。

最後,每一位讀到這裏的網友,感謝你們能耐心地看完。希望在成爲一名更優秀的Java程序員的道路上,我們可以一起學習、一起進步。

想了解學習以上內容可加羣569068099  驗證:(009)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章