深入理解Java的面向?qū)ο?/h2>

一、OOA OOD OOP

和C語言的面向過程編程不同,Java是面向?qū)ο缶幊痰恼Z言。即在分析業(yè)務(wù)的時候,將業(yè)務(wù)中的元素用一個個對象去代替,然后再根據(jù)這些對象去設(shè)計程序,最后面向這些對象編程。

OOA:面向?qū)ο蠓治?/p>

OOD:面向?qū)ο笤O(shè)計

OOP:面向?qū)ο缶幊?/p>

二、面向?qū)ο蟮娜筇匦?/h3>

封裝??

1、封裝的概念

將類的某些信息隱藏在類的內(nèi)部,不允許外部程序直接訪問,而是通過該類提供的方法來對隱藏的信息進行操作和訪問。

2、好處

(1)只能通過規(guī)定的方法訪問數(shù)據(jù)

(2)隱藏類的實例細節(jié),方便修改和實現(xiàn)。

3、封裝的實現(xiàn)步驟

(1)修改屬性的可見性設(shè)為(private)

(2)創(chuàng)建getter/setter方法(用于屬性的讀寫)(通過這兩種方法對數(shù)據(jù)進行獲取和設(shè)定,對象通過調(diào)用這兩種發(fā)方法實現(xiàn)對數(shù)據(jù)的讀寫)

(3)在getter/setter方法中加入屬性控制語句(對屬性值的合法性進行判斷)

4、Java中的包

(1)作用:管理java文件,解決同名文件的沖突

(2)定義包:package 包名

必須放在java源程序的第一行,包名間可以使用“.”號隔開例如:com.zzr.Myclass

5、包的使用。

(1)可以通過import關(guān)鍵字,在某個文件中使用其他文件中的類。

import.com.zzr.music.MyClass

(2)java中,包的名字規(guī)范是全小寫的字母拼寫

(3)使用時,不僅可以加載某個包下的所有文件? 比如:com.zzr.*

也可以加載某個具體子包下的所有文件? 比如:com.zzr.music.*

6、java中的訪問修飾符

private:只能在本類中使用(正因為private中的元素不能在外面直接訪問,所以才利用調(diào)用getter/setter方法訪問)

默認:本來和同包中使用

protected:本類,同包,子類中使用

public:本類,同包,子類,其他中都可以使用

7、java中的this關(guān)鍵字

this關(guān)鍵字代表當前對象

this.屬性:操作當前對象的屬性

this.方法:調(diào)用當前對象的方法

封裝對象的屬性的時候,經(jīng)常會使用this關(guān)鍵字

例如:

8、內(nèi)部類

(1)定義:內(nèi)部類( Inner Class )就是定義在另外一個類里面的類。與之對應(yīng),包含內(nèi)部類的類被稱為外部類。

(2)作用:內(nèi)部類提供了更好的封裝,可以把內(nèi)部類隱藏在外部類之內(nèi),不允許同一個包中的其他類訪問該類

內(nèi)部類的方法可以直接訪問外部類的所有數(shù)據(jù),包括私有的數(shù)據(jù)(是在內(nèi)部類的方法內(nèi)使用)

內(nèi)部類所實現(xiàn)的功能使用外部類同樣可以實現(xiàn),只是有時使用內(nèi)部類更方便

(3)分類:

成員內(nèi)部類(外部類中的一個類,直接public class 定義)

靜態(tài)內(nèi)部類(比成員內(nèi)部類多了個static,成員變量的訪問以及類的實例化都有變化)

方法內(nèi)部類(在外部類的方法內(nèi)部的類,不能在別的方法使用)

匿名內(nèi)部類

實例:

注意內(nèi)部類對象的創(chuàng)建。

9、內(nèi)部類之成員內(nèi)部類

定義:定義在一個類中的類似于這個類的一個成員變量的內(nèi)部類成為***成員內(nèi)部類***

成員內(nèi)部類的使用方法:例如:

(1) Inner 類定義在 Outer 類的內(nèi)部,相當于 Outer 類的一個成員變量的位置,Inner 類可以使用任意訪問控制符,如 public 、 protected 、 private 等

