【碼上行動】Java[二] 面向對象

1. 類和對象

之前初學得時候寫的文章👉Java類和對象

簡單概括來說就是一句話:類是設計圖,對象是產品,一個是抽象的不存在的東西,一個是現實的能用的東西。

  • 類是對一類事物的描述,是抽象的

  • 對象是類事物的實例,是貝體的

  • 類是對象的模板,對象是類的實體

那你要用這個類(設計圖),你就要先實例化對象(把這個設計圖上的東西造出來用)

就像樓盤,你得現有設計圖紙,才能造房子呀。對於住房的人來說,設計圖紙又用不了;但是沒有圖紙又沒法造房子。

所以,得出了結論:

類是對象的抽象,對象是類的實例

在Java中,一切皆對象,因爲類不能用呀。但是必須現有類纔能有對象,因爲對象的產生依賴於類的實例化。(設計圖紙也沒你談什麼造房子)

類的定義與使用

類的組成:

  • 成員變量:對應事物的屬性

  • 成員方法:對應事物的行爲

具體的使用參見👉Java類和對象

單個對象調用內存圖

程序的入口是main方法,從mian方法處開始執行

  1. 先實例化對象,在棧上存放對象的地址

  2. 在堆上創建新的對象(new出來的)

  3. 實例化對象,爲成員變量賦值

  4. 在引用成員方法時,先通過棧上存放的引用地址找到堆內存上成員方法的地址(成員方法放在成員方法區Method Area)

  5. 根據堆上的引用找到方法區的method調用

  6. 方法區的方法要想執行,先壓入到棧中才能執行。在主方法中調用的順序就是執行的順序。壓入棧中執行完後立即彈出釋放

  7. 最後全部執行完成,主方法也銷燬(主方法是最先執行的方法,由棧的結構可知最後釋放)

兩個對象調用同一個方法內存圖

上面的例子是實例化了一個對象來調用方法,那一個類實例化兩個甚至是多個對象,怎麼來調用呢?

Phone類中有三個方法,實例化了兩個對象onetwonew了幾次,就在堆上開闢幾個新空間。但是方法不會創建兩次,兩個在堆上實例的對象引用的都是方法區同一個地址。

這裏還有一個概念,叫引用傳遞

引用傳遞:一塊堆內存可以被多個棧內存所指向

User user1 = new User();
User user2 = user1;

就是將user1在堆上實例化對象的地址引用賦給user2,此時兩個棧引用指向同一塊堆內存

一個有意思的問題就來了。在方法之間,即可以傳遞值,也可以進行引用傳遞。那麼,Java參數傳遞的方式,到底是值傳遞還是引用傳遞?

Java中到底是值傳遞還是引用傳遞

給出結論:Java參數傳遞方式的爲值傳遞,並且只有值傳遞一種

詳情參見:👇👇👇👇👇👇

Java中的參數傳遞,到底是值傳遞還是引用傳遞?

成員變量與局部變量

局部變量與成員變量:

1. 定義的位置不一樣

  • 局部變量:定義在方法中

  • 成員變量:定義在類中

2. 作用範圍不一樣

  • 局部變量:作用在定義的方法中

  • 成員變量:作用在類中

3. 默認值不一樣

  • 局部變量:無默認值,要想使用必須手動賦初值

  • 成員變量:如果沒有賦值,會有默認值

    • 如果是整數 默認爲0

    • 如果是浮點數 默認爲0.0

    • 如果是字符 默認爲’\u0000’

    • 如果是布爾 默認爲 false

    • 如果是引用類型 默認爲null

4. 內存位置不一樣

  • 局部變量:棧中

  • 成員變量:堆中

5. 生命週期不一樣

  • 局部變量:進棧誕生,出棧消失

  • 成員變量:對象創建產生,對象被回收消失

this關鍵字

this關鍵字主要有以下三個方面用途:

  1. this調用本類屬性

  2. this調用本類方法(構造方法和成員方法)

  3. this表示當前對象

當方法的局部變量和類的成員變量重名的時候,根據“就近原則”,優先使用局部變量

如果需要訪問本類當中的成員變量,需要使用格式:this.成員變量名


super關鍵字

this關鍵字主要有以下三個方面用途:

  1. 在子類的成員方法中,訪問父類的成員變量。

  2. 在子類的成員方法中,訪問父類的成員方法。

  3. 在子類的構造方法中,訪問父類的構造方法。

