面向?qū)ο蠊P記
一、 對(duì)象在內(nèi)存中的存放方法以及被調(diào)用過程
-
class文件
首先被加載到方法區(qū)
中的class文件內(nèi)容
區(qū)域,類的成員變量
和成員方法
自然也隨著類的加載而被加載到方法區(qū)。 - 創(chuàng)建對(duì)象的時(shí)候,首先在棧中開辟空間,用于存放對(duì)對(duì)象的引用地址(即
Phone p
的地址); - 在堆中開辟空間存放對(duì)象實(shí)體(即
new Phone()
),同時(shí)會(huì)給該對(duì)象的屬性分配內(nèi)存空間來存放該對(duì)象的屬性值,即會(huì)把方法區(qū)中該類的成員變量復(fù)制一份到堆內(nèi)存中(首先是默認(rèn)值,然后才是調(diào)用者賦給的值),用于存放該對(duì)象的屬性值,但該類的方法就不會(huì)被復(fù)制到堆中,而是通過堆中一個(gè)指針的引用指向方法區(qū)中該方法。(其實(shí)這一點(diǎn)很好理解,每個(gè)對(duì)象的屬性值不一樣,但是每個(gè)對(duì)象的行為卻是一樣的,因此每個(gè)對(duì)象需要有自己的成員變量來保存自己的屬性值信息,而方法不用,只用調(diào)用同一個(gè)方法即可。) - 在調(diào)用對(duì)象的成員變量時(shí),實(shí)際上是給該成員變量進(jìn)行復(fù)制,因此是在堆中執(zhí)行的。
- 在調(diào)用對(duì)象的成員方法時(shí),該方法會(huì)被加載到棧中(實(shí)際上在棧中有個(gè)方法棧專門用來保存對(duì)方法的引用),該方法被調(diào)用完成后,立刻就被銷毀了,因此對(duì)成員方法的調(diào)用發(fā)生在棧內(nèi)存中。
注意:棧的特點(diǎn)是先進(jìn)后出,畫圖的時(shí)候因從下往上,這里只是為了方便解釋,畫圖的時(shí)候從上往下。
-
一個(gè)對(duì)象的引用圖解
一個(gè)對(duì)象的內(nèi)存圖解
-
兩個(gè)對(duì)象的內(nèi)存圖解
兩個(gè)對(duì)象的內(nèi)存圖解
注意到:
- 兩個(gè)對(duì)象分別有自己的成員變量空間;
- 兩個(gè)對(duì)象通過地址指向方法區(qū)的同一個(gè)地址,即成員方法的地址;
-
三個(gè)對(duì)象的內(nèi)存圖解
三個(gè)對(duì)象的內(nèi)存圖解
注意到:兩個(gè)引用指向同一個(gè)對(duì)象時(shí),實(shí)際上是對(duì)同一個(gè)對(duì)像進(jìn)行操作
二、成員變量、局部變量、靜態(tài)變量的區(qū)別
(一) 成員變量和局部變量的區(qū)別
- 在類中的位置不同
- 成員變量:在類中方法外
- 局部變量:在方法定義中或方法聲明上
- 在內(nèi)存中的位置不同
- 成員變量:在堆內(nèi)存中(對(duì)象的實(shí)體在堆內(nèi)存中,成員變量是跟隨著對(duì)象的實(shí)體,因此也就在堆內(nèi)存中)
- 局部變量:在棧內(nèi)存中(局部變量在方法內(nèi)部,方法在被調(diào)用的時(shí)候會(huì)被加載到棧中,因此局部變量存在于棧內(nèi)存中)
- 生命周期不同(在內(nèi)存中的位置不同,生命周期自然不同)
- 成員變量:隨著對(duì)象的創(chuàng)建而存在,隨著對(duì)象的消失而消失;
- 局部變量:隨著方法的調(diào)用而存在,隨著方法的調(diào)用完畢而消失;
- 初始化值不同(在內(nèi)存中的位置不同,初始化值自然不同)
- 成員變量:有默認(rèn)初始值;
- 局部變量:沒有默認(rèn)初始化值,必須定義、賦值,然后才能使用;
(二)成員變量和靜態(tài)變量的區(qū)別
- 所屬不同
- 成員變量:屬于對(duì)象,也叫
實(shí)例變量(對(duì)象變量)
; - 靜態(tài)變量:屬于類,也叫
類變量
;
- 成員變量:屬于對(duì)象,也叫
- 內(nèi)存中位置不同
- 成員變量:隨著對(duì)象的創(chuàng)建而存在于
堆內(nèi)存
中; - 靜態(tài)變量:隨著類的加載而存在于方法區(qū)的
靜態(tài)區(qū)
;
- 成員變量:隨著對(duì)象的創(chuàng)建而存在于
- 生命周期不同
- 成員變量:隨著
對(duì)象
的創(chuàng)建而存在,隨著對(duì)象的消失而消失; - 靜態(tài)變量:隨著
類
的加載而加載,隨著類的消失而消失;
- 成員變量:隨著
- 調(diào)用不同
- 成員變量:只能通過對(duì)象名來調(diào)用;
- 靜態(tài)變量:即可以通過類名來調(diào)用,也可以通過對(duì)象名來調(diào)用;
(三)靜態(tài)變量和局部變量的區(qū)別
- 這個(gè)區(qū)別太明顯,不做區(qū)分;
注意事項(xiàng):因?yàn)樵趦?nèi)存中的位置不同,所以要注意形式參數(shù)問題
- 基本類型:形式參數(shù)的改變不影響實(shí)際參數(shù)
- 引用類型:形式參數(shù)的改變直接影響實(shí)際參數(shù)
三、構(gòu)造方法
構(gòu)造方法的注意事項(xiàng)
- 默認(rèn)構(gòu)造方法問題:
- 如果在類中沒有定義構(gòu)造方法,系統(tǒng)將自動(dòng)提供一個(gè)無參構(gòu)造方法;
- 如果在類中定義了構(gòu)造方法,系統(tǒng)將不再提供默認(rèn)的無參構(gòu)造方法;
- 構(gòu)造方法的重載問題:
- 只要我們給出了構(gòu)造方法,不管是無參還是代參構(gòu)造方法,系統(tǒng)都不在提供構(gòu)造方法,如果我們想使用無參構(gòu)造方法,就必須自己定義聲明;
- 建議永遠(yuǎn)自己給出無參構(gòu)造方法;
四、Student s=new Student()
做了哪些事情
- 把
Student.class
文件加載到內(nèi)存中方法區(qū)的class文件區(qū)域
;- 在
棧內(nèi)存
中給s
變量開辟一個(gè)內(nèi)存空間;- 在
堆內(nèi)存
中給new Student()
對(duì)象申請(qǐng)一個(gè)空間;- 給
成員變量
進(jìn)行默認(rèn)初始化
;- 給
成員變量
進(jìn)行顯示初始化
;- 給
成員變量
進(jìn)行構(gòu)造方法初始化
(即通過構(gòu)造方法給成員變量進(jìn)行初始化);- 對(duì)象初始化完畢,把
堆內(nèi)存
的地址值
賦值給s
變量;
- 總結(jié):
-
構(gòu)造方法
對(duì)類進(jìn)行初始化實(shí)際上是對(duì)類中的數(shù)據(jù)
(即成員變量
)進(jìn)行初始化; - 對(duì)成員變量進(jìn)行初始化的時(shí)候,首先
默認(rèn)初始化
,然后是顯示初始化
,最后才是構(gòu)造方法初始化
;-
默認(rèn)初始化
實(shí)際上就是給數(shù)據(jù)賦默認(rèn)值(即0,false,null,'\u0000',0.0f,0.0d,...)的過程, -
顯示初始化
實(shí)際上就是把手動(dòng)賦值的值賦值給成員變量,比如private int num=1
;這個(gè)1就是顯示初始化, -
構(gòu)造方法初始化
就是通過構(gòu)造方法傳遞過來的值對(duì)成員變量進(jìn)行初始化;
- 因此才是這個(gè)初始化順序,即默認(rèn)初始化
--->顯示初始化
--->構(gòu)造方法初始化
;
-
-
五、 成員變量的定義規(guī)則##
- 變量什么時(shí)候定義成成員變量?
- 如果這個(gè)變量是
用來修飾這個(gè)類的信息的
,那么該變量就應(yīng)該定義為成員變量;
- 如果這個(gè)變量是
- 變量定義在哪里好呢?
- 變量的范圍越小越好,因?yàn)槟芗皶r(shí)被回收;
六、static 關(guān)鍵字
- 特點(diǎn):
- 隨著類的加載而加載;
- 因此被
static
修飾的成員變量/成員方法
也叫類成員
;
- 因此被
- 優(yōu)先于對(duì)象而存在;(由第一點(diǎn)就可以推出第二點(diǎn))
- 自然也就優(yōu)先于對(duì)象的成員變量和成員方法而存在;
- 被
static
修飾的成員變量
和成員方法
被類的所有對(duì)象共享;- 即一個(gè)對(duì)象修改了某個(gè)成員變量的值,其他對(duì)象該成員變量的值也被修改了;
- 這是我們判斷是否使用
static
關(guān)鍵字的條件;- 即如果某個(gè)成員變量或成員方法是被所有對(duì)象共享的,那么這個(gè)成員變量或成員方法就應(yīng)該被定義為靜態(tài)的;
- 因?yàn)殡S著類的加載而加載,所以可以直接通過類名來調(diào)用,不用通過對(duì)象來調(diào)用(當(dāng)然也可以通過對(duì)象名來調(diào)用);
- 隨著類的加載而加載;
- 注意事項(xiàng):
- 在靜態(tài)方法中沒有
this
關(guān)鍵字; -
靜態(tài)方法
只能訪問靜態(tài)的成員變量
和靜態(tài)的成員方法
;- 即靜態(tài)只能訪問靜態(tài),非靜態(tài)可以訪問靜態(tài)和非靜態(tài);
- 在靜態(tài)方法中沒有
-
static
內(nèi)存圖解
static內(nèi)存圖解
注意到:
- 被
static
修飾的方法(如main方法)不用創(chuàng)建對(duì)象直接通過靜態(tài)方法區(qū)
加載到棧內(nèi)存中; - 當(dāng)成員變量被
static
修飾的時(shí)候,該成員變量
就存在于靜態(tài)方法區(qū)
的靜態(tài)區(qū)
,而不是堆中,對(duì)象通過地址來指向該變量,這里就是靜態(tài)標(biāo)記
,而類的成員方法不被static
修飾時(shí),存放在靜態(tài)方法區(qū)的該對(duì)象專有的方法區(qū),通過地址值指向這些方法,這就是方法標(biāo)記
; -
靜態(tài)方法區(qū)
中的靜態(tài)區(qū)
專門用來存放被static
修飾的變量和方法,同時(shí)在方法區(qū)中專門有一個(gè)區(qū)域用來存放類的構(gòu)造方法
和非靜態(tài)成員方法
;
七、JVM內(nèi)存簡單區(qū)分
-
棧
- 特點(diǎn):
- 每個(gè)線程都包含一個(gè)棧區(qū),每個(gè)棧中的數(shù)據(jù)(基本類型,引用類型)都是私有的,其他棧不能訪問,所以線程中的數(shù)據(jù)也是私有的;
- 棧中只存放基本數(shù)據(jù)類型以及引用數(shù)據(jù)類型的引用;
- 特點(diǎn):
-
堆
- 特點(diǎn):
- JVM只有一個(gè)堆區(qū)(heap),且被所有線程共享;
- 堆內(nèi)存中存放的全是對(duì)象,且每個(gè)對(duì)象都包含一個(gè)與之對(duì)應(yīng)的class的信息(class的目的是為了得到操作指令);
- 特點(diǎn):
-
靜態(tài)方法區(qū)
- 特點(diǎn):
- 被所有的線程共享;
- 靜態(tài)方法區(qū)中包含的都是在整個(gè)程序中
永遠(yuǎn)唯一
的元素,如所有的class文件,static修飾的變量;
- 靜態(tài)方法區(qū)細(xì)分:
- class文件區(qū)域
- 特點(diǎn):
- java編譯生成的所有class文件首先被加到該區(qū)域,然后該class文件中的成員信息再被加載到棧或者堆中;
- 靜態(tài)區(qū)
- 特點(diǎn):
1. 所有被`static`修飾的`變量`和`方法`存放區(qū)域,通過地址值進(jìn)行引用;
- 方法區(qū)
- 特點(diǎn):
- 對(duì)象的
構(gòu)造方法
和非靜態(tài)方法
存放區(qū)域,通過地址值進(jìn)行引用;
- 對(duì)象的
- 特點(diǎn):
- 常量池
- 特點(diǎn):
- 存放一些常量;
- 特點(diǎn):
- class文件區(qū)域
- 特點(diǎn):
八、代碼塊
- 定義:在java中,用
{}
括起來的代碼被稱為代碼塊,根據(jù)位置和聲明的不同,可以被分為以下幾類:- 局部代碼塊
- 在方法中出現(xiàn),用于限定變量生命周期,及早釋放,提高內(nèi)存利用率;
- 構(gòu)造代碼塊
- 定義:在構(gòu)造方法的位置(即類中成員位置),用{}括起來的代碼。每次調(diào)用構(gòu)造方法前,都會(huì)先執(zhí)行構(gòu)造代碼塊,即先執(zhí)行構(gòu)造代碼塊,再執(zhí)行構(gòu)造方法;
- 作用:
- 可以把多個(gè)構(gòu)造方法中相同的代碼放到一起,提高代碼復(fù)用性;
- 對(duì)對(duì)象進(jìn)行初始化;
- 說明:每調(diào)用一次構(gòu)造方法,構(gòu)造代碼塊就會(huì)被執(zhí)行一次,因此構(gòu)造代碼塊會(huì)被執(zhí)行多次;
- 靜態(tài)代碼塊
- 定義:在類中成員位置,用{}括起來的代碼,并且加了static修飾;
- 作用:對(duì)類進(jìn)行初始化;
- 說明:靜態(tài)代碼塊只執(zhí)行一次;
- 同步代碼塊
- 定義: 在方法中出現(xiàn),用{}括起來的代碼,被
synchroized
修飾的代碼塊; - 作用:在多線程中保證
{}
中的代碼是原子性操作,即保證線程安全;
- 定義: 在方法中出現(xiàn),用{}括起來的代碼,被
- 局部代碼塊
- 注意:
- 靜態(tài)代碼塊,構(gòu)造代碼塊,構(gòu)造方法的執(zhí)行順序
- 靜態(tài)代碼塊 > 構(gòu)造代碼塊 > 構(gòu)造方法
- 靜態(tài)代碼塊,構(gòu)造代碼塊,構(gòu)造方法的執(zhí)行次數(shù)
- 靜態(tài)代碼塊:只執(zhí)行一次;
- 構(gòu)造代碼塊:執(zhí)行多次,構(gòu)造方法被調(diào)用幾次,構(gòu)造代碼塊就執(zhí)行幾次;
- 構(gòu)造方法:執(zhí)行多次;
- 靜態(tài)代碼塊,構(gòu)造代碼塊,構(gòu)造方法的執(zhí)行順序
九、繼承
1. 繼承注意事項(xiàng)
- 父類的私有成員(成員變量,成員方法)不能被繼承;
- 子類不能繼承父類的構(gòu)造方法,但是可以通過
super
關(guān)鍵字訪問父類的構(gòu)造方法; - 不要為了部分功能而去繼承;
- 繼承使用條件:是兩個(gè)對(duì)象是集合中的屬于關(guān)系時(shí)(兩個(gè)類之間如果可以用
is a
來表述),就可以使用繼承;
2.繼承中類中成員之間的關(guān)系
1)繼承中成員變量
之間的關(guān)系
- 當(dāng)子類和父類中的成員變量名稱一樣時(shí),遵循就近原則,即
- 首先到子類的局部變量中找,有就使用,沒有執(zhí)行下一步;
- 在子類的成員變量中找,有就使用,沒有執(zhí)行下一步;
- 在父類的的成員變量中找,有就使用,沒有就報(bào)錯(cuò);
2)繼承中構(gòu)造方法
之間的關(guān)系
- 子類中
所有的構(gòu)造方法
默認(rèn)都會(huì)訪問父類的空參構(gòu)造方法
;- 因?yàn)樽宇悤?huì)繼承父類中的數(shù)據(jù),可能還會(huì)使用父類的數(shù)據(jù)。所以子類初始化之前,一定要先完成父類數(shù)據(jù)的初始化;
- 子類每個(gè)構(gòu)造方法的第一條語句默認(rèn)是
super();
,不寫也默認(rèn)有;
- 如果父類沒有無參構(gòu)造方法,那么子類的構(gòu)造方法就會(huì)報(bào)錯(cuò),解決方法:
- 方法一:在父類中加一個(gè)無參構(gòu)造方法;
- 方法二:通過
super(...)
去顯式調(diào)用父類的帶參構(gòu)造方法; - 方法三:子類通過
this
去調(diào)用本類的其他構(gòu)造方法,從而間接調(diào)用父類的構(gòu)造方法;
- 總結(jié):
- 子類中一定要有一個(gè)構(gòu)造方法去訪問父類的構(gòu)造方法,否則父類數(shù)據(jù)就無法初始化;
- 實(shí)際上這里是
分層初始化
,即先初始化爺爺類,在初始化父親類,最后才是初始化子類,而super()
僅僅表示先初始化父類;
- 注意事項(xiàng):
this(...)或者super(...)必須出現(xiàn)在第一條語句上;
如果不是放在第一條語句上,就可能對(duì)父類的數(shù)據(jù)進(jìn)行了多次初始化,所以必須放在第一條語句上;
4)繼承中成員方法
的關(guān)系
-
方法重寫
(和方法重載分開):- 子類中出現(xiàn)了和父類中一模一樣的方法聲明(即返回值類型,方法名,參數(shù)列表均相同);
- 使用特點(diǎn):
- 在訪問重寫方法時(shí),如果子類有就先訪問子類的,子類中沒有,就到父類中找,如果沒有找到,就報(bào)錯(cuò);
簡單記:就近原則,先找自己的,有就用;如果沒有再找父類的;
- 什么時(shí)候使用方法重寫:
- 當(dāng)子類需要父類的功能,而功能主體子類有自己特有內(nèi)容時(shí),可以重寫父類中的方法,這樣既沿襲了父類的功能(通過
super.成員方法()
來調(diào)用父類的功能),又定義了子類特有的功能;
- 當(dāng)子類需要父類的功能,而功能主體子類有自己特有內(nèi)容時(shí),可以重寫父類中的方法,這樣既沿襲了父類的功能(通過
- 方法重寫注意事項(xiàng):
- 父類的私有方法不能被重寫;
因?yàn)楦割愃接蟹椒ň筒荒鼙焕^承,自然就不能被重寫;
2. 子類重寫父類的方法時(shí),訪問權(quán)限不能更低,即子類方法的訪問權(quán)限必須大于等于父類方法的訪問權(quán)限;
>子類重寫父類方法的時(shí)候,最好聲明一模一樣。
3. 父類是靜態(tài)方法
,子類也必須通過靜態(tài)方法進(jìn)行重寫;
>父類是靜態(tài),子類重寫時(shí)也必須是靜態(tài),否則會(huì)報(bào)錯(cuò);
>
>父類是非靜態(tài),子類重寫時(shí)也必須是非靜態(tài),否則會(huì)報(bào)錯(cuò);
- 父類的私有方法不能被重寫;
5)this
和super
的區(qū)別與聯(lián)系
-
表示的意義
- this: 代表本類對(duì)象的引用;
- super:代碼父類對(duì)象的引用(實(shí)際上是父類存儲(chǔ)空間的標(biāo)識(shí));
-
用法
- 訪問成員變量
//表示訪問本類的成員變量
this.成員變量
//表示訪問本類的成員變量
super.成員變量
- 訪問構(gòu)造方法
>//表示訪問本類的構(gòu)造方法
>
>this(...)
>
>//表示訪問父類的構(gòu)造方法,雖然不能繼承父類的構(gòu)造方法,但是可以通過super來訪問
>
>//實(shí)際開發(fā)中,經(jīng)常使用這種方法給父類的參數(shù)賦值,而且即使父類的參數(shù)是private修飾的,也不影響通過子類進(jìn)行賦值;
>
>super(...)- 訪問成員方法
//表示訪問本類的成員方法
this.成員方法()
//表示訪問父類的成員方法
super.成員方法()
成員變量屬于每個(gè)對(duì)象 (而不是類)
私有的值,它表征了該對(duì)象的某種狀態(tài),因此父類的成員變量子類可以訪問,卻不能修改,而成員方法表征了某一系列對(duì)象所具有的行為,因此子類可以覆蓋父類的方法,實(shí)現(xiàn)自己的特有的功能,但是JVM是如何實(shí)現(xiàn)方法重寫時(shí),調(diào)用的是子類的方法的了?
十、final
關(guān)鍵字
-
final
使用場景- 如果父類的某個(gè)
成員變量
只能被子類訪問,但是不能被修改值; - 如果父類的某個(gè)
成員方法
只能被子類調(diào)用,但是不能被子類重寫, - 如果某個(gè)
類
就是最終類,不能有子類;
- 如果父類的某個(gè)
-
final
特點(diǎn):final可以修飾類
,成員變量
,成員方法
;-
final
修飾類
,該類不能被繼承; -
final
修飾成員變量
,這個(gè)成員變量只能被使用,不能被子類重新賦值;當(dāng)一個(gè)變量被
final
修飾時(shí),不能修改實(shí)際上就是常量,這是自定義常量,
字面值常量就是字符串,true,10這種形式的;
3.final
修飾成員方法
,這個(gè)成員方法只能被調(diào)用,不能被子類重寫;
-
-
final
修飾局部變量- 首先
final
可以修飾局部變量;區(qū)別于
private
等權(quán)限修飾符,這四個(gè)權(quán)限修飾符不能修飾局部變量,因?yàn)榫植孔兞勘緛砭椭荒茉诜椒▋?nèi)部可以使用,方法外部不能使用,即本來就被封裝起來了,權(quán)限修飾符再來修飾沒有任何意義。
2.final
修飾基本數(shù)據(jù)類型時(shí),基本類型的值不能發(fā)生改變;
3.final
修飾引用數(shù)據(jù)類型時(shí),引用類型的地址值不能發(fā)生改變,但是該對(duì)象的堆內(nèi)存的值是可以改變的,即該對(duì)象的成員變量的值是可以改變的,成員方法是可以被重寫的;
- 首先
-
final
修飾變量的初始化時(shí)機(jī)- 被
final
修飾的變量只能賦值一次; - 對(duì)于非靜態(tài)常量而言,賦值在構(gòu)造方法完畢之前完成;
即case 1:如果被final修飾的變量已經(jīng)賦值(例如
private int num=10;
),就不能在構(gòu)造方法中給該常量賦值,否則會(huì)報(bào)錯(cuò),case 2:如果被final修飾的變量未賦值(例如
private int num;
),可以在構(gòu)造代碼塊中賦值,但不能再在構(gòu)造方法中賦值,否則會(huì)報(bào)錯(cuò);case 3:如果被final修飾的變量未賦值(例如
private int num;
),同時(shí)也沒有在構(gòu)造代碼塊中賦值,就可以在構(gòu)造方法中賦值,此時(shí)不會(huì)報(bào)錯(cuò); - 被
十一、多態(tài)
1. 多態(tài):同一個(gè)事物(對(duì)象),在不同時(shí)刻體現(xiàn)出來的不同狀態(tài)
2. 多態(tài)的前提:
- 要有繼承關(guān)系;
- 要有方法重寫;
- 要有父類引用指向子類對(duì)象;
Fu f=new Zi();
3. 多態(tài)中的成員訪問特點(diǎn)
- 成員變量
>編譯能否通過看左邊,運(yùn)行看左邊;
>
>成員變量不存在重寫概念,所以只能訪問的父類的成員變量;
- 構(gòu)造方法
>在創(chuàng)建子類對(duì)象(調(diào)用子類構(gòu)造方法)的時(shí)候,會(huì)默認(rèn)訪問父類的構(gòu)造方法(會(huì)先對(duì)父類進(jìn)行初始化),對(duì)父類的數(shù)據(jù)進(jìn)行初始化;
- 成員方法
>編譯能否通過看左邊,運(yùn)行看右邊;
>
>由于父類的成員方法可以子類重寫,所以實(shí)際上調(diào)用的是子類的方法;
- 靜態(tài)方法
>編譯能否通過看左邊,運(yùn)行看左邊
>
>靜態(tài)和類相關(guān),算不上重寫,所以訪問還是父類的;
總結(jié):由于成員方法
存在重寫
,所以他運(yùn)行看子類的,成員變量
、靜態(tài)方法
均不存在重寫概念,所以運(yùn)行看父類的
4. 多態(tài)的優(yōu)點(diǎn)與缺點(diǎn)
1)優(yōu)點(diǎn)
提高了代碼的維護(hù)性(通過繼承實(shí)現(xiàn));
-
提高了代碼的擴(kuò)展性(通過多態(tài)保證);
由于子類可以重寫父類的方法,所在在父類中調(diào)用父類的方法,在子類中重寫這些方法,這樣就保證了擴(kuò)展性
//父類 class Animal { public void eat(){ System.out.println("eat"); } public void sleep(){ System.out.println("sleep"); } } class Dog extends Animal { public void eat(){ System.out.println("狗吃肉"); } public void sleep(){ System.out.println("狗站著睡覺"); } } class Cat extends Animal { public void eat() { System.out.println("貓吃魚"); } public void sleep() { System.out.println("貓趴著睡覺"); } } class AnimalTool { private AnimalTool(){} //這里就體現(xiàn)出了由于多態(tài)而保證擴(kuò)展性,如果這里傳入Cat,Dog,就沒有擴(kuò)展性了, //每新添加一個(gè)Animal的子類,就需要把這個(gè)方法復(fù)制一份,擴(kuò)展性就差了 //擴(kuò)展性就體現(xiàn)在這里傳入的是Animal,而不是Animal的子類 public static void useAnimal(Animal a) { a.eat(); a.sleep(); } }
2)缺點(diǎn)
- 不能使用子類中的特有功能;
- 解決:將父類的引用強(qiáng)制轉(zhuǎn)換成對(duì)子類的引用(向下轉(zhuǎn)型);
對(duì)象間的轉(zhuǎn)型問題
向上轉(zhuǎn)型
Fu f=new Zi();
向下轉(zhuǎn)型
Zi zi=(Zi)f;
- 解決:將父類的引用強(qiáng)制轉(zhuǎn)換成對(duì)子類的引用(向下轉(zhuǎn)型);
3)多態(tài)繼承中內(nèi)存圖解
- 這里應(yīng)該可以理解
super
表示父類存儲(chǔ)空間的標(biāo)識(shí)這句話了吧; - JVM是如何實(shí)現(xiàn)方法重寫時(shí),調(diào)用的是子類的方法的了?當(dāng)沒有重寫時(shí),調(diào)用的是父類的方法了?這里不明白;
4)多態(tài)中的對(duì)象變化內(nèi)存圖解
注意:
-
ClassCastException
是在運(yùn)行時(shí)才拋出的,因?yàn)樵诰幾g的時(shí)候,地址的指向沒有出錯(cuò),類是在運(yùn)行時(shí)才加載到內(nèi)存中去的,因此才會(huì)檢測類的相關(guān)信息;
5)多態(tài)的種類
- 具體類多態(tài)(基本沒用)
即左邊是一個(gè)具體類,右邊是具體類的子類
- 抽象類多態(tài)(常用)
即左邊是一個(gè)抽象類,右邊是抽象類的具體實(shí)現(xiàn)類
- 接口類多態(tài) (用的最多)
即左邊是一個(gè)接口,右邊是接口的具體實(shí)現(xiàn)類
十二、抽象類
(一)為什么會(huì)有抽象類
由于現(xiàn)實(shí)世界的復(fù)雜性,有些事物雖然是對(duì)象,但是是對(duì)一類事物的總結(jié)(如水果,動(dòng)物),本身是一個(gè)抽象的對(duì)象,沒有具體的屬性值和具體的功能實(shí)現(xiàn),如果這個(gè)時(shí)候我們給這種對(duì)象具體屬性值和具體的功能,就違反了對(duì)現(xiàn)實(shí)事物的描述,所以是不對(duì)的,因此我只能對(duì)這種事物進(jìn)行聲明,表明他有某個(gè)功能,只要是這個(gè)事物的子類,就一定有這個(gè)功能,就要實(shí)現(xiàn)這個(gè)功能,這就是抽象類的來源;
(二)抽象類的特點(diǎn)
- 抽象類和抽象方法必須用
abstract
來修飾; - 抽象類中不一定有抽象方法,但是有抽象方法的類一定是抽象類;
- 抽象類不能實(shí)例化,即不能用抽象類來創(chuàng)建對(duì)象;
- 因?yàn)槌橄箢惒皇蔷唧w的,不能造出具體的對(duì)象,否則就違反了現(xiàn)實(shí)上面的說明;
- 但是抽象類是有構(gòu)造方法的,只是不能實(shí)例化對(duì)象而已;
- 抽象類的構(gòu)造方法專門用來給子類訪問,對(duì)父類進(jìn)行數(shù)據(jù)初始化;
- 抽象類的子類
- 如果不想重寫抽象方法,該子類可以是個(gè)抽象類;
- 如果子類是一個(gè)具體的類,就必須重寫所有的抽象方法;
- 抽象類的實(shí)例化是通過具體的子類來實(shí)現(xiàn)的,是多態(tài)的方式,因此多態(tài)的所有特點(diǎn),使用規(guī)則均適合抽象的實(shí)例化;
(三)抽象類的成員特點(diǎn)
- 成員變量
- 既可以有變量,也可以有常量;(要和接口做區(qū)別)
- 構(gòu)造方法
- 有構(gòu)造方法,用于子類訪問父類數(shù)據(jù)的初始化;
- 成員方法
- 抽象方法:強(qiáng)制要求子類做的事情
- 非抽象方法:子類繼承的事情,提高代碼復(fù)用性;
(四)抽象類的注意事項(xiàng)
- 一個(gè)抽象類中可以沒有抽象方法,這樣做是為了不然抽象類創(chuàng)建對(duì)象(抽象類本來就不能創(chuàng)建對(duì)象),必須由其具體實(shí)現(xiàn)類創(chuàng)建對(duì)象
-
abstract
關(guān)鍵字不能和那些關(guān)鍵字一起出現(xiàn)-
abstract
不能和private
一起出現(xiàn)沖突,
abstract
修改的方法必須由具體子類實(shí)現(xiàn),而private
修飾的方法,子類無法訪問;
2.abstract
和final
不能一起出現(xiàn)
>沖突,abstract
修飾的方法必須由具體子類實(shí)現(xiàn),而final
修飾的方法不能被子類重寫;
3.abstract
和static
不能一起出現(xiàn)
>無意義,abstract
修飾的方法沒有方法體,而static
修飾的方法可以直接用類名調(diào)方法,這樣相當(dāng)于調(diào)的空方法;
4. 抽象類中的非抽象方法可以用static
修飾,這樣用類名調(diào)方法就不會(huì)出現(xiàn)上述問題;
-
十三、接口
(一)接口的作用
提高事物功能的擴(kuò)展性,彌補(bǔ)類的不足;
比如被訓(xùn)練的猴子可以表演雜技,但是表演雜技并不是動(dòng)物和猴子固有的功能,因此不能寫到動(dòng)物類,猴子類,只能是被訓(xùn)練過的猴子擁有表演雜技的功能,這種表演雜技的功能就是額外的功能,既然不能寫在類中,就通過接口實(shí)現(xiàn)這種功能;
(二)接口的特點(diǎn)
- 接口不能被實(shí)例化
接口中的方法都是抽象的,不給出具體實(shí)現(xiàn);(JDK 8中接口中的方法可以有方法實(shí)現(xiàn))
接口的實(shí)例化通過具體子類來實(shí)現(xiàn),即通過多態(tài)的方式來實(shí)例化;
- 接口的子類
- 接口的子類可以是抽象類,但是沒有意義
- 接口的子類可以是具體類,但是必須實(shí)現(xiàn)所有的抽象方法;
(三)接口的成員特點(diǎn)
- 成員變量
- 接口中的變量默認(rèn)
public static final
修飾,即接口中的成員變量只能是常量;
- 接口中的變量默認(rèn)
- 構(gòu)造方法
- 接口沒有構(gòu)造方法,因?yàn)榻涌谥饕菙U(kuò)展功能的,而沒有具體存在,必須依附于某個(gè)主體(實(shí)際上就是對(duì)象);
所有的類都默認(rèn)繼承
Object
類,而Object
類唯一構(gòu)造方法就是Object()
,因此如果在接口的實(shí)現(xiàn)類的構(gòu)造方法中如果出現(xiàn)super()
時(shí),并不是訪問的接口的接口的構(gòu)造方法(接口本來就沒有構(gòu)造方法),而是訪問的是Object
類的構(gòu)造方法; - 成員方法
- 只能是抽象方法,而且默認(rèn)
public abstract
修飾;
- 只能是抽象方法,而且默認(rèn)
注意:類與類之間只能是單繼承關(guān)系,但是接口與接口之間可以是多繼承的關(guān)系,即一個(gè)接口可以繼承自多個(gè)接口;
(四)接口與抽象類的區(qū)別
- 成員不同
- 抽象類
- 成員變量:可以是變量,也可以是常量;
- 構(gòu)造方法:有
- 成員方法:可以有抽象方法,也可以有非抽象方法;
- 接口
- 成員變量:只能是常量;
- 構(gòu)造方法:無
- 成員方法:只能是抽象方法;
- 抽象類
- 關(guān)系不同
- 類與類:繼承,單繼承;
- 類與接口:實(shí)現(xiàn),可以多實(shí)現(xiàn);
- 接口與接口:單繼承,多繼承均可;
- 設(shè)計(jì)理念不同
- 抽象類:被繼承體現(xiàn)的是
is a
的關(guān)系,抽象類中定義的是該繼承體系中的共性功能
; - 接口:被實(shí)現(xiàn)體現(xiàn)的是
like a
(像什么)的關(guān)系,接口中定義的是該繼承體系的擴(kuò)展功能
(個(gè)性功能);
- 抽象類:被繼承體現(xiàn)的是
十四、內(nèi)部類
(一)內(nèi)部類的訪問特點(diǎn)
- 內(nèi)部類可以直接訪問外部類的成員,包括
private
修飾的成員; - 外部類要訪問內(nèi)部類的成員,必須創(chuàng)建內(nèi)部類對(duì)象;
(二)成員內(nèi)部類特點(diǎn)
- 如何直接訪問內(nèi)部類的成員變量,成員方法;
外部類名.內(nèi)部類名 對(duì)象名=外部類對(duì)象.內(nèi)部類對(duì)象;
Outer.Innner oi=new Outer().new Inner();
- 內(nèi)部類用
private
修飾(為了保證數(shù)據(jù)安全),這時(shí)內(nèi)部類就不能被訪問,通常做法是,在外部類中定義一個(gè)方法,在方法內(nèi)部訪問內(nèi)部類(當(dāng)然有邏輯控制),這樣就可以通過外部類來訪問內(nèi)部類; - 當(dāng)內(nèi)部類被
static
修飾時(shí)(這樣做是為了方便數(shù)據(jù)訪問),內(nèi)部類就不能訪問外部類非靜態(tài)成員,因?yàn)殪o態(tài)只能訪問靜態(tài);成員內(nèi)部類被
static
修飾時(shí),訪問方式為外部類名.內(nèi)部類名 對(duì)象名=new 外部類名.內(nèi)部類名();
Outer.Inner oi=new Outer.Inner(); - 內(nèi)部類的方法訪問外部類的成員:通過外部類名限定this對(duì)象;
Outer.this.method()
、Outer.this.params
;
(三)局部內(nèi)部類
- 可以直接訪問外部類的成員;
- 在局部位置(即方法內(nèi)部),可以創(chuàng)建內(nèi)部類對(duì)象,通過對(duì)象名調(diào)用內(nèi)部類方法,來使用局部內(nèi)部類的功能;
- 局部內(nèi)部類訪問局部變量時(shí),局部變量必須被
final
修飾;- 因?yàn)榫植孔兞侩S著方法的調(diào)用而調(diào)用,隨著方法的調(diào)用結(jié)束而消失,而堆內(nèi)存的內(nèi)容并不會(huì)立即消失,所以通過
final
修飾變成常量,即使局部變量消失了,常量的值還存在,依然可以使用。實(shí)際上,通過反編譯class
文件可以發(fā)現(xiàn),被final
修飾的局部變量,在內(nèi)部類中直接是常量,而不是局部變量的變量名。
- 因?yàn)榫植孔兞侩S著方法的調(diào)用而調(diào)用,隨著方法的調(diào)用結(jié)束而消失,而堆內(nèi)存的內(nèi)容并不會(huì)立即消失,所以通過