(2) Inner 類中定義的 test() 方法可以直接訪問 Outer 類中的數(shù)據(jù),而不受訪問控制符的影響,如直接訪問 Outer 類中的私有屬性a

(3) 定義了成員內(nèi)部類后,必須使用外部類對象來創(chuàng)建內(nèi)部類對象,而不能直接去 new 一個內(nèi)部類對象,即:內(nèi)部類 對象名 = 外部類對象.new 內(nèi)部類( );

(4) 編譯上面的程序后,會發(fā)現(xiàn)產(chǎn)生了兩個 .class 文件

其中,第二個是外部類的 .class 文件,第一個是內(nèi)部類的 .class 文件,即成員內(nèi)部類的 .class 文件總是這樣:外部類名$內(nèi)部類名.class

(5)外部類是不能直接調(diào)用內(nèi)部類的變量和方法的,可先創(chuàng)建內(nèi)部類的對象,然后通過內(nèi)部類的對象來訪問其成員變量和方法。

(6)如果外部類和內(nèi)部類具有相同的成員變量或方法,內(nèi)部類默認訪問自己的成員變量或方法,如果要訪問外部類的成員變量,可以使用 this 關(guān)鍵字。如:

10、靜態(tài)內(nèi)部類

定義:被static修飾的內(nèi)部類成為靜態(tài)內(nèi)部類

使用方法:

(1)靜態(tài)內(nèi)部類不能直接訪問外部類的非靜態(tài)成員,但可以通過 new 外部類().成員 的方式訪問,靜態(tài)成員還是可以繼續(xù)直接訪問的

(2)如果外部類的靜態(tài)成員與內(nèi)部類的成員名稱相同,可通過***“類名.靜態(tài)成員”訪問外部類的靜態(tài)成員***(這里和成員內(nèi)部類是不同的,成員內(nèi)部類是通過類名.this.變量名來調(diào)用的);如果外部類的靜態(tài)成員與內(nèi)部類的成員名稱不相同,則可通過“成員名”直接調(diào)用外部類的靜態(tài)成員

(3) 創(chuàng)建靜態(tài)內(nèi)部類的對象時,不需要外部類的對象,可以直接創(chuàng)建 內(nèi)部類 對象名= new 內(nèi)部類();

11、方法內(nèi)部類:

定義:方法內(nèi)部類就是內(nèi)部類定義在外部類的方法中,方法內(nèi)部類只在該方法的內(nèi)部可見,即只在該方法內(nèi)可以使用。例如:

注意:(1)因為是方法內(nèi)部類不能被除了方法之外所使用,所以不能直接mo.print().應(yīng)該是mo.show().因為這個內(nèi)部類是定義在show這個方法中的。

(2)由于方法內(nèi)部類不能在外部類的方法以外的地方使用,因此方法內(nèi)部類不能使用訪問控制符和 static 修飾符。

12、總結(jié)

(1)只有是成員內(nèi)部類創(chuàng)建內(nèi)部類對象時候才需要通過外部類對象來創(chuàng)建。其他都是可以直接創(chuàng)建的。

(2)當外部類和內(nèi)部類變量重名的話,想要調(diào)用外部變量。成員內(nèi)部類需要(類名.this.變量名)。靜態(tài)內(nèi)部類需要是(類名.變量名)

2、繼承

繼承的特點

1、Java的繼承通過extends關(guān)鍵字實現(xiàn).

2、實現(xiàn)繼承的類被稱為子類.

3、被繼承的類被稱為父類.

父類和子類的關(guān)系, 是一種一般和特殊的關(guān)系.

例如水果和蘋果的關(guān)系, 蘋果繼承了水果, 蘋果是水果的子類, 水果是蘋果的父類.

Java里子類繼承父類的語法格式如下:

從上面的語法格式來看, 定義子類的語法非常簡單, 只需要在原來的類定義上增加 extends SuperClass 即可.

即表明該類是 SuperClass 的子類.

為什么國內(nèi)把 extends 翻譯為 繼承 而不是 擴展呢?

除了歷史原因, 還有一點.子類繼承了父類, 也將獲得父類的全部成員變量和方法.這與我們現(xiàn)實中子輩從父輩那里獲得一筆財富的繼承關(guān)系很像.但是, Java的子類不能繼承父類的構(gòu)造器.

