1.繼承
什麼是繼承關係:
可以基於某個父類堆對象的定義加以拓展,而產生新的子類定義,子類可以繼承父類原來的某些定義,也可以增加原來父類所沒有的定義,或者複寫父類的某些特性
從面向對象的角度上說:繼承是一種從一般到特殊的關係,是一種”is -a”的關係
在java語言中.存在多個類的時候,我們使用:”extends”關鍵字來表示子類和父類的關係
語法格式:在定義子類的時候來表明自己需要拓展於哪一個父類
Public class 子類類名 extends父類類名
{
編寫自己特有的狀態和行爲
}
-----------------------------------------------------------------------------------------------------------
在java中,類和類之間的繼承關係只允許單繼承,不允許多繼承
也就是說一個類A ,只能有一個直接的父類.不能出現類A同時繼承於類B和類C.但是,java中允許多重繼承
多重繼承例子:
動物有胎生和卵生,胎生動物有老虎,老虎中有東北虎,華南虎等..
在java中除了object類之外,每個類都有一個直接的父類:
Class student extends Person(){}
Student的直接父類是Person
問題:class Person{}的直接父類是誰:
Object是java語言的根類(老祖宗類,任何類都是object的子類.), Class Person() 等價於 class Personextends object() ,Object要麼就是一個類的直接父類,要麼就是一個類的間接父類.
繼承的作用:
(1)解決代碼的重複問題
(2)真正的作用,表達一個體系
先寫子類還是先寫父類
一般的我們在開發工程中先編寫多個自定義類,寫完之後,發現多個類之間存在共同的代碼,可研抽取一個去做父類,我們以後做開發都是基於框架/組件來做的,我們是在別人的基礎上,繼續做開發.好比別人提供清水房,我們只需要在清水房的繼承之上做裝修就可以使用,以後我們定義新的類去繼承框架中/組件中提供的父類.
---------------------------------------------------------------------------------------------------------------------------------
子類繼承父類之後,可以擁有父類的某一些狀態和行爲(子類複用父類的功能或狀態)
子類繼承了父類的哪些成員(根據訪問修飾符來判斷):
(1)如果父類中的成員使用public修飾,子類無條件繼承
(2)如果父類中的成員使用protected 修飾,子類也繼承,即使父類和子類不在同一個包中
(3)如果父類和子類在同一個包中,此時子類可以繼承父類中的缺省修飾符的成員
(4)如果父類中的成員使用private修飾,子類打死都繼承不來,private只能在本類中訪問
(5)父類的構造器,子類也不能繼承,因爲構造器必須和當前的類名相同
2方法覆蓋
方法覆寫的原則(一同兩下一大):
一同
(1)實例的方法前面必須相同*(方法前面= 方法名+ 方法的參數列表)
兩小:
(1)子類放到的返回值類型是和父類方法的返回類型相同或者是其子類
(2)子類方法聲明拋出的異常類型和父類方法聲明拋出的異常類相同或者是其子類
一大:
子類方法的訪問權限必須必父類方法訪問權限更大或者相等
Private類的方法不能被子類所繼承,也就不存在覆蓋的概念
判斷是否是覆寫方法的必殺技:@Override標籤;若是覆寫方法,則在方法前貼上該標籤
編譯通過,否則出錯;
只有方法存在覆蓋的概念,字段沒有覆蓋
方法覆蓋解決的問題:父類的某一個行爲不符合子類具體特徵的時候,此時子類需要重新定義父類的方法,並且編寫方法體
3.方法重載和覆蓋的區別
本身二者一點關係都沒有,僅僅因爲名字很像
1方法重載:overload
作用:解決了同一個類中,相同功能的方法名不同的問題,既然是相同功能,那麼方法的名稱應該相同只有一個類
規則:兩同一不同
2方法重寫:override
作用:解決子類繼承父類之後,可能父類的某一個方法不滿足子類的具體特徵.此時需要重新再子類定義中重寫方法體
規則:一同.兩小,一大
4.super關鍵字
需求:在子類中的某一個方法中,去調用父類被覆蓋的方法
//鳥類
class bird() {
public void fly()
{
System.out.println("fly");
}
}
//企鵝
class Penguin extends bird
{
//重新定義了fly
@Override//當前方法覆蓋了父類的方法
public void fly() {
System.out.println("penguin cant fly");
}
public void sing()
{
//調用bird類的fly方法
System.out.println("i am a penguin");
super.fly();
}
}
此時解決方法:使用super關鍵字,什麼是super
This:當前對象,調用this所在的方法,this就是哪一個對象
Super:當前對象的父類對象
5.隱藏和覆蓋
子類初始化的過程:創建子類對象的過程
在創建子類對象之前,會先創建父類對象
調用子類構造器之前,在子類構造器中會先調用父類構造器,默認調用的是父類無參數構造器.
(1)如果父類不存在可以被子類訪問的構造器,則不能存在子類
(2)如果父類沒有提供無參數構造器,此時 子類必須顯式通過super語句去調用父類帶參數的構造器
Super關鍵字使用的場景:
(1)可以使用super解決子類隱藏了父類字段的情況
(2)在子類方法中,調用父類被覆蓋的方法,引出super的例子
(3)在子類構造器中,調用父類構造器,此時必須使用super語句
所謂隱藏就是”遮蔽”的意思:
(1)如果滿足繼承的訪問權限下,隱藏父類靜態方法;若子類定義的靜態方法的簽名和超類中的靜態方法前面相同,那麼此時就是隱藏父類方法.注意是:靜態方法,子類存在和父類一模一樣的靜態方法
(2)滿足繼承訪問權限下,隱藏父類字段,若子類定義的字段和超類中的字段名相同(不管類型),此時就是隱藏父類字段,此時只能通過super訪問被隱藏的字段
(3)隱藏本類字段:若本類中的某局部變量名和字段名相同,此時就是隱藏本類字段,此時只能通過this訪問被隱藏的字段
6.Object
Object要麼是一個類的根類,要麼就是一個類的間接父類
Class ABC{} 其實等價於 class ABC extends object
------------------------------------------------------------------------------------
所有對象(包括數組)都實現這個類的方法
Object本身指對象的意思,我們發生所有的對象都是具有某一共同行爲,所有我們抽象出一個類:object其他對象都會繼承於object類,也就用於object類方法
引用數據類型:類/接口/數組,引用類型又稱爲對象類,所謂的數組名稱應該指數組對象
Object類的常見方法:
1)protected void finalize()
當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。
垃圾回收期在回收某一個對象之前,會先調用該方法,做掃尾工作
2)public final Class<?> getClass()
返回此 Object 的運行時類。返回的Class 對象是由所表示類的static synchronized 方法鎖定的對象。
3)public int hashCode()
返回該對象的哈希碼值。支持此方法是爲了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
hashCode決定了對象再哈希表中的存儲位置
4)public boolean equals(Object obj)
Equals方法本身和”==”相同,比較對象的內存地址
官方建議:每個類都應該覆蓋equals方法,不要比較內存地址,而去比較我們關心的數據.因爲關心的是數據而不是內存地址
比如:兩個學生對象,我們不管是如何new的,只要學號相同我們就應該任務是同一個對象.
5)public String toString()
返回該對象的字符串表示。
打印對象時,其實打印的就是對象的toString方法
System.out.println(obj對象);等價於System.out.print(obj對象.toString);
默認情況下打印對象,打印的是對象的十六進制hashCode值,但我們更關心對象中村粗的數據
官方建議,覆寫toString方法,打印關心的數據
7.多態思想
學了繼承關係,我們知道繼承關係是一種”is a”關係,也就是說子類是父類的一種特殊情況
問題:子類的對象是動物?
既然子類是一種特殊的父類,那麼我們可以不可以任務狗/貓對象時動物類型的對象
-------------------------------------------------------------------------------------------------------------------
當我們的代碼變成如下的樣子時候,多態就產生了:
Animal a = new Dog();
對象a具有兩種類型:
編譯類型:聲明對象變量的類型,表示吧對象看成什麼類型
運行類型:對象的真實類型,Dog,運行類型------>真實類型.
當編譯類型和運行類型不同時候,多態就產生了,前提:編譯類型是運行類型的父類
所謂多態:對象具有多種形態,對象可以存在不同的形式
Animal a = null;
a = new Dog();//此時a表示DOG類型的形態
a = new Cat();//此時a表示Cat類型的形態
---------------------------------------------------------------------------------------------------------------------------
多態的前提:可以是繼承關係(類和類直接),也可以是實現關係(接口和實現類之間).
開發中多態一般指第二種.
-------------------------------------------------------------------------------------------------------------------------
我家裏養了一隻寵物叫”乖乖”,此時”乖乖”可以有多種形態:
乖乖是狗,乖乖叫聲:汪汪
乖乖是喵,乖乖叫聲:喵喵
多態特點:
把子類對象賦給父類變量,在運行時期會表現出具體子類的特徵(調用子類方法)
8.多態的好處
需求:給適用於提供一個位於動物的方法.用於餵養動物.
沒有多態:
針對不同類型的動物,我們得提供不同的餵養方法.不優雅
我只想提供一個方法就能統一餵養所有動物.
存在多態:
統一了餵養動物的行爲,
從上述例子可以得知,多態的作用:當把不同子類對象都當做父類類型看待,可以屏蔽不同子類對象之間的實現差異,從而寫出統一的代碼達到通用編程,以適應需求不斷變化.
多態怎麼產生.
什麼是多態:
多態的特點:
多態的作用
9.多態時方法調用問題
前提:必須先存在多態情況
存在父類:SuperClass ,子類SubClass,方法: doWork;
------------------------------------------------------------------------
測試代碼:
SuperClass clz = new SubClass();//多態
Clz.doWork();
情況1:doWork存在於SuperClass ,不存在於SubClass
編譯通過,執行SuperClass中的doWork方法.
應該先從SubClass類中去找doWork方法,找不到再去superclass中找
// 父類
class SuperClass {
public void doWork() {
System.out.println("super.dowork");
}
}
// 子類
class SubClass extends SuperClass {
}
// 多態調用方法問題
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
情況2:doWork存在於SubClass ,不存在於SuperClass
編譯錯誤
編譯時期,回去編譯類型(SuperClass)中尋找是否有dowork方法
找到:編譯通過
找不到:編譯錯誤
// 父類
class SuperClass {
}
// 子類
class SubClass extends SuperClass {
public void doWork()
{
System.out.println("sub.dowork");
}
}
// 多態調用方法問題
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
情況3:doWork存在於SubClass ,也存在於SuperClass
此時編譯通過: 執行SubClass中的doWork方法
運行時期,調用運行類型中的(Subclass)中的方法
// 父類
class SuperClass {
public void doWork() {
System.out.println("super.dowork");
}
}
// 子類
class SubClass extends SuperClass {
public void doWork()
{
System.out.println("sub.dowork");
}
}
// 多態調用方法問題
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
情況4:doWork存在於SubClass ,也存在於SuperClass,但是doWork是靜態方法
// 父類
class SuperClass {
public void doWork() {
System.out.println("super.dowork");
}
}
// 子類
class SubClass extends SuperClass {
public static void doWork()
{
System.out.println("sub.dowork");
}
}
// 多態調用方法問題
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
此時這種情況,我們稱之爲隱藏,而不叫方法覆蓋
此時結果:編譯通過,執行superclass中的doWork方法
靜態方法的調用只需要類就可以;
如果使用對象來調用靜態方法,其實使用的是對象的編譯類型來調用靜態方法
10.引用類型的轉換:
引用類型的大和小,指的是父類和子類的關係
自動類型轉換:把子類對象賦給父類變量;
Animal a = new Dog();
強制類型轉換 把父類類型對象賦給子類類型變量(但是該父類類型變量的真實類型應該是子類類型)
Animal a = new Dog();
Dog b = (Dog) a;
Instanceof 運算符
判斷該對象是否是某一個類的實例
語法格式: boolean b = 對象 A instanceof 對象B
若對象是類的實例返回true
若對象是類的父類實例,也返回true
---------------------------------------------------------------------------------------
在開發中,有時候我們希望判斷是真實類型的實例,而不想判斷爲編譯類型的實例
System.out.println(Obj instanceof Object );//true
System.out.println(Obj instanceof String);//true
---------------------------------------------------------------
System.out.println(obj.getClass());//獲取對象的真實類型
繼承關係:子類可以繼承到父類中的部分成員,那麼此時子類是可以修改父類的信息
繼承關係破壞封裝
爲什麼引入繼承: 爲了代碼複用問題
解決代碼複用問題,不一定非要使用繼承,也可以使用”包含關係”(has a)
通過在各自的類中創建 t的對象,然後調用t的方法,這樣有了組合
如果A類爲了得到B類的功能行爲:
如果a是b的一種特殊情況,我們就應該使用繼承
否則採用組合
說明:本文乃學習整理參考而來.