1. 程式人生 > >Java程式設計師從笨鳥到菜鳥之(三)面向物件之封裝,繼承,多型(下)

Java程式設計師從笨鳥到菜鳥之(三)面向物件之封裝,繼承,多型(下)

五:再談繼承

   繼承是一種聯結類的層次模型,並且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。物件的一個新類可以從現有的類中派生,這個過程稱為類繼承。新類繼承了原始類的特性,新類稱為原始類的派生類(子類),而原始類稱為新類的基類(父類)。派生類可以從它的基類那裡繼承方法和例項變數,並且類可以修改或增加新的方法使之更適合特殊的需要。私有成員能繼承,但是由於訪問許可權的控制,在子類中不能直接使用父類的私有成員。並且java是單繼承,一個子類只能有一個父類

繼承中的構造方法

     當生成子類物件時,Java預設首先呼叫父類的不帶引數的構造方法,然後執行該構造方法,生成父類的物件。接下來,再去呼叫子類的構造方法,生成子類的物件。【要想生成子類的物件,首先需要生成父類的物件,沒有父類物件就沒有子類物件。比如說:沒有父親,就沒有孩子】。

     如果子類使用super()顯式呼叫父類的某個構造方法,那麼在執行的時候就會尋找與super()所對應的構造方法而不會再去尋找父類的不帶引數的構造方法。與this一樣,super也必須要作為構造方法的第一條執行語句,前面不能有其他可執行語句。 

      當兩個方法形成重寫關係時,可以在子類方法中通過super.run()形式呼叫父類的run()方法,其中super.run()不必放在第一行語句,因此此時父類物件已經構造完畢,先呼叫父類的run()方法還是先呼叫子類的run()方法是根據程式的邏輯決定的。 

方法的覆蓋(重寫)

 重寫的要求:子類覆蓋方法和父類被覆蓋方法的方法返回型別,方法名稱,引數列表必須相同

子類覆蓋方法的訪問許可權必須大於等於父類的方法的訪問許可權

方法覆蓋只能存在於子類和父類之間

子類覆蓋方法不能比父類被覆蓋方法丟擲更多異常

方法重寫與方法過載之間的關係:過載發生在同一個類內部的兩個或多個方法。重寫發生在父類與子類之間。 

final關鍵字在繼承中的使用

final可以用於以下四個地方:

定義變數,包括靜態的和非靜態的。 

如果final修飾的是一個基本型別,就表示這個變數被賦予的值是不可變的,即它是個常量;如果final修飾的是一個物件,就表示這個變數被賦予的引用是不可變的,不可改變的只是這個變數所儲存的引用,並不是這個引用所指向的物件,其實更貼切的表述final的含義的描述,那就是,如果一個變數或方法引數被final修飾,就表示它只能被賦值一次,但是JAVA虛擬機器為變數設定的預設值不記作一次賦值。

被final修飾的變數必須被初始化。初始化的方式有以下幾種:

1. 在定義的時候初始化。

2. 在初始化塊中初始化。

3. 在類的構造器中初始化。

4. 靜態變數也可以在靜態初始化塊中初始化。

1) 定義方法。 

當final用來定義一個方法時,它表示這個方法不可以被子類重寫,但是它這不影響它被子類繼承。

說明:

具有private訪問許可權的方法也可以增加final修飾,但是由於子類無法繼承private方法,因此也無法重寫它。編譯器在處理private方法時,是按照final方法來對待的,這樣可以提高該方法被呼叫時的效率。不過子類仍然可以定義同父類中的private方法具有同樣結構的方法,但是這並不會產生重寫的效果,而且它們之間也不存在必然聯絡。

3)定義類。 

由於final類不允許被繼承,編譯器在處理時把它的所有方法都當作final的,因此final類比普通類擁有更高的效率。final的類的所有方法都不能被重寫,但這並不表示final的類的屬性(變數)值也是不可改變的,要想做到final類的屬性值不可改變,必須給它增加final修飾。

關於繼承的幾點注意:

a) 父類有的,子類也有 

b) 父類沒有的,子類可以增加 

c) 父類有的,子類可以改變 

d) 構造方法不能被繼承 

e) 方法和屬性可以被繼承 

f) 子類的構造方法隱式地呼叫父類的不帶引數的構造方法 

g) 當父類沒有不帶引數的構造方法時,子類需要使用super來顯

式地呼叫父類的構造方法,super指的是對父類的引用 