super和this兩種構造調用,不能同時使用,因爲都得放在第一行,不能同時滿足。

2. 面向對象的思想

面向對象,面向是什麼?爲什麼面向的是對象?

面向誰,就更加關注誰。我們面向對象自然就是更加關注“對象”了。

俺沒有對象,不知道有“對象”的思想是什麼。有對象的鐵汁萌好好關心一下對象,沒有的也別new了。好好學習就完事了,等着國家分配吧。

那,爲什麼要面向對象呢?因爲我們關注的不再是程序具體怎麼實現的,而是更加關注實現了什麼功能。

面向對象的思想來源於生活,大神們設計編程語言都是基於“來源於生活但是高於生活”的思想來設計的。面向對象,就是我關注的是”用什麼來實現這件事“,面向過程更加關注“怎麼來實現”。一個強調結果,誰能幫我幹成;一個強調過程,我怎麼來做。

就像洗衣服,面向過程就是自己洗,先把衣服放盆裏用洗衣液浸泡,然後洗衣服,晾衣服。面向對象則是直接把衣服扔給我們的對象------“全自動洗衣機”來洗,我不關係它怎麼洗的,我只關心洗乾淨沒有.

其實,與面向過程相比,就是關注的點不一樣了,我們來二者對比一下,更好的說明。

面向對象,也有偷懶一說。其實就是別人封裝寫好的功能,你不用管怎麼實現的,你關心的是用別人寫好的東西來實現自己想要的功能。

其實,在這一點上,相比於Java,Python(動態的、面向對象的腳本語言)更具有代表性,Python有很多很多第三方的類庫,都是別人寫好的功能,使用更加方便。

我們拿一個打印數組來說明:

【面向過程】

【面向對象】

Arrays.toString()方法源碼:

面向過程全部需要自己來實現手工打印,而面向對象則直接調用打印數組的方法。就好像別人都在用打火機了,你還在鑽木取貨。但是對比而言,面向過程擁有更高的執行效率,這是面向過程不具備的優勢。

3. 面向對象的三大特徵

面向對象名詞擴展

  • OOA:面向對象分析

  • OOD:面向對象設計

  • OOP:面向對象編程

面向對象三大特徵

  1. 封裝性:類內部操作對外部的不可見性(保護性)

  2. 繼承性:子類繼承了父類所有的功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展

  3. 多態性:多態是同一個行爲具有多個不同表現形式的能力

4. 封裝

詳情參見:👇👇👇👇👇👇

private封裝

5. 繼承

繼承是爲了更好地複用之前的代碼。子類繼承了父類所有的功能(不能選擇性的繼承,只能是全繼承),並在無需重新編寫原來的類的情況下對這些功能進行擴展。

Java中的繼承是單繼承,要想實現多繼承則可以使用接口或者多重繼承

  • 類與類之間是單繼承的。直接父類只有一個

  • 類與接囗之間是多實現的。一個類可以實現多個接口

  • 接囗與接囗之間是多繼承的


繼承注意事項:

  1. 子類擁有父類對象所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類是無法訪問,只是擁有(因爲封裝對外的不可見性)。
  2. 子類可以擁有自己屬性和方法,即子類可以對父類進行擴展。
  3. 子類可以用自己的方式實現父類的方法。

繼承中成員變量訪問規則:

在父子類的繼承關係當中,創建子類對象,訪問成員變量的規則:

  • 就近原則

繼承中成員方法訪問規則:

在父子類的繼承關係當中,創建子類對象,訪問成員方法的規則:

  • 創建的對象是誰,就優先用誰(new 的誰,就用誰),如果沒有則向上找

繼承中構造方法訪問規則:

  • 子類構造方法中有一個默認隱含的super()調用,所以一定先調用父類構造,再調用子類(先有父親纔有兒子)

  • 子類構造可以通過super關鍵字來調用父類重載構造,不寫則默認調用父類無參構造,寫了則調用指定參數

  • 父類構造調用,必須是子類構造方法的第一個語句,而且只能調用一次


重載(overload)和重寫(override)

重載就是同樣的一個方法能夠根據輸入的數據的類型不同,做出不同的處理

重載就是同一個類中,多個同名方法根據不同的傳參來執行不同的邏輯處理。

重寫

重寫就是當子類繼承父類的相同方法,輸入數據一樣,但要實現和父類不同的功能時,就需要覆寫父類方法