重寫父類的方法

子類包含與父類同名方法的現(xiàn)象稱為方法重寫(Override). 也被稱為方法覆蓋.

可以說子類重寫了父類的方法, 也可以說子類覆蓋了父類的方法, 都行.

方法的重寫要遵循兩同兩小一大規(guī)則.

兩同: 方法名相同 / 形參列表相同

兩小: 子類方法返回值類型應(yīng)比父類方法返回值類型小或相等. / 子類方法聲明拋出的異常類應(yīng)比父類方法聲明拋出的異常類更小或相等.

一大: 子類方法的訪問權(quán)限應(yīng)比父類方法的訪問權(quán)限大或相等.

尤其需要指出, 覆蓋方法和被覆蓋方法要么都是類方法, 要么都是實例方法.不能一個是類方法, 一個是實例方法,

當子類覆蓋了父類方法后, 子類的對象將無法訪問父類中被覆蓋的方法.但可以在子類方法中調(diào)用父類中被覆蓋的方法.如果需要在子類方法中調(diào)用父類中被覆蓋的方法, 則可以使用super(被覆蓋的是實例方法) 或者 父類類名(被覆蓋的是類方法) 來作為調(diào)用者,?調(diào)用父類中被覆蓋的方法.

如果父類方法具有 private 訪問權(quán)限, 則該方法對其子類是隱藏的.因此子類無法訪問該方法, 也就無法重寫該方法.

如果子類中定義了一個與父類 private 方法具有相同的方法名 / 相同的形參列表 / 相同的返回值類型的方法,依然不是重寫.這只是在子類中重新定義了一個新的方法.

漲姿勢:?

方法重載和方法重寫在英文中分別是 overload 和 override?

重載和重寫 并不是同一種東西, 雖然二者都是發(fā)生在方法之間, 并要求方法名相同之外, 并沒有太大相似之處.?

因為重載主要發(fā)生在同一個類的多個同名方法之間.?

而重寫發(fā)生在子類和父類的同名方法之間.?

當然, 父類方法和子類方法之間也有可能發(fā)生重載, 因為子類會獲得父類方法.?

如果子類定義了一個與父類方法有相同方法名, 但參數(shù)列表不同的方法, 就會形成父類方法和子類方法的重載.

super限定

如果需要在子類方法中調(diào)用父類被覆蓋的實例方法. 則可以使用 super 限定來調(diào)用父類被覆蓋的實例方法.?

super 是 Java提供的一個關(guān)鍵字, super 用于限定該對象調(diào)用它從父類繼承得到的實例變量或方法.

正如 this 不能出現(xiàn)在 static 修飾的方法中一樣, super 也不能出現(xiàn)在 static 修飾的方法中.

static 修飾的方法是屬于類的.

該方法的調(diào)用者可能是一個類, 而不是對象, 因而 super 限定也就失去了意義.

如果在構(gòu)造器中使用 super

則 super 用于限定該構(gòu)造器初始化的是該對象從父類繼承得到的實例變量, 而不是該類自己定義的實例變量.

如果子類定義了和父類同名的實例變量.

則會發(fā)生子類實例變量隱藏父類實例變量的情形.

在正常情況下, 子類里定義的方法直接訪問該實例變量默認會訪問到子類中定義的實例變量.

無法訪問到父類中被隱藏的實例變量.

在子類定義的實例方法中可以通過 super 來訪問父類中被隱藏的實例變量.

漲姿勢:

當程序創(chuàng)建一個子類對象時.

系統(tǒng)不僅會為該類中定義的實例變量分配內(nèi)存.

也會為它從父類繼承得到的所有實例變量分配內(nèi)存.

即使子類定義了與父類中同名的實例變量.

也就是說, 當系統(tǒng)創(chuàng)建一個 java 對象時.

如果該 java 類有兩個父類(一個直接父類 A / 一個間接父類 B)

假設(shè) A 類中定義了 2 個實例變量, B 類中定義了 3 個實例變量.

當前類中定義了 2 個實例變量, 那么這個 java 對象會保存 2 + 3 + 2 個實例變量.

因為子類中定義與父類中同名的實例變量并不會完全覆蓋父類中定義的實例變量, 它只是簡單的隱藏了父類中實例變量, 所以會出現(xiàn)如下特殊情況:

