七、繼承
多個類中存在相同屬性和行爲時,將這些內容抽取到單獨一個類中,那麼多個類中無需再定義這些屬性和行爲,只需要和抽取出來的類構成繼承關係。
-
-
提高代碼的擴展性。
-
類與類之間產生了關係,是學習多態的前提。例如:學生 is - a 人。
【修飾符】 class 父類 {
...
}
【修飾符】 class 子類 extends 父類 {
...
}
1、Java只支持單繼承,每一個子類只有一個直接父類
2、Java支持多層繼承,父類還可以有父類
3、子類會繼承父類所有特徵,包括成員變量、成員方法,哪怕是私有的,私有的在子類中無法直接使用。
4、子類不會繼承父類的代碼塊、構造器等。
5、但是子類一定會調用父類的實例初始化方法。
-
-
子類雖會繼承父類私有(private)的成員,但子類不能對繼承的私有成員直接進行訪問,可通過繼承的get/set方法進行訪問。如圖所示:
我們說父類的所有成員變量都會繼承到子類中,那麼如果子類出現與父類同名的成員變量會怎麼樣呢?
結論:
(1)當父類的成員變量私有化時,在子類中是無法直接訪問的,所以是否重名不影響,如果想要訪問父類的私有成員變量,只能通過父類的get/set方法訪問;
格式:super.父類成員變量名,但是前提條件,這個成員變量沒有私有化。
說明:雖然我們可以區分父子類的重名成員變量,但是實際開發中,我們不建議這麼幹。
我們說父類的所有方法子類都會繼承,但是當某個方法被繼承到子類之後,子類覺得父類原來的實現不適合於子類,該怎麼辦呢?
1.@Override:寫在方法上面,用來檢測是不是有效的正確覆蓋重寫。這個註解就算不寫,只要滿足要求,也是正確的方法覆蓋重寫。建議保留
2.必須保證父子類之間方法的名稱相同,參數列表也相同。
注意:如果返回值類型是基本數據類型和void,那麼必須是相同
4.子類方法的權限必須【大於等於】父類方法的權限修飾符。
5.幾種特殊的方法不能被重寫
-
靜態方法不能被重寫
-
私有等在子類中不可見的方法不能被重寫
-
final方法不能被重寫
小貼士:重寫時,用到super.父類成員方法,表示調用父類的成員方法。
當類之間產生了關係,其中各類中的構造方法,又產生了哪些影響呢?
-
所以子類是無法繼承父類構造方法的。
-
構造方法的作用是初始化實例變量的,而子類又會從父類繼承所有成員變量
所以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中默認有一個
super()
,表示調用父類的實例初始化方法,父類成員變量初始化後,纔可以給子類使用。
如果父類沒有無參構造怎麼辦?
解決辦法:在子類構造器中,用super(實參列表),顯示調用父類的有參構造解決。
結論:
子類對象實例化過程中必須先完成從父類繼承的成員變量的實例初始化,這個過程是通過調用父類的實例初始化方法來完成的。
-
super():表示調用父類的無參實例初始化方法,要求父類必須有無參構造,而且可以省略不寫;
-
super(實參列表):表示調用父類的有參實例初始化方法,當父類沒有無參構造時,子類的構造器首行必須寫super(實參列表)來明確調用父類的哪個有參構造(其實是調用該構造器對應的實例初始方法)
-
super()和super(實參列表)都只能出現在子類構造器的首行
表示這個類不能被繼承,沒有子類
2、修飾方法
表示這個方法不能被子類重寫
3、聲明常量
如果某個成員變量用final修飾後,沒有set方法,並且必須初始化(可以顯式賦值、或在初始化塊賦值、實例變量還可以在構造器中賦值)
當修飾的是數組變量時,數組的地址值不可以改變但數組元素可以改變。
this代表當前對象的引用(地址值),即對象自己的引用。
-
-
this用於實例方法中:表示調用該方法的對象,即誰在調用,this就代表誰。
1、this.成員變量名
2、this.成員方法
調用當前對象自己的成員方法時,都可以加"this.",也可以省略,實際開發中都省略
3、this()或this(實參列表)
當需要調用本類的其他構造器時,就可以使用該形式。
要求:
如果一個類中聲明瞭n個構造器,則最多有 n - 1個構造器中使用了"this(【實參列表】)",否則會發生遞歸調用死循環
在子類中訪問父類的成員變量,特別是當子類的成員變量與父類的成員變量重名時。
(3)super()或super(實參列表)
在子類的構造器首行,用於表示調用父類的哪個構造器(本質上是該構造器對應的實例初始化方法)
super() 和 this() 都必須是在構造方法的第一行,所以不能同時出現。
this()和this(實參列表) 與 super()或super(實參列表) 不能同時出現
-
沒有super和this
-
在構造器、代碼塊、方法中如果出現使用某個變量,先查看是否是當前塊聲明的局部變量,
-
-
如果當前類中沒有找到,會往上找父類的(非private,跨包還不能是缺省的)
-
-
this :代表當前對象的引用。
-
通過this找成員變量和成員方法時,先從當前類中找,沒有的會往上找父類的(非private,跨包還不能是缺省的)。
-
但是this()或this(實參列表)只會在本類中找
-
-
super :代表父類的存儲空間標識(可以理解爲父類的引用)。
-
通過super找成員變量和成員方法時,直接從父類空間(包含父類的父類繼承的)找
-
super()或super(實參列表)只能從直接父類找
-
通過super只能訪問父類在子類中可見的(非private,跨包還不能是缺省的)
-
this一定是從當前類的成員開始找
即沒有this又沒有super,一定是從局部變量開始找
注意:super和this都不能出現在靜態方法和靜態代碼塊中,因爲super和this都是存在與對象中的
(1)靜態類成員變量的顯式賦值語句
(2)靜態代碼塊中的語句
整個類初始化只會進行一次,如果子類初始化時,發現父類沒有初始化,那麼會先初始化父類。
結論:
每一個類都有一個類初始化方法<clinit>()方法,然後子類初始化時,如果發現父類沒有加載和沒有初始化,會先加載和初始化父類,然後再加載和初始化子類。一個類,只會初始化一次。
實例初始化方法的方法體,由四部分構成:
(1)super()或super(實參列表) 這裏選擇哪個,看原來構造器首行是哪句,沒寫,默認就是super()
(2)非靜態實例變量的顯示賦值語句
(3)非靜態代碼塊
(4)對應構造器中的代碼
特別說明:其中(2)和(3)是按順序合併的,(1)一定在最前面(4)一定在最後面
執行特點:
-
創建對象時,纔會執行,
-
調用哪個構造器,就是指定它對應的實例初始化方法
-
創建子類對象時,父類對應的實例初始化會被先執行(父類的clinit<>(有參或無參)方法執行完後再執行子類的init<>(有參或無參)方法),執行父類哪個實例初始化方法,看用super()還是super(實參列表)
結論:
類初始化肯定優先於實例初始化。
類初始化只做一次。
實例初始化是每次創建對象都要進行。