重寫發生在運行期,是子類對父類的允許訪問的方法的實現過程進行重新編寫。

  1. 返回值類型、方法名、參數列表必須相同,拋出的異常範圍小於等於父類,訪問修飾符範圍大於等於父類。
  2. 如果父類方法訪問修飾符爲 private/final/static ,則子類就不能重寫該方法,但是被 static 修飾的方法能夠被再次聲明。
  3. 構造方法無法被重寫

重載就是產生一個新的,覆寫就是把原來的覆蓋了。


請解釋 方法重載(overload) 和 方法覆寫(override) 的區別:

子類方法重名問題

局部變量:直接寫成員變量名

本類的成員變量:this.成員變量名

父類的成員變量:super.成員變量名

6. 多態

多態性:多態是同一個行爲具有多個不同表現形式的能力

extends繼承或者implements實現,是多態性的前提

【多態性的理解】:對象又有多種表現的形態

就好比說張三這個學生,是屬於人類的。這種說法是正確的,因爲一個學生,本來就是一個人。我們先說的是小範圍的,再說大範圍的。

那要是反過來說,人類就是一個學生沒這肯定不行了。因爲他可能早就畢業了,現在不是學生了,而是一個員工了。

那麼,這也就剛好說明了,實現多態性的前提就是,發生在繼承關係的類中(接口的實現,也可以看做是繼承)。正因爲學生類繼承了人類,才能說“張三這個學生,是屬於人類的”;但是人類沒有繼承學生類呀,所以“人類就是一個學生”的說法是錯誤的。


多態性的體現

現在我有兩個類,一個是Fu類,一個是Zi類,Zi類繼承了Fu類。

多態性在代碼中的體現:父類引用指向子類對象

Fu odj = new Zi(); //父類引用指向子類對象

多態中成員變量的訪問:

訪問成員變量的兩種方式:

  1. 直接通過對象名稱訪問成員變量:看等號左邊是誰,優先用誰,沒有則向上找

  2. 間接通過成員方法訪問成員變量:看該方法屬於誰,優先用誰,沒有則向上找

輸出結果:

10

10

多態中成員方法的訪問:

  • new的是誰,就優先用誰,沒有則向上找

  • 編譯看左邊,運行看右邊

輸出結果:

方法執行👉子類
父類特有方法.

那我再訪問一下子類的methodZi()呢?

  • 會出現編譯報錯

編譯看左邊,左邊是Fu,Fu當中沒有methodeZi()方法,所以編譯報錯


【多態在代碼中的體現總結:】

  • 成員變量:編譯看左邊,運行還看左邊

  • 成員方法:編譯看左邊,運行看右邊new的誰,就執行誰

向上轉型

向上轉型:其實就是多態的寫法

【格式】:父類名稱 對象名 = new 子類名稱( );

【含義】:右側創建一個子類對象,把它當做父類來看待使用

Animal animal =  new Cat();

【注意事項】:向上轉型一定是安全的。從小範圍轉向了大範囤,從小範圍的貓,向上轉換成爲更大範囤的動物。

可以類比爲自動類型的轉換

double num = 100; //int----->double

向下轉型

向下轉型:其實是一個【還原】的動作。

Q:還原的是誰呢?

  • 在發生向下轉型之前,一定先發生向上轉型,還原的是向上轉型時子類的對象。

【格式】:子類名稱 對象名 =(子類名稱) 父類對象

【含義】:將父類對象,[ 還原 ] 成爲本來的子類對象

Animal animal = new Cat();  //本來是貓,向上轉型成爲動物
Cat cat =(cat) animal;  //本來是貓,已經被當做動物了,還原回來成爲本來的貓

運行結果:

Exception in thread “main” java.lang.ClassCastException: 狗類轉換異常
😼貓吃魚
貓捉老鼠

【注意事項】

a. 在發生向下轉型的時候,必須先向下轉型。就是必須保證對象本來創建的時候,就是貓,才能向下轉型成爲貓。

b. 如果對象創建的時候本來不是貓,現在非要向下轉型成爲貓,就會報錯ClassCastException<類型轉換異常>

可以類比爲強制轉換

int num = (int) 10.0;

做個總結:

多態就是一個對象擁有多種表現的形態

這就是面向對象的一些知識要點了。其實,隨着學習的不斷地深入,面向對象的精華是思想在項目和框架中的應用。


【參考鏈接】

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