調(diào)用父類構(gòu)造器

子類不會獲得父類的構(gòu)造器.

但子類構(gòu)造器里可以調(diào)用父類構(gòu)造器的初始化代碼.

類似于前面所介紹的一個構(gòu)造器調(diào)用另一個重載的構(gòu)造器.

在一個構(gòu)造器中調(diào)用另一個重載的構(gòu)造器使用 this 調(diào)用來完成.

在子類構(gòu)造器中調(diào)用父類構(gòu)造器使用 super 調(diào)用來完成.

使用 super 調(diào)用和使用 this 調(diào)用也很像.

區(qū)別在于 super 調(diào)用的是其父類的構(gòu)造器, 而 this 調(diào)用的是同一個類中重載的構(gòu)造器.

因此, 使用 super 調(diào)用父類構(gòu)造器也必需出現(xiàn)在子類構(gòu)造器執(zhí)行體的第一行.

所以 this 調(diào)用 和 super 調(diào)用不會同時出現(xiàn).

不管是否使用 super 調(diào)用來執(zhí)行父類構(gòu)造器的初始化代碼.

子類構(gòu)造器總會調(diào)用父類構(gòu)造器一次.

子類構(gòu)造器調(diào)用父類構(gòu)造器分如下幾種情況:

子類構(gòu)造器執(zhí)行體的第一行使用 super 顯式調(diào)用父類構(gòu)造器.系統(tǒng)將根據(jù) super 調(diào)用里傳入的實參列表調(diào)用父類對應(yīng)的構(gòu)造器.

子類構(gòu)造器執(zhí)行體的第一行代碼使用 this 顯式調(diào)用本類中重載的構(gòu)造器,系統(tǒng)將根據(jù) this 調(diào)用里傳入的實參列表調(diào)用本類中的另一個構(gòu)造器.執(zhí)行本類中另一個構(gòu)造器時即會調(diào)用父類構(gòu)造器.

子類構(gòu)造器執(zhí)行體中既沒有 super 調(diào)用, 也沒有 this 調(diào)用, 系統(tǒng)將會在執(zhí)行子類構(gòu)造器之前, 隱式調(diào)用父類無參數(shù)的構(gòu)造器.

3、多態(tài)

我們用一個例子進行說明。

這無非就是一個簡單運行時多態(tài)問題,眾所周知,Java的多態(tài)分為編譯時多態(tài)和運行時多態(tài)兩種。其中, 編譯時多態(tài)主要指方法的重載,運行時多態(tài)指程序中定義的對象引用所指向的具體類型在運行期間才確定。 運行時多態(tài)有三個條件:如List<String> list=new ArrayList<>();

1、繼承

2、方法重寫(覆蓋)

3、向上轉(zhuǎn)型

main函數(shù)中,base指向的實際上是Impl對象,因此在調(diào)用sayHi()方法時,會執(zhí)行Impl對象的sayHi()方法而非Base對象的sayHi()方法,因此,輸出的內(nèi)容顯然是Impl:Hi。

實際上是這樣的嗎?然而很不幸,輸出的卻是 Base:Hi。問題出在哪兒?難道這不滿足運行時多態(tài)的條件?不應(yīng)該啊,首先,Impl類繼承了Base類,其次,Impl類重寫了Base類的sayHi()方法,最后,調(diào)用時進行了向上轉(zhuǎn)型。三個貌似條件都滿足,但是等等,回憶一下方法重寫的要求:

子類方法的訪問權(quán)限必須大于等于父類方法;

子類方法的返回類型必須是父類方法返回類型或為父類方法返回類型的子類型。

上述問題的原因在于父類的方法是由private修飾,所以子類的方法并不是對父類的重寫,而是一個全新的方法,因為父類的private方法是對子類隱藏的。所以并沒有符合重寫,所以也就不是多態(tài)了。

總結(jié):要想實現(xiàn)多態(tài),一定要完全符合多態(tài)的三個條件,缺一不可。

引用(本文章只供本人學習以及學習的記錄,如有侵權(quán),請聯(lián)系我刪除)

Java中的封裝

java中類的繼承詳解

徹底理解Java中的訪問修飾符

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容