h) super關鍵字必須是構造方法中的第一行語句。 

六:然後議多型

多型(Polymorphism):用我們通俗易懂的話來說就是子類就是父類(貓是動物,學生也是人),因此多型的意思就是:父型別的引用可以指向子類的物件。

方法的重寫、過載與動態連線構成多型性。Java之所以引入多型的概念,原因之一是它在類的繼承問題上和C++不同,後者允許多繼承,這確實給其帶來的非常強大的功能,但是複雜的繼承關係也給C++開發者帶來了更大的麻煩,為了規避風險,Java只 允許單繼承,派生類與基類間有IS-A的關係(即”is a “動物)。這樣做雖然保證了繼承關係的簡單明瞭,但是勢必在功能上有很大的限制,所以,Java引入了多型性的概念以彌補這點的不足,此外,抽象類和接 口也是解決單繼承規定限制的重要手段。同時,多型也是面向物件程式設計的精髓所在。 

在一個類中,可以定義多個同名的方法,只要確定它們的引數個數和型別不同,這種現象稱為類的多型。類的多型性體現在兩方面:一是方法的過載上,包括成員方法和構造方法的過載;二是在繼承過程中,方法的重寫。

多型性是面向物件的重要特徵。方法過載和方法覆寫實際上屬於多型性的一種體現,真正的多型性還包括物件多型性的概念。

物件多型性主要是指子類和父類物件的相互轉換關係。

a) 向上型別轉換(upcast):比如說將Cat型別轉換為Animal型別,即將子型別轉換為父型別。對於向上型別轉換,不需要顯式指定。

b) 向下型別轉換(downcast):比如將Animal型別轉換為Cat型別。即將父型別轉換為子型別。對於向下型別轉換,必須要顯式指定(必須要使用強制型別轉換)。

網上摘抄的一段多型小總結:

1. Java中除了staticfinal方法外,其他所有的方法都是執行時繫結的。在我另外一篇文 章中說到private方法都被隱式指定為final的,因此final的方法不會在執行時繫結。當在派生類中重寫基類中staticfinal、或 private方法時,實質上是建立了一個新的方法。2.在派生類中,對於基類中的private方法,最好採用不同的名字。3.包含抽象方法的類叫做抽象類。注意定義裡面包含這樣的意思,只要類中包含一個抽象方法,該類就是抽象類。抽象類在派生中就是作為基類的角色,為不同的子類提供通用的介面。4.物件清理的順序和建立的順序相反,當然前提是自己想手動清理物件,因為大家都知道Java垃圾回收器。5.在基類的構造方法中小心呼叫基類中被重寫的方法,這裡涉及到物件初始化順序。6.構造方法是被隱式宣告為static方法。7.用繼承表達行為間的差異,用欄位表達狀態上的變化。 

七、大談抽象

抽象類(abstract class):使用了abstract關鍵字所修飾的類叫做抽象類。抽象類無法例項化,也就是說,不能new出來一個抽象類的物件(例項)。

抽象方法(abstract method):使用abstract關鍵字所修飾的方法叫做抽象方法。抽象方法需要定義在抽象類中。相對於抽象方法,之前所定義的方法叫做具體方法(有宣告,有實現)。

如果一個類包含了抽象方法,那麼這個類一定是抽象類。

如果某個類是抽象類,那麼該類可以包含具體方法(有宣告、有實現)。

如果一個類中包含了抽象方法,那麼這個類一定要宣告成abstract class,也就是說,該類一定是抽象類;反之,如果某個類是抽象類,那麼該類既可以包含抽象方法,也可以包含具體方法。

無論何種情況,只要一個類是抽象類,那麼這個類就無法例項化。

在子類繼承父類(父類是個抽象類)的情況下,那麼該子類必須要實現父類中所定義的所有抽象方法;否則,該子類需要宣告成一個abstract class。

八:最後談介面

       Java語言不支援一個類有多個直接的父類(多繼承),但現例項子中,又有很多類似於多繼承的例子,比如教師,他的父類既可以是人,也可以是父母,所以,在java中就用繼承來填充這個空缺,java不可以多繼承, 但可以實現(implements)多個介面,間接的實現了多繼承

         Java介面的特徵歸納:

