Java的面向?qū)ο缶幊袒靖拍顚W(xué)習(xí)筆記整理
個(gè)人理解,編程中所謂的 類¨ 與現(xiàn)實(shí)世界中對物體的分門別類中的類是同一個(gè)概念,只是在編程中將其借用進(jìn)來。類代表有一系列共性和相同操作或動作的事物,在編程中即為抽象的數(shù)據(jù)類型。具體的每個(gè)個(gè)體(現(xiàn)實(shí)世界中)、實(shí)例變量(對于在編程中來說)就是對象。
類是現(xiàn)實(shí)世界某些對象的共同特征(屬性和操作)的表示,對象是類的實(shí)例。
類的屬性:就是類的靜態(tài)屬性的簡稱,指類內(nèi)包含的各項(xiàng)數(shù)據(jù),如變量或其他類的對象;
類的服務(wù): 則被稱為成員函數(shù)或方法。
¨
Java中類的定義形式如下:
[修飾符] class 類名 [extends 父類] [implements 接口名] { 類成員變量聲明 類方法聲明 }
我們再來仔細(xì)說說這中間的每一個(gè)部分:
在class關(guān)鍵字前,也即類的修飾符有大體分三種類型——訪問修飾符public公共類、final修飾符(最終類說明符)和abstract修飾符(抽象類說明符)
而其中,權(quán)限修飾符只能為public或默認(rèn)(即為空,什么都沒有,表示定義為友好的),public表示該類可被任何地方使用和訪問(只要程序能找到該類位置),無論是在同一包內(nèi),還是在不同包。注意,這與C++中不同,C++中沒有還對類的訪問權(quán)限用修飾符來限制的,而是對類之間的繼承關(guān)系有訪問權(quán)限的說明,除此之外,它們倒是都對類的屬性和方法有訪問權(quán)限有限制。 默認(rèn)的訪問權(quán)限(即定義為友好的),即是指只能被同一包內(nèi)的類引用和訪問,而不能被其它包中的類訪問和引用,即使import進(jìn)去。
后面還會提到:以類的屬性和方法缺省修飾符時(shí),也表示為只能被同一包中的類引用和訪問。
Java中不允許多重繼承,這與C++中不同,也算是為了彌補(bǔ)這個(gè)不足,Java中引進(jìn)了接口的概念。
上述類的定義中,類體中主要是類的具體內(nèi)容,包括類的屬性和類的方法。類的屬性可以是簡單變量,也可以是某些類的實(shí)例,如果是類的實(shí)例,可以如下定義:
[修飾符] 類名 對象名=new類名(參數(shù)列表);
在聲明對象和復(fù)雜變量時(shí),可以不在聲明時(shí)用創(chuàng)建,可以在以后的構(gòu)造函數(shù)中創(chuàng)建。
類中定義的方法通常起到兩種作用:一是圍繞著類的屬性進(jìn)行各種操作;二是與其他的類或?qū)ο筮M(jìn)行數(shù)據(jù)交流、消息傳遞等操作。
Java中聲明方法的語法如下:
[修飾符] 返回值類型 方法名(參數(shù)列表) throws 例外名1,例外名2,… { 方法體: 局部變量聲明; 語句序列; }
類的方法,又稱為成員函數(shù),用來規(guī)定類屬性上的操作,實(shí)現(xiàn)類的內(nèi)部功能的機(jī)制,同時(shí)也是類與外界進(jìn)行交互的重要窗口。
Java程序員把注意力放在創(chuàng)建稱為類的用戶自定義類型(user-definedtype)上,類也稱為程序員定義的類型(programmer-definedtype),每個(gè)類都含有數(shù)據(jù)和一組操作數(shù)據(jù)的方法,類中的數(shù)據(jù)部分稱為實(shí)例變量。用戶定義類型(即類)的實(shí)例稱為對象。
對象是類的一個(gè)實(shí)例,類是同種對象的抽象,是創(chuàng)建對象的模板。在程序中創(chuàng)建—個(gè)對象將在內(nèi)存中開辟一塊空間,其中包括該對象的屬性和方法。創(chuàng)建對象使用關(guān)鍵字運(yùn)算符new。
構(gòu)造函數(shù)(可以對比C++中,與C++幾乎相同)
創(chuàng)建自己的構(gòu)造函數(shù)
•構(gòu)造函數(shù)的名字和類的名字是相同的。當(dāng)構(gòu)造Employee類的對象時(shí),此構(gòu)造函數(shù)被啟動,實(shí)例字段賦初值,在Java中,定義和初始化是統(tǒng)一的——兩者缺一不可。
例如,用下面的代碼創(chuàng)建Employee類的一個(gè)實(shí)例時(shí),
newEmployee (“James Bond”,100000,1950,1,1);
構(gòu)造函數(shù)的特點(diǎn)有:
(1)構(gòu)造函數(shù)和類具有相同的名字。
(2)一個(gè)類可以有多個(gè)構(gòu)造函數(shù)。
(3)構(gòu)造函數(shù)可以有0個(gè)、1個(gè)或多個(gè)參數(shù)。
(4)構(gòu)造函數(shù)沒有返回值。
(5)構(gòu)造函數(shù)總是和new運(yùn)算符一起被調(diào)用。
構(gòu)造函數(shù)的作用
(1)對象初始化
(2)引入更多的靈活度(變量賦值或更復(fù)雜的操作)
(3)Java中可以不定義構(gòu)造函數(shù)
Java中可以不定義構(gòu)造函數(shù),此時(shí)系統(tǒng)會自動為該系統(tǒng)生成一個(gè)默認(rèn)的構(gòu)造函數(shù)。這個(gè)構(gòu)造函數(shù)的名字與類名相同,它沒有任何形式參數(shù),也不完成任何操作。
方法概述
Java程序是由一個(gè)個(gè)類定義組成的,類有兩個(gè)部分:屬性和方法。屬性描述類是什么,方法描述類做什么。任何對象都有獨(dú)立的內(nèi)存存儲它的屬性。類的所有的對象共享存貯在內(nèi)存的方法。
換言之:方法是類的主要組成部分。在一個(gè)類中,程序的作用體現(xiàn)在方法中。
方法即是JAVA創(chuàng)建一個(gè)有名字的子程序。一個(gè)主方法和若干個(gè)子方法構(gòu)成。主方法調(diào)用其他方法,其他方法間也可互相調(diào)用,同一個(gè)方法可被一個(gè)或多個(gè)方法調(diào)用任意次。
在一個(gè)方法中定義另一個(gè)方法將產(chǎn)生語法錯(cuò)誤。
(1)最好避免局部變量“屏蔽”實(shí)例變量,在一個(gè)類中不使用同名標(biāo)識符就可以做到這一點(diǎn);方法調(diào)用中參數(shù)用來傳遞數(shù)值、傳遞引用,同時(shí)方法還可以嵌套、遞歸調(diào)用。
(2)方法體中如果指定了非void的返回值類型,方法中就必須包含一條return語句保證任何情況下都有返回?cái)?shù)值,return語句后面不能跟任何表達(dá)式;
Java程序的基本結(jié)構(gòu)如下:
引入Java類庫; 定義用戶類1 { 定義類1的若干變量或?qū)ο螅? 定義類1的方法1; 定義類1的方法2; … 定義類1的方法M1; } 定義用戶類2 { 定義類2的若干變量或?qū)ο螅? 定義類2的方法1; 定義類2的方法2; … 定義類2的方法M2 }
Java推出了“訪問控制修飾符”的概念,允許庫創(chuàng)建者聲明哪些東西是客戶程序員可以使用的,哪些是不可使用的。
這種訪問控制的級別在“最大訪問”和“最小訪問”的范圍之間,分別包括:public,“默認(rèn)”(無關(guān)鍵字),protected以及private。下面的列表說明訪問控制修飾符含義:
公共訪問控制符public
用于類:
Java中類的訪問控制符只有一個(gè):public,即公共的。一個(gè)類被聲明為公共類,表明它可以被所有的其他類所訪問和引用,這里的訪問和引用是指這個(gè)類作為整體是可見和可使用的,程序的其他部分可以創(chuàng)建這個(gè)類的對象、訪問這個(gè)類內(nèi)部可見的成員變量和調(diào)用它的可見的方法。
一個(gè)類作為整體對程序的其他部分可見,并不能代表類內(nèi)的所有屬性和方法也同時(shí)對程序的其他部分可見,前者只是后者的必要條件,類的屬性和方法能否為所有其他類所訪問,還要看這些屬性和方法自己的訪問控制符。
用于類內(nèi)屬性:
用public修飾的類內(nèi)屬性稱為公共屬性,若這個(gè)類是公共類則它可以被所有的其他類訪問。
缺省訪問控制符
用于類
假如一個(gè)類沒有訪問控制符,說明它具有缺省的訪問控制特性。這種缺省的訪問控制權(quán)規(guī)定該類只能被同一個(gè)包中的類訪問和引用,而不可以被其他包中的類使用,這種訪問特性稱為包訪問性。通過聲明類的訪問控制符可以使整個(gè)程序結(jié)構(gòu)清晰、嚴(yán)謹(jǐn),減少可能產(chǎn)生類間干擾和錯(cuò)誤。
用于類屬性
類內(nèi)的屬性和方法如果沒有訪問控制符號來限定,也說明它們具有包訪問性。
3 私有訪問控制符private
用private修飾的屬性或方法只能被該類自身所訪問和修改,而不能被任何其他類,包括該類的子類,來獲取和引用
1). 私有數(shù)據(jù)
例如有三個(gè)實(shí)例字段,它們含有在Employee類的實(shí)例內(nèi)部被操作的數(shù)據(jù)。
private string name;
private double salary;
private Date hireDay;
private(私有的)關(guān)鍵字用來確??梢栽L問這些實(shí)例字段的只能是Employee類本身的方法。
2).私有方法
在實(shí)現(xiàn)類時(shí),我們使所有的數(shù)據(jù)字段都是私有的,因?yàn)楣_的數(shù)據(jù)是危險(xiǎn)的。對于方法又是什么情況呢 ?雖然大多數(shù)方法是公開的,但是私有方法也經(jīng)常使用。這些方法只能被同一個(gè)分離的方法。
總起來說,在下面的情況下可以選擇私有方法:
(1)與類的使用者無關(guān)的那些方法。
(2)如果類的實(shí)現(xiàn)改變了,不容易維護(hù)的那些方法。
保護(hù)訪問控制符protected
用protected修飾的成員變量可以被三種類所引用:該類自身、與它在同一個(gè)包中的其他類、在其他包中的該類的子類。使用protected修飾符的主要作用是允許其他包中的它的子類來訪問父類的特定屬性。
protected關(guān)鍵字為我們引入了一種名為“繼承”的概念,它以現(xiàn)有的類為基礎(chǔ),并在其中加入新的成員,同時(shí)不會對現(xiàn)有的類產(chǎn)生影響——我們將這種現(xiàn)有的類稱為“基礎(chǔ)類”或者“基本類”(Base Class)。亦可改變那個(gè)類現(xiàn)有成員的行為。對于從一個(gè)現(xiàn)有類的繼承,我們說自己的新類“擴(kuò)展”(extends)了那個(gè)現(xiàn)有的類
私有保護(hù)訪問控制符private protected
private和protected按順序連用構(gòu)成一個(gè)完整的訪問控制符:私有保護(hù)訪問控制符。用privateprotected修飾的成員變量可以被兩種類訪問和引用,一種是該類本身,一種是該類的所有子類,不論這些子類是與該類在同一個(gè)包里,還是處于其他的包中。
相對于protected,privateprotected修飾符把同一包內(nèi)的非子類排除在可訪問的范圍之外,使得成員變量更專有于具有明確繼承關(guān)系的類,而不是松散地組合在一起的包。
靜態(tài)修飾符
static稱為靜態(tài)修飾符,它可以修飾類中的屬性和方法。
使用static(靜態(tài))關(guān)鍵字,可滿足兩方面的要求:
(1)一種情形是只想用一個(gè)存儲區(qū)域來保存一個(gè)特定的數(shù)據(jù)——無論要創(chuàng)建多少個(gè)對象,甚至根本不創(chuàng)建對象;被static修飾的屬性稱為靜態(tài)屬性,這類屬性一個(gè)最本質(zhì)的特點(diǎn)是:它們是類的屬性,而不屬于任何一個(gè)類的具體對象。換句話說,對于該類的任何一個(gè)具體對象而言,靜態(tài)屬性是一個(gè)公共的存儲單元,任何一個(gè)類的對象訪問它時(shí),取到的都是相同的數(shù)值,同樣任何一個(gè)類的對象去修改它時(shí),也都是在對同一個(gè)內(nèi)存單元做操作。
(2)另一種情形是我們需要一個(gè)特殊的方法,它沒有與這個(gè)類的任何對象關(guān)聯(lián)。也就是說,即使沒有創(chuàng)建對象,也需要一個(gè)能用類直接調(diào)用的方法。
static一項(xiàng)重要的用途就是幫助我們在不必創(chuàng)建對象的前提下調(diào)用那個(gè)方法。
靜態(tài)常量
靜態(tài)變量是很少見的。然而,靜態(tài)常量卻很普遍。例如,Math類中定義了一個(gè)靜態(tài)常量:
public class Math
{ …… public static final double PI=3.1.4159265358979323846; …… }
靜態(tài)方法
聲明一個(gè)方法為static至少有三重含義:
(1)使用這個(gè)方法時(shí),應(yīng)該使用類名做前綴,而不是某一個(gè)具體的對象名;
(2)非static的方法是屬于某個(gè)對象的方法,在這個(gè)對象創(chuàng)建時(shí)對象的方法在內(nèi)存中擁有自己專用的代碼段;而static的方法是屬于整個(gè)類的,它在內(nèi)存中的代碼段將隨著類的定義而分配和裝載,不被任何一個(gè)對象專有;
(3)由于static方法是屬于整個(gè)類的,所以它不能操縱和處理屬于某個(gè)對象的成員變量,而只能處理屬于整個(gè)類的成員變量。
5 main方法
main方法并不對任何對象施加操作。實(shí)際上,當(dāng)程序開始執(zhí)行時(shí),還不存在任何對象。靜態(tài)方法被執(zhí)行,并構(gòu)造程序所需的對象。
提示 每個(gè)類都可以有一個(gè)main方法。這是對類進(jìn)行單元測試的一個(gè)很方便技巧。
abstract是抽象修飾符,可以用來修飾類或方法。
抽象類
當(dāng)一個(gè)類被聲明為abstract時(shí),這個(gè)類被稱為是抽象類。所謂抽象類就是沒有具體實(shí)例對象的類。
針對這個(gè)問題,Java專門提供了一種機(jī)制,名為“抽象方法”。它屬于一種不完整的方法,只含有一個(gè)聲明,沒有方法主體。下面是抽象方法聲明時(shí)采用的語法:
abstract void X();
抽象方法
作為類方法修飾符,abstract則聲明了一種僅有方法頭,而沒有具體的方法體和操作實(shí)現(xiàn)的抽象方法。
可見,abstract方法只有方法頭的聲明,而用一個(gè)分號來代替方法體的定義:至于方法體的具體實(shí)現(xiàn),那是由當(dāng)前類的不同子類在它們各自的類定義中完成的。
需要特別注意的是,所有的抽象方法,都必須存在于抽象類之
中。
除了抽象方法,抽象類也可以有具體的數(shù)據(jù)和方法。
在Java編程語言中抽象方法是非常重要的概念。在接口里你會大量的用到它。
注意:這里要與接口進(jìn)行對比、記憶,接口中的方法都屬于抽象方法,當(dāng)然,接口中也有屬性,其具體性質(zhì)將在后文詳述。
最終類、最終屬性、最終方法與終結(jié)器(C++中可沒有final最終修飾符)
final是最終修飾符,它可以修飾類、屬性和方法。另外終結(jié)器的關(guān)鍵字與final很相近,一并介紹
最終類
如果一個(gè)類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個(gè)類不能既被聲明為abstract的,又被聲明為final的。
被定義成final的類,通常是一些有特殊作用的、用來完成標(biāo)準(zhǔn)功能的類,將一個(gè)類定義為final則可以將它的內(nèi)容、屬性和功能固定下來,與它的類名形成穩(wěn)定的映射關(guān)系,從而保證引用這個(gè)類時(shí)所實(shí)現(xiàn)的功能是準(zhǔn)確無誤的
最終屬性
許多程序設(shè)計(jì)語言都有自己的辦法告訴編譯器某個(gè)數(shù)據(jù)是“常數(shù)”。常數(shù)主要應(yīng)用于下述兩個(gè)方面:
(1)編譯期常數(shù),它永遠(yuǎn)不會改變;
(2)在運(yùn)行期初始化的一個(gè)值,我們不希望它發(fā)生變化。
可以把一個(gè)實(shí)例字段定義為final(不能改變的)。在對象被構(gòu)造時(shí),這種字段必須被初始化。即,必須保證在每一個(gè)構(gòu)造函數(shù)結(jié)束之前其值已被設(shè)定。以后字段的值不能改變
最終方法
之所以要使用final方法,可能是出于對兩方面理由的考慮。
第一個(gè)是為方法“上鎖”,防止任何繼承類改變它的本來含義。設(shè)計(jì)程序時(shí),若希望一個(gè)方法的行為在繼承期間保持不變,而且不可被覆蓋或改寫,就可以采取這種做法。
采用final方法的第二個(gè)理由是程序執(zhí)行的效率
終結(jié)器
終結(jié)器的作用是回收對象時(shí)執(zhí)行的方法。類似與構(gòu)造函數(shù)是創(chuàng)建對象時(shí)執(zhí)行的方法一樣。
例
protected voidfinalize(){ System.out.println(“ “); }
其它修飾符
volatile
如果一個(gè)屬性被volatile修飾,說明這個(gè)屬性可以同時(shí)被幾個(gè)線程所控制和修改。
synchronized
主要用于線程同步
native
表示該方法不是用java語言寫成的(是用C,C++等語言寫的)
網(wǎng)上查到的一點(diǎn)資料:——內(nèi)部類
簡單的說,內(nèi)部類就是類中的類,舉個(gè)例子:
class A { private int i; private void m() { } class B { mm(int j) { i = j; m(); } } }
這里,B就是A的內(nèi)部類
內(nèi)部類的特點(diǎn)就是,可以方便的訪問外部類里面的私有方法和屬性,比如,這里B里面可以直接訪問A里面的私有屬性i,和私有方法m()
面向?qū)ο缶幊套钪匾奶卣骶褪欠庋b性(也可稱作抽象性)、繼承性和多態(tài)性,那么,作為面向?qū)ο缶幊陶Z言,Java在這方面更是有其出色之處:
“繼承性是軟件復(fù)用的一種形式,對降低軟件復(fù)雜性行之有效。繼承性同時(shí)是面向?qū)ο蟪绦蛟O(shè)計(jì)語言的特點(diǎn),采用對象但沒有繼承性的語言是基于對象的語言,但不是面向?qū)ο蟮恼Z言,這是兩者的區(qū)別”
類之間的繼承關(guān)系是現(xiàn)實(shí)世界中遺傳關(guān)系的直接模擬,它表示類之間的內(nèi)在聯(lián)系以及對屬性和操作的共享,即子類可以沿用父類(被繼承類)的某些特征。當(dāng)然,子類也可以具有自己獨(dú)立的屬性和操作
繼承性是軟件復(fù)用的一種形式。新類由已存在的類生成,通過保留它們的屬性和行為,并且根據(jù)新類的要求對性能加以修改,添加新的屬性和行為。如果子類只從一個(gè)父類繼承,則稱為單繼承;如果子類從一個(gè)以上父類繼承,則稱為多繼承。注意Java不支持多重繼承,但它支持“接口”概念。接口使Java獲得了多重繼承的許多優(yōu)點(diǎn),摒棄了相應(yīng)的缺點(diǎn)。注意:C++支持多繼承
繼承關(guān)系的定義:
[修飾符]class子類名 extends父類名,父類名2
父類名跟在extends
關(guān)鍵字后面,用來說明當(dāng)前類是哪個(gè)已經(jīng)存在類的子類,存在繼承關(guān)系。
定義 雇員類 Employee的兩個(gè)子類:
一般雇員類:CommonEmployee
主 管 類:ManagerEmployee
子類從父類繼承有兩個(gè)主要的方面:
(1)屬性的繼承。例如,公司是一個(gè)父類,一個(gè)公司有名稱、地址、經(jīng)理、雇員等,這些都屬于結(jié)構(gòu)方面。
(2)方法的繼承。一個(gè)父類定義了若干操作,如一個(gè)公司要有項(xiàng)目、利潤、任命經(jīng)理、錄用職工等操作,子公司也將繼承這些行為s;mp
classCommonEmployeeextends Employee//子類1: { intm_ManagerNo ;//定義類屬性m _ManagerNo,代表雇員上司的編號 } classManagerEmployeeextends Employee //子類2: { intm_SecretaryNo; //定義類屬性m_SecretaryNo,代表秘書的編號 }
屬性繼承與隱藏
盡管Employee類是一個(gè)父類,但是并不因?yàn)樗歉割惥鸵馕吨懈嗟墓δ?。恰恰相反,子類比它們的父類具有更多的功能。因?yàn)樽宇愂歉割惖臄U(kuò)展,增加了父類沒有的屬性和方法
(1)子類不能訪問父類的private成員,但子類可以訪問其父類的public,
(2)protected訪問是public和private訪問之間一個(gè)保護(hù)性的中間層次。
(3)由于被繼承的父類成員沒有在子類聲明中列出,但是這些成員確實(shí)存在于子類中。
在這里,要區(qū)分一下繼承、覆蓋與重載這幾個(gè)易混淆的概念:
只有方法這一概念層面,這三個(gè)概念才易混淆:
方法繼承
對于子類對象,可以使用父類中的方法。即使這些方法沒有明顯地在子類中定義,它們也自動地從父類中繼承過來了
方法覆蓋
方法的覆蓋是指:子類定義同名方法來覆蓋父類的方法,是多態(tài)技術(shù)的一個(gè)實(shí)現(xiàn)。當(dāng)父類方法在子類中被覆蓋時(shí),通常是子類版本調(diào)用父類版本,并做一些附加的工作。
有很多注意事項(xiàng),這里,我主要提一下this與super,C++中有this(且與Java中中概念差不多),但是沒有super。
this表示的是當(dāng)前對象本身,this代表當(dāng)前對象的一個(gè)引用??梢岳斫鉃閷ο蟮牧硪粋€(gè)名字。利用this可以調(diào)用當(dāng)前對象的方法和屬性。
如:this.getName()和getName()在類中是一樣的。
super表示的是當(dāng)前對象的直接父類對象,是當(dāng)前對象的父類對象的引用
方法重載
重載的定義:可以用相同的方法名但不同的參數(shù)表來定義方法(參數(shù)表中參數(shù)的數(shù)量、類型或次序有差異),這稱為方法重載。
•重載(overloading):當(dāng)多個(gè)方法具有相同的名字而含有不同的參數(shù)時(shí),便發(fā)生重載。編譯器必須挑選處調(diào)用哪個(gè)方法。它通過將在不同方法頭部中的參數(shù)類型和在特定的方法調(diào)用中使用值的類型進(jìn)行比較,從而挑選出正確的方法。
多態(tài)性允許以統(tǒng)一的風(fēng)格處理已存在的變量及相關(guān)的類,使增加系統(tǒng)中新功能變得容易。這里,貼一下網(wǎng)上找的資料,能更清楚地將繼承中需特別注意的多態(tài)、繼承中的問題弄清:
Java的多態(tài)性
面向?qū)ο缶幊逃腥齻€(gè)特征,即封裝、繼承和多態(tài)。
封裝隱藏了類的內(nèi)部實(shí)現(xiàn)機(jī)制,從而可以在不影響使用者的前提下改變類的內(nèi)部結(jié)構(gòu),同時(shí)保護(hù)了數(shù)據(jù)。
繼承是為了重用父類代碼,同時(shí)為實(shí)現(xiàn)多態(tài)性作準(zhǔn)備。那么什么是多態(tài)呢?
方法的重寫、重載與動態(tài)連接構(gòu)成多態(tài)性。Java之所以引入多態(tài)的概念,原因之一是它在類的繼承問題上和C++不同,后者允許多繼承,這確實(shí)給其帶來的非常強(qiáng)大的功能,但是復(fù)雜的繼承關(guān)系也給C++開發(fā)者帶來了更大的麻煩,為了規(guī)避風(fēng)險(xiǎn),Java只允許單繼承,派生類與基類間有IS-A的關(guān)系(即“貓”is a “動物”)。這樣做雖然保證了繼承關(guān)系的簡單明了,但是勢必在功能上有很大的限制,所以,Java引入了多態(tài)性的概念以彌補(bǔ)這點(diǎn)的不足,此外,抽象類和接口也是解決單繼承規(guī)定限制的重要手段。同時(shí),多態(tài)也是面向?qū)ο缶幊痰木杷凇?/p>
要理解多態(tài)性,首先要知道什么是“向上轉(zhuǎn)型”。
我定義了一個(gè)子類Cat,它繼承了Animal類,那么后者就是前者是父類。我可以通過
Cat c = new Cat();
實(shí)例化一個(gè)Cat的對象,這個(gè)不難理解。但當(dāng)我這樣定義時(shí):
Animal a = new Cat();
這代表什么意思呢?
很簡單,它表示我定義了一個(gè)Animal類型的引用,指向新建的Cat類型的對象。由于Cat是繼承自它的父類Animal,所以Animal類型的引用是可以指向Cat類型的對象的。那么這樣做有什么意義呢?因?yàn)樽宇愂菍Ω割惖囊粋€(gè)改進(jìn)和擴(kuò)充,所以一般子類在功能上較父類更強(qiáng)大,屬性較父類更獨(dú)特,
定義一個(gè)父類類型的引用指向一個(gè)子類的對象既可以使用子類強(qiáng)大的功能,又可以抽取父類的共性。
所以,父類類型的引用可以調(diào)用父類中定義的所有屬性和方法,而對于子類中定義而父類中沒有的方法,它是無可奈何的;
同時(shí),父類中的一個(gè)方法只有在在父類中定義而在子類中沒有重寫的情況下,才可以被父類類型的引用調(diào)用;
對于父類中定義的方法,如果子類中重寫了該方法,那么父類類型的引用將會調(diào)用子類中的這個(gè)方法,這就是動態(tài)連接。
看下面這段程序:
class Father{ public void func1(){ func2(); } //這是父類中的func2()方法,因?yàn)橄旅娴淖宇愔兄貙懥嗽摲椒? //所以在父類類型的引用中調(diào)用時(shí),這個(gè)方法將不再有效 //取而代之的是將調(diào)用子類中重寫的func2()方法 public void func2(){ System.out.println("AAA"); } } class Child extends Father{ //func1(int i)是對func1()方法的一個(gè)重載 //由于在父類中沒有定義這個(gè)方法,所以它不能被父類類型的引用調(diào)用 //所以在下面的main方法中child.func1(68)是不對的 public void func1(int i){ System.out.println("BBB"); } //func2()重寫了父類Father中的func2()方法 //如果父類類型的引用中調(diào)用了func2()方法,那么必然是子類中重寫的這個(gè)方法 public void func2(){ System.out.println("CCC"); } } public class PolymorphismTest { public static void main(String[] args) { Father child = new Child(); child.func1();//打印結(jié)果將會是什么? } }
上面的程序是個(gè)很典型的多態(tài)的例子。子類Child繼承了父類Father,并重載了父類的func1()方法,重寫了父類的func2()方法。重載后的func1(int i)和func1()不再是同一個(gè)方法,由于父類中沒有func1(int i),那么,父類類型的引用child就不能調(diào)用func1(int i)方法。而子類重寫了func2()方法,那么父類類型的引用child在調(diào)用該方法時(shí)將會調(diào)用子類中重寫的func2()。
那么該程序?qū)蛴〕鍪裁礃拥慕Y(jié)果呢?
很顯然,應(yīng)該是“CCC”。
對于多態(tài),可以總結(jié)它為:
(1)使用父類類型的引用指向子類的對象(實(shí)際對象);
(2)該引用只能調(diào)用父類中定義的方法和變量;
(3)如果子類中重寫了父類中的一個(gè)方法,那么在調(diào)用這個(gè)方法的時(shí)候,將會調(diào)用子類中的這個(gè)方法;(動態(tài)連接、動態(tài)調(diào)用)
(4)變量不能被重寫(覆蓋),”重寫“的概念只針對方法,如果在子類中”重寫“了父類中的變量,那么在編譯時(shí)會報(bào)錯(cuò)。
多態(tài)是通過:
(1) 接口 和 實(shí)現(xiàn)接口并覆蓋接口中同一方法的幾個(gè)不同的類體現(xiàn)的
(2) 父類 和 繼承父類并覆蓋父類中同一方法的幾個(gè)不同子類實(shí)現(xiàn)的.
1.基本概念
多態(tài)性:發(fā)送消息給某個(gè)對象,讓該對象自行決定響應(yīng)何種行為。
通過將子類對象引用賦值給超類對象引用變量來實(shí)現(xiàn)動態(tài)方法調(diào)用。
ava 的這種機(jī)制遵循一個(gè)原則:當(dāng)超類對象引用變量引用子類對象時(shí),被引用對象的類型而不是引用變量的類型決定了調(diào)用誰的成員方法,但是這個(gè)被調(diào)用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。
(1)如果a是類A的一個(gè)引用,那么,a可以指向類A的一個(gè)實(shí)例,或者說指向類A的一個(gè)子類。
(2)如果a是接口A的一個(gè)引用,那么,a必須指向?qū)崿F(xiàn)了接口A的一個(gè)類的實(shí)例。
Java多態(tài)性實(shí)現(xiàn)機(jī)制
SUN目前的JVM實(shí)現(xiàn)機(jī)制,類實(shí)例的引用就是指向一個(gè)句柄(handle)的指針,這個(gè)句柄是一對指針:
一個(gè)指針指向一張表格,實(shí)際上這個(gè)表格也有兩個(gè)指針(一個(gè)指針指向一個(gè)包含了對象的方法表,另外一個(gè)指向類對象,表明該對象所屬的類型);
另一個(gè)指針指向一塊從java堆中為分配出來內(nèi)存空間。
總結(jié)
(1)通過將子類對象引用賦值給超類對象引用變量來實(shí)現(xiàn)動態(tài)方法調(diào)用。
DerivedC c2=new DerivedC(); BaseClass a1= c2; //BaseClass 基類,DerivedC是繼承自BaseClass的子類 a1.play(); //play()在BaseClass,DerivedC中均有定義,即子類覆寫了該方法
分析:
* 為什么子類的類型的對象實(shí)例可以覆給超類引用?
自動實(shí)現(xiàn)向上轉(zhuǎn)型。通過該語句,編譯器自動將子類實(shí)例向上移動,成為通用類型BaseClass;
* a.play()將執(zhí)行子類還是父類定義的方法?
子類的。在運(yùn)行時(shí)期,將根據(jù)a這個(gè)對象引用實(shí)際的類型來獲取對應(yīng)的方法。所以才有多態(tài)性。一個(gè)基類的對象引用,被賦予不同的子類對象引用,執(zhí)行該方法時(shí),將表現(xiàn)出不同的行為。
在a1=c2的時(shí)候,仍然是存在兩個(gè)句柄,a1和c2,但是a1和c2擁有同一塊數(shù)據(jù)內(nèi)存塊和不同的函數(shù)表。
(2)不能把父類對象引用賦給子類對象引用變量
BaseClass a2=new BaseClass(); DerivedC c1=a2;//出錯(cuò)
在java里面,向上轉(zhuǎn)型是自動進(jìn)行的,但是向下轉(zhuǎn)型卻不是,需要我們自己定義強(qiáng)制進(jìn)行。
c1=(DerivedC)a2; 進(jìn)行強(qiáng)制轉(zhuǎn)化,也就是向下轉(zhuǎn)型.
(3)記住一個(gè)很簡單又很復(fù)雜的規(guī)則,一個(gè)類型引用只能引用引用類型自身含有的方法和變量。
你可能說這個(gè)規(guī)則不對的,因?yàn)楦割愐弥赶蜃宇悓ο蟮臅r(shí)候,最后執(zhí)行的是子類的方法的。
其實(shí)這并不矛盾,那是因?yàn)椴捎昧撕笃诮壎?,動態(tài)運(yùn)行的時(shí)候又根據(jù)型別去調(diào)用了子類的方法。而假若子類的這個(gè)方法在父類中并沒有定義,則會出錯(cuò)。
例如,DerivedC類在繼承BaseClass中定義的函數(shù)外,還增加了幾個(gè)函數(shù)(例如 myFun())
分析:
當(dāng)你使用父類引用指向子類的時(shí)候,其實(shí)jvm已經(jīng)使用了編譯器產(chǎn)生的類型信息調(diào)整轉(zhuǎn)換了。
這里你可以這樣理解,相當(dāng)于把不是父類中含有的函數(shù)從虛擬函數(shù)表中設(shè)置為不可見的。注意有可能虛擬函數(shù)表中有些函數(shù)地址由于在子類中已經(jīng)被改寫了,所以對象虛擬函數(shù)表中虛擬函數(shù)項(xiàng)目地址已經(jīng)被設(shè)置為子類中完成的方法體的地址了。
(4)Java與C++多態(tài)性的比較
jvm關(guān)于多態(tài)性支持解決方法是和c++中幾乎一樣的,
只是c++中編譯器很多是把類型信息和虛擬函數(shù)信息都放在一個(gè)虛擬函數(shù)表中,但是利用某種技術(shù)來區(qū)別。
Java把類型信息和函數(shù)信息分開放。Java中在繼承以后,子類會重新設(shè)置自己的虛擬函數(shù)表,這個(gè)虛擬函數(shù)表中的項(xiàng)目有由兩部分組成。從父類繼承的虛擬函數(shù)和子類自己的虛擬函數(shù)。
虛擬函數(shù)調(diào)用是經(jīng)過虛擬函數(shù)表間接調(diào)用的,所以才得以實(shí)現(xiàn)多態(tài)的。
Java的所有函數(shù),除了被聲明為final的,都是用后期綁定。
1個(gè)行為,不同的對象,他們具體體現(xiàn)出來的方式不一樣,
比如: 方法重載 overloading 以及 方法重寫(覆蓋)override
class Human{ void run(){輸出 人在跑} } class Man extends Human{ void run(){輸出 男人在跑} } 這個(gè)時(shí)候,同是跑,不同的對象,不一樣(這個(gè)是方法覆蓋的例子) class Test{ void out(String str){輸出 str} void out(int i){輸出 i} }
這個(gè)例子是方法重載,方法名相同,參數(shù)表不同
ok,明白了這些還不夠,還用人在跑舉例
Human ahuman=new Man();
這樣我等于實(shí)例化了一個(gè)Man的對象,并聲明了一個(gè)Human的引用,讓它去指向Man這個(gè)對象
意思是說,把 Man這個(gè)對象當(dāng) Human看了.
比如去動物園,你看見了一個(gè)動物,不知道它是什么, "這是什么動物? " "這是大熊貓! "
這2句話,就是最好的證明,因?yàn)椴恢浪谴笮茇?但知道它的父類是動物,所以,
這個(gè)大熊貓對象,你把它當(dāng)成其父類 動物看,這樣子合情合理.
這種方式下要注意 new Man();的確實(shí)例化了Man對象,所以 ahuman.run()這個(gè)方法 輸出的 是 "男人在跑 "
如果在子類 Man下你 寫了一些它獨(dú)有的方法 比如 eat(),而Human沒有這個(gè)方法,
在調(diào)用eat方法時(shí),一定要注意 強(qiáng)制類型轉(zhuǎn)換 ((Man)ahuman).eat(),這樣才可以...
對接口來說,情況是類似的...
實(shí)例:
package domatic; //定義超類superA class superA { int i = 100; void fun(int j) { j = i; System.out.println("This is superA"); } } // 定義superA的子類subB class subB extends superA { int m = 1; void fun(int aa) { System.out.println("This is subB"); } } // 定義superA的子類subC class subC extends superA { int n = 1; void fun(int cc) { System.out.println("This is subC"); } } class Test { public static void main(String[] args) { superA a = new superA(); subB b = new subB(); subC c = new subC(); a = b; a.fun(100); a = c; a.fun(200); } }
/*
* 上述代碼中subB和subC是超類superA的子類,我們在類Test中聲明了3個(gè)引用變量a, b,
* c,通過將子類對象引用賦值給超類對象引用變量來實(shí)現(xiàn)動態(tài)方法調(diào)用。也許有人會問:
* "為什么(1)和(2)不輸出:This is superA"。
* java的這種機(jī)制遵循一個(gè)原則:當(dāng)超類對象引用變量引用子類對象時(shí),
* 被引用對象的類型而不是引用變量的類型決定了調(diào)用誰的成員方法,
* 但是這個(gè)被調(diào)用的方法必須是在超類中定義過的,
* 也就是說被子類覆蓋的方法。
* 所以,不要被上例中(1)和(2)所迷惑,雖然寫成a.fun(),但是由于(1)中的a被b賦值,
* 指向了子類subB的一個(gè)實(shí)例,因而(1)所調(diào)用的fun()實(shí)際上是子類subB的成員方法fun(),
* 它覆蓋了超類superA的成員方法fun();同樣(2)調(diào)用的是子類subC的成員方法fun()。
* 另外,如果子類繼承的超類是一個(gè)抽象類,雖然抽象類不能通過new操作符實(shí)例化,
* 但是可以創(chuàng)建抽象類的對象引用指向子類對象,以實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)性。具體的實(shí)現(xiàn)方法同上例。
* 不過,抽象類的子類必須覆蓋實(shí)現(xiàn)超類中的所有的抽象方法,
* 否則子類必須被abstract修飾符修飾,當(dāng)然也就不能被實(shí)例化了
*/
以上大多數(shù)是以子類覆蓋父類的方法實(shí)現(xiàn)多態(tài).下面是另一種實(shí)現(xiàn)多態(tài)的方法-----------重寫父類方法
JAVA里沒有多繼承,一個(gè)類之能有一個(gè)父類。而繼承的表現(xiàn)就是多態(tài)。一個(gè)父類可以有多個(gè)子類,而在子類里可以重寫父類的方法(例如方法print()),這樣每個(gè)子類里重寫的代碼不一樣,自然表現(xiàn)形式就不一樣。這樣用父類的變量去引用不同的子類,在調(diào)用這個(gè)相同的方法print()的時(shí)候得到的結(jié)果和表現(xiàn)形式就不一樣了,這就是多態(tài),相同的消息(也就是調(diào)用相同的方法)會有不同的結(jié)果。舉例說明:
//父類 public class Father{ //父類有一個(gè)打孩子方法 public void hitChild(){ } } //子類1 public class Son1 extends Father{ //重寫父類打孩子方法 public void hitChild(){ System.out.println("為什么打我?我做錯(cuò)什么了!"); } } //子類2 public class Son2 extends Father{ //重寫父類打孩子方法 public void hitChild(){ System.out.println("我知道錯(cuò)了,別打了!"); } } //子類3 public class Son3 extends Father{ //重寫父類打孩子方法 public void hitChild(){ System.out.println("我跑,你打不著!"); } } //測試類 public class Test{ public static void main(String args[]){ Father father; father = new Son1(); father.hitChild(); father = new Son2(); father.hitChild(); father = new Son3(); father.hitChild(); } }
都調(diào)用了相同的方法,出現(xiàn)了不同的結(jié)果!這就是多態(tài)的表現(xiàn)!
import java.io.*; class Super{ Super(){ System.out.println("This is super class!"); } void method(){ System.out.println("Super's method"); } } class Sub extends Super{ Sub(){ super(); System.out.println("\n\t:and here is the child"); } void method(){ System.out.println("child's method"); } } public class Super_Sub{ public static void main(String[] args){ Super sup=new Sub(); sup.method(); Sub child=(Sub)new Super();//這里,實(shí)際分配的內(nèi)存是Super的,但是卻用Child來指代它,這就是“向下轉(zhuǎn)型”(父類冒充子類,因?yàn)樽宇愒赨ML中畫時(shí)是在下的嘛),必經(jīng)強(qiáng)制類型轉(zhuǎn)換 child.method(); } }
對于數(shù)據(jù)來說,繼承是否為正確的設(shè)計(jì)可以用一個(gè)簡單的規(guī)則來判斷。“is-a”規(guī)則表明子類的每一個(gè)對象都是一個(gè)超類的對象。例如,每一個(gè)經(jīng)理是一個(gè)員工。然而,只有經(jīng)理類是員工類的子類才是有意義的。很明顯,反過來就不行了——并不是每個(gè)員工都是經(jīng)理。
還有一個(gè)明確敘述“is-a”規(guī)則的方法是替代原則。該原則規(guī)定無論何時(shí),如果程序需要一個(gè)超類對象,都可以用一個(gè)子類對象來代替
動態(tài)綁定
理解調(diào)用一個(gè)對象方法的機(jī)制是非常重要的。下面具體介紹:X.f;
(1)編譯器檢查對象的聲明類型和方法名。
(2)接著,編譯器檢查方法調(diào)用中的參數(shù)類型。如果在所有的叫做f的方法中有一個(gè)其參數(shù)類型同調(diào)用提供的參數(shù)類型最匹配,那么該方法就會被選擇調(diào)用。這個(gè)過程稱作超載選擇。(靜態(tài))
(3)當(dāng)程序運(yùn)行并且使用動態(tài)綁定來調(diào)用一個(gè)方法時(shí),那么虛擬機(jī)必須調(diào)用同x所指向的對象的實(shí)際類型相匹配的方法版本。
……
如果類中沒有寫構(gòu)造函數(shù),那么系統(tǒng)會自動為該類提供一個(gè)默認(rèn)構(gòu)造函數(shù),該構(gòu)造函數(shù)將所有的實(shí)例字段初始化為默認(rèn)值:
……
包用途:
Java允許把多個(gè)類收集在一起成為一組,稱作包(package)。包便于組織任務(wù),以及使自己的任務(wù)和其他人提供的代碼庫相分離。
標(biāo)準(zhǔn)Java庫被分類成許多的包,其中包括java.1ang、java.util和java.net等等。標(biāo)準(zhǔn)Java包是分層次的。就像在硬盤上嵌套有各級子目錄一樣,可以通過層次嵌套組織包。所有的Java包都在Java和Javax包層次內(nèi)
創(chuàng)建包
已經(jīng)看到,已有的庫,比如JavaAPI中的類和接口,可以導(dǎo)入到Java程序中。
Java API中的每一個(gè)類和接口屬于一個(gè)特定的包。它包含一組相關(guān)聯(lián)的類和接口,實(shí)際是對類和接口進(jìn)行組織的目錄結(jié)構(gòu)。
例如,假定文件名是MyClass.java。它意味著在那個(gè)文件有一個(gè)、而且只能有一個(gè)public類。而且那個(gè)類的名字必須是MyClass(包括大小寫形式):
packagemypackage; publicclass MyClass { …… }
創(chuàng)建可復(fù)用的類的步驟簡要說明如下:
(1)定義一個(gè)public類。如果類不是public,它只能被同一包中的其他類使用。
(2)選擇一個(gè)包名,并把package語句加到可復(fù)用的類的源代碼文件中。
(3)編譯這個(gè)類。這樣,它就被放到適當(dāng)?shù)陌夸浗Y(jié)構(gòu)中,以供編譯器和解譯器使用。
(4)把這個(gè)可復(fù)用的類導(dǎo)入到需要用它的程序中?,F(xiàn)在就可以使用它了。
注意 在Java語言中可以出現(xiàn)在類定義的括號外面的僅有兩個(gè)語句,它們是package和import。
包引用---每個(gè)類名前加上完整的包名
例如,給出一個(gè)指向此包中的類的快捷方式。一旦使用import(導(dǎo)入)了以后,就不再需要給出完整的包名。
可以引入一個(gè)特定的類,也可以引入整個(gè)包。import語句要放在源文件的頭部(但在所有package語句的下面)。例如,可以通過下面的語句引入在java.util包中的所有的類:
importjava.util.*;
然后,就可以使用
Datetoday=new Date();
而不需要在前面加上包名。也可以引入包中某個(gè)特定的類:
importjava.util.Date;
要把類放人一個(gè)包中,必須把此包的名字放在源文件頭部,并且放在對包中的類進(jìn)行定義的代碼之前。例如,在文件Employee.java的開始部分如下:
packagecom.horstmann.corejava; publicclass Employee { …… }
把包中的文件放入與此完整的包名相匹配的子目錄中。例如,在包c(diǎn)om.horstmann.corejava中的所有的類文件都必須放在子目錄com/horstmann/core.java(Windows下的com\horstmann\corejava)下。這是最簡單的一種方法
類被存儲在文件系統(tǒng)的子目錄中。類的路徑必須與所在包名相匹配。
在前面的例子中,包目錄com/horstmann/corejava是程序目錄的一個(gè)子目錄。然而這樣安排很不靈活。一般,有多個(gè)程序需要訪問包文件。為了使包可以在多個(gè)程序間共享,需要做以下事情:
1)把類放在一個(gè)或多個(gè)特定的目錄中,比如/home/user/classdir。此目錄是包樹的基本目錄。如果加入了類com.horstmann.corejava.Employee,那么此類文件必須位于子目錄/home/user/classdir/com/horstmann/corejava下。
2)設(shè)置類路徑。類路徑是其子目錄包含類文件的所有基本目錄的集合。classpath
已經(jīng)接觸過public和private訪問指示符。
被標(biāo)記為Public的部件可以被任何類使用,而私有部件只能被定義它們的類使用。如果沒有指定public或private,那么部件(即類、方法或變量)可以被同一個(gè)包中的所有方法訪問。
Java API包
為了簡化面向?qū)ο蟮木幊踢^程,Java系統(tǒng)事先設(shè)計(jì)并實(shí)現(xiàn)了一些體現(xiàn)了常用功能的標(biāo)準(zhǔn)類,如用于輸入/輸出的類,用于數(shù)學(xué)運(yùn)算的類,用于圖形用戶界面設(shè)計(jì)的類,用于網(wǎng)絡(luò)處理的類等。這些系統(tǒng)標(biāo)準(zhǔn)類根據(jù)實(shí)現(xiàn)的功能不同,可以劃分成不同的集合,每個(gè)集合是一個(gè)包,合稱為類庫。可以引用這些包,也可以創(chuàng)建自己的包。
Java的類庫是系統(tǒng)提供的已實(shí)現(xiàn)的標(biāo)準(zhǔn)類的集合,是Java編程的API,它可以幫助開發(fā)者方便、快捷地開發(fā)Java程序
接口主要作用是可以幫助實(shí)現(xiàn)類似于類的多重繼承的功能。在Java中,出于簡化程序結(jié)構(gòu)的考慮,不再支持類間的多重繼承而只支持單重繼承,即一個(gè)類至多只能有一個(gè)直接父類。然而在解決實(shí)際問題的過程中,僅僅依靠單重繼承在很多情況下都不能將問題的復(fù)雜性表述完整,需要其他的機(jī)制作為輔助。
接口聲明
Java中聲明接口的語法如下:
[public] interface 接口名 [extends 父接口名列表] { //接口體; //常量域聲明 [public] [static] [final] 域類型 域名=常量值; //抽象方法聲明 [public] [abstract] 返回值 方法名(參數(shù)列表) [throw異常列表]; }
從上面的語法規(guī)定可以看出,定義接口與定義類非常相似,實(shí)際上完全可以把接口理解成為一種特殊的類,接口是由常量和抽象方法組成的特殊類
(1)接口中的屬性都是用final修飾的常量,
(2)接口中的方法都是用abstract修飾的抽象方法,在接口中只能給出這些抽象方法的方法名、返回值和參數(shù)列表,而不能定義方法體,即僅僅規(guī)定了一組信息交換、傳輸和處理的“接口”
接口的實(shí)現(xiàn)
一個(gè)類要實(shí)現(xiàn)某個(gè)或某幾個(gè)接口時(shí),有如下的步驟和注意事項(xiàng):
(1)在類的聲明部分,用implements關(guān)鍵字聲明該類將要實(shí)現(xiàn)哪些接口;
如下:
class類名implements接口{ }
(2)如果實(shí)現(xiàn)某接口的類不是abstract的抽象類,則在類的定義部分必須實(shí)現(xiàn)指定接口的所有抽象方法,即為所有抽象方法定義方法體,而且方法頭部分應(yīng)該與接口中的定義完全一致,即有完全相同的返回值和參數(shù)列表;
(3)如果實(shí)現(xiàn)某接口的類是abstract的抽象類,則它可以不實(shí)現(xiàn)該接口所有的方法。
(4)一個(gè)類在實(shí)現(xiàn)某接口的抽象方法時(shí),必須使用完全相同的方法頭。
(5)接口的抽象方法,其訪問限制符都已指定是public,所以類在實(shí)現(xiàn)方法時(shí),必須顯式地使用public修飾符。
小結(jié):
多重繼承是指一個(gè)子類繼承多個(gè)父類。Java不支持多重繼承,但Java提供了接口。
子類不能訪問父類的private成員,但子類可以訪問其父類的public,protected和包訪問成員;要訪問父類的包訪問成員,子類一定要在父類的包內(nèi)。
子類構(gòu)造函數(shù)總是先調(diào)用(顯式的或隱式地)其父類的構(gòu)造函數(shù),以創(chuàng)建和初始化子類的父類成員。
子類的對象可以當(dāng)作其父類的對象對待,反之則不行(即向上轉(zhuǎn)型)
protected訪問是public和private訪問之間一個(gè)保護(hù)性的中間層次。父類方法、子類方法和在同一個(gè)包內(nèi)類的方法都能訪問父類的protected成員,但其他方法均不能訪問
一個(gè)子類對象引用可以隱式地轉(zhuǎn)換成一個(gè)父類對象引用。使用顯式的類型轉(zhuǎn)換,可以把父類引用轉(zhuǎn)換成子類引用。如果目標(biāo)不是子類對象,將產(chǎn)生ClassCastException例外處理。
相關(guān)文章
SSH框架網(wǎng)上商城項(xiàng)目第23戰(zhàn)之在線支付功能實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第23戰(zhàn)之在線支付功能實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-06-06Spring Boot高效數(shù)據(jù)聚合之道深入講解
這篇文章主要給大家介紹了關(guān)于Spring Boot高效數(shù)據(jù)聚合之道的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06SpringBoot配置mybatis駝峰命名規(guī)則自動轉(zhuǎn)換的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot配置mybatis駝峰命名規(guī)則自動轉(zhuǎn)換的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09java8學(xué)習(xí)教程之lambda表達(dá)式的使用方法
Java8最值得學(xué)習(xí)的特性就是Lambda表達(dá)式,下面這篇文章主要給大家介紹了關(guān)于java8學(xué)習(xí)教程之lambda表達(dá)式使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09基于selenium-java封裝chrome、firefox、phantomjs實(shí)現(xiàn)爬蟲
這篇文章主要介紹了基于selenium-java封裝chrome、firefox、phantomjs實(shí)現(xiàn)爬蟲,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2020-10-10MyBatis-Plus通用CRUD操作的實(shí)現(xiàn)
MyBatis-Plus是基于MyBatis的增強(qiáng)工具,主要目的是簡化MyBatis的使用并提升開發(fā)效率,它提供了通可以用CRUD操作、分頁插件、多種插件支持、自動代碼生成器等功能,感興趣的可以了解一下2024-10-10