1, Java介面中的成員變數預設都是public,static,final型別的(都可省略),必須被顯示初始化,即介面中的成員變數為常量(大寫,單詞之間用"_"分隔)

   2, Java介面中的方法預設都是public,abstract型別的(都可省略),沒有方法體,不能被例項化 

   3, Java介面中只能包含public,static,final型別的成員變數和public,abstract型別的成員方法

   4, 介面中沒有構造方法,不能被例項化

   5, 一個介面不能實現(implements)另一個介面,但它可以繼承多個其它的介面

6, Java介面必須通過類來實現它的抽象方法

public class A implements B{...}

7, 當類實現了某個Java介面時,它必須實現介面中的所有抽象方法,否則這個類必須宣告為抽象的

8, 不允許建立介面的例項(例項化),但允許定義介面型別的引用變數,該引用變數引用實現了這個介面的類的例項

9, 一個類只能繼承一個直接的父類,但可以實現多個介面,間接的實現了多繼承.

10通過介面,可以方便地對已經存在的系統進行自下而上的抽象,對於任意兩個類,不管它們是否屬於同一個父類,只有它們存在相同的功能,就能從中抽象出一個接 口型別.對於已經存在的繼承樹,可以方便的從類中抽象出新的介面,但從類中抽象出新的抽象類卻不那麼容易,因此介面更有利於軟體系統的維護與重構.對於兩 個系統,通過介面互動比通過抽象類互動能獲得更好的鬆耦合.

11,、介面是構建鬆耦合軟體系統的重要法寶,由於介面用於描述系統對外提供的所有服務,因此介面中的成員變數和方法都必須是public型別的,確保外部使用者 能訪問它們,介面僅僅描述系統能做什麼,但不指明如何去做,所有介面中的方法都是抽象方法,介面不涉及和任何具體例項相關的細節,因此介面沒有構造方法不能被例項化,沒有例項變數.

比較抽象類與介面

相同點

1, 代表系統的抽象層,當一個系統使用一顆繼承樹上的類時,應該儘量把引用變數宣告為繼承樹的上層抽象型別,這樣可以提高兩個系統之間的送耦合

2, 都不能被例項化

3, 都包含抽象方法,這些抽象方法用於描述系統能提供哪些服務,但不提供具體的實現

不同點:

1, 在抽象類中可以為部分方法提供預設的實現,從而避免在子類中重複實現它們,這是抽象類的優勢,但這一優勢限制了多繼承,而介面中只能包含抽象方法.由於在 抽象類中允許加入具體方法,因此擴充套件抽象類的功能,即向抽象類中新增具體方法,不會對它的子類造成影響,而對於介面,一旦介面被公佈,就必須非常穩定,因 為隨意在介面中新增抽象方法,會影響到所有的實現類,這些實現類要麼實現新增的抽象方法,要麼宣告為抽象類

2, 一個類只能繼承一個直接的父類,這個父類可能是抽象類,但一個類可以實現多個介面,這是介面的優勢,但這一優勢是以不允許為任何方法提供實現作為代價的 三為什麼Java語言不允許多重繼承呢?當子類覆蓋父類的例項方法或隱藏父類的成員變數及靜態方法時,Java虛擬機器採用不同的繫結規則,假如還允許一個類 有多個直接的父類,那麼會使繫結規則更加複雜,

結論:

因此,為了簡化系統結構設計和動態繫結機制,Java語言禁止多重繼承.而介面中只有抽象方法,沒有例項變數和靜態方法,只有介面的實現類才會實現 介面的抽象方法(介面中的抽象方法是通過類來實現的),因此,一個類即使有多個介面,也不會增加Java虛擬機器進行動態繫結的複雜度.因為Java虛擬機器 永遠不會把方法與介面繫結,而只會把方法與它的實現類繫結.使用介面和抽象類的總體原則:

1, 用介面作為系統與外界互動的視窗站在外界使用者(另一個系統)的角度,介面向使用者承諾系統能提供哪些服務,站在系統本身的角度,介面制定系統必須實現哪 些服務,介面是系統中最高層次的抽象型別.通過介面互動可以提高兩個系統之間的送耦合系統A通過系統B進行互動,是指系統A訪問系統B,把引用變數宣告 為系統B中的介面型別,該引用變數引用系統B中介面的實現類的例項。

2, Java介面本身必須非常穩定,Java介面一旦制定,就不允許隨遇更加,否則對外面使用者及系統本身造成影響

3, 用抽象類來定製系統中的擴充套件點

抽象類來完成部分實現,還要一些功能通過它的子類來實現