面向?qū)ο缶幊倘?特性 --封裝耘眨、繼承、多態(tài)

封裝

把客觀事物封裝成抽象的類境肾,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對(duì)象操作剔难,對(duì)不可信的進(jìn)行信息隱藏。封裝是面向?qū)ο蟮奶卣髦话掠鳎菍?duì)象和類概念的主要特性偶宫。

通俗的說,一個(gè)類就是一個(gè)封裝了數(shù)據(jù)以及操作這些數(shù)據(jù)的代碼的邏輯實(shí)體环鲤。在一個(gè)對(duì)象內(nèi)部纯趋,某些代碼或某些數(shù)據(jù)可以是私有的,不能被外界訪問冷离。

通過這種方式吵冒,對(duì)象對(duì)內(nèi)部數(shù)據(jù)提供了不同級(jí)別的保護(hù),以防止程序中無(wú)關(guān)的部分意外的改變或錯(cuò)誤的使用了對(duì)象的私有部分西剥。但是如果?個(gè)類沒有提供給外界訪問的?法痹栖,那么這個(gè)類也沒有什么意義了。

我們來看一個(gè)常見的 類瞭空,比如:Student

public class Student implements Serializable {

    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
復(fù)制代碼

將對(duì)象中的成員變量進(jìn)行私有化揪阿,外部程序是無(wú)法訪問的疗我。對(duì)外提供了訪問的方式,就是set和get方法南捂。 而對(duì)于這樣一個(gè)實(shí)體對(duì)象吴裤,外部程序只有賦值和獲取值的權(quán)限,是無(wú)法對(duì)內(nèi)部進(jìn)行修改

繼承

繼承 就是子類繼承父類的特征和行為溺健,使得子類對(duì)象(實(shí)例)具有父類的實(shí)例域和方法麦牺,或子類從父類繼承方法,使得子類具有父類相同的行為矿瘦。 在 Java 中通過 extends 關(guān)鍵字可以申明一個(gè)類是從另外一個(gè)類繼承而來的枕面,一般形式如下:

class 父類 {
}

class 子類 extends 父類 {
}
復(fù)制代碼

繼承概念的實(shí)現(xiàn)方式有二類:實(shí)現(xiàn)繼承接口繼承

實(shí)現(xiàn)繼承是指直接使用基類的屬性和方法而無(wú)需額外編碼的能力

接口繼承是指僅使用屬性和方法的名稱缚去、但是子類必須提供實(shí)現(xiàn)的能力

一般我們繼承基本類和抽象類用 extends 關(guān)鍵字潮秘,實(shí)現(xiàn)接口類的繼承用 implements 關(guān)鍵字。

注意點(diǎn):

通過繼承創(chuàng)建的新類稱為“子類”或“派生類”易结,被繼承的類稱為“基類”枕荞、“父類”或“超類”。

繼承的過程搞动,就是從一般到特殊的過程躏精。要實(shí)現(xiàn)繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實(shí)現(xiàn)鹦肿。

子類可以擁有父類的屬性和方法矗烛。

子類可以擁有自己的屬性和方法, 即?類可以對(duì)?類進(jìn)?擴(kuò)展。

子類可以重寫覆蓋父類的方法箩溃。

JAVA 只支持單繼承瞭吃,即一個(gè)子類只允許有一個(gè)父類,但是可以實(shí)現(xiàn)多級(jí)繼承涣旨,及子類擁有唯一的父類歪架,而父類還可以再繼承。

使用implements關(guān)鍵字可以變相的使java具有多繼承的特性霹陡,使用范圍為類繼承接口的情況和蚪,可以同時(shí)繼承多個(gè)接口(接口跟接口之間采用逗號(hào)分隔)。

# implements 關(guān)鍵字

public interface A {
    public void eat();
    public void sleep();
}

public interface B {
    public void show();
}

public class C implements A,B {
}
復(fù)制代碼

值得留意的是: 關(guān)于父類私有屬性和私有方法的繼承 的討論 這個(gè)網(wǎng)上 有大量的爭(zhēng)論烹棉,我這邊以Java官方文檔為準(zhǔn): With the use of the extends keyword, the subclasses will be able to inherit all the properties of the superclass except for the private properties of the superclass. 子類不能繼承父類的私有屬性(事實(shí))攒霹,但是如果子類中公有的方法影響到了父類私有屬性,那么私有屬性是能夠被子類使用的浆洗。

官方文檔 明確說明: private和final不被繼承催束,但從內(nèi)存的角度看的話:父類private屬性是會(huì)存在于子類對(duì)象中的。

通過繼承的方法(比如辅髓,public方法)可以訪問到父類的private屬性

如果子類中存在與父類private方法簽名相同的方法泣崩,其實(shí)相當(dāng)于覆蓋

個(gè)人覺得文章里的一句話很贊,我們不可能完全繼承父母的一切(如性格等)洛口,但是父母的一些無(wú)法繼承的東西卻仍會(huì)深刻的影響著我們矫付。

多態(tài)

同一個(gè)行為具有多個(gè)不同表現(xiàn)形式或形態(tài)的能力就是 多態(tài)。網(wǎng)上的爭(zhēng)論很多第焰,筆者個(gè)人認(rèn)同網(wǎng)上的這個(gè)觀點(diǎn):重載也是多態(tài)的一種表現(xiàn)买优,不過多態(tài)主要指運(yùn)行時(shí)多態(tài)

Java 多態(tài)可以分為 重載式多態(tài)重寫式多態(tài):

-重載式多態(tài),也叫編譯時(shí)多態(tài)挺举。編譯時(shí)多態(tài)是靜態(tài)的杀赢,主要是指方法的重載,它是根據(jù)參數(shù)列表的不同來區(qū)分不同的方法湘纵。通過編譯之后會(huì)變成兩個(gè)不同的方法脂崔,在運(yùn)行時(shí)談不上多態(tài)。也就是說這種多態(tài)再編譯時(shí)已經(jīng)確定好了梧喷。

-重寫式多態(tài)砌左,也叫運(yùn)行時(shí)多態(tài)。運(yùn)行時(shí)多態(tài)是動(dòng)態(tài)的,主要指繼承父類和實(shí)現(xiàn)接口時(shí)铺敌,可使用父類引用指向子類對(duì)象實(shí)現(xiàn)汇歹。這個(gè)就是大家通常所說的多態(tài)性

這種多態(tài)通過動(dòng)態(tài)綁定(dynamic binding)技術(shù)來實(shí)現(xiàn)偿凭,是指在執(zhí)行期間判斷所引用對(duì)象的實(shí)際類型产弹,根據(jù)其實(shí)際的類型調(diào)用其相應(yīng)的方法。也就是說弯囊,只有程序運(yùn)行起來痰哨,你才知道調(diào)用的是哪個(gè)子類的方法。 這種多態(tài)可通過函數(shù)的重寫以及向上轉(zhuǎn)型來實(shí)現(xiàn)常挚。

多態(tài)存在的三個(gè)必要條件:

  1. 繼承
  2. 重寫
  3. 父類引用指向子類對(duì)象:Parent p = new Child();

我們一起來看個(gè)例子作谭,仔細(xì)品讀代碼,就明白了:

@SpringBootTest
class Demo2021ApplicationTests {

    class Animal {
        public void eat(){
            System.out.println("動(dòng)物吃飯奄毡!");
        }
        public void work(){
            System.out.println("動(dòng)物可以幫助人類干活折欠!");
        }
    }

    class Cat extends Animal {
        public void eat() {
            System.out.println("吃魚");
        }
        public void sleep() {
            System.out.println("貓會(huì)睡懶覺");
        }
    }

    class Dog extends Animal {
        public void eat() {
            System.out.println("吃骨頭");
        }
    }

    @Test
    void contextLoads() {
        //part1
        Cat cat_ = new Cat();
        cat_.eat();
        cat_.sleep();
        cat_.work();

        //part2
        Animal cat=new Cat();
        cat.eat();
        cat.work();
        cat.sleep();//此處編譯會(huì)報(bào)錯(cuò)。

        Animal dog=new Dog();
        dog.eat();//結(jié)果為:吃骨頭吼过。此處調(diào)用子類的同名方法锐秦。

        //part3
        //如果想要調(diào)用父類中沒有的方法,則要向下轉(zhuǎn)型盗忱,這個(gè)非常像"強(qiáng)轉(zhuǎn)"
        Cat cat222 = (Cat)cat;        // 向下轉(zhuǎn)型(注意酱床,如果是(Cat)dog 則會(huì)報(bào)錯(cuò))
        cat222.sleep();        //結(jié)果為:貓會(huì)睡懶覺。 可以調(diào)用 Cat 的 sleep()
    }

}
復(fù)制代碼

我們來看上面part1部分:

Cat cat_ = new Cat();
cat_.eat();
cat_.sleep();
cat_.work();
復(fù)制代碼

結(jié)果:

吃魚 貓會(huì)睡懶覺趟佃。 動(dòng)物可以幫助人類干活扇谣!

cat_.work(); 這處繼承了父類Animal的方法昧捷,還是很好理解的 我們接著看part2:

Animal cat=new Cat();
cat.eat();
cat.work();
cat.sleep();//此處編譯會(huì)報(bào)錯(cuò)。

Animal dog=new Dog();
dog.eat();//結(jié)果為:吃骨頭罐寨。此處調(diào)用子類的同名方法靡挥。
復(fù)制代碼

這塊就比較特殊了,我們一句句來看

Animal cat=new Cat(); 像這種這個(gè) 父類引用指向子類對(duì)象鸯绿,這種現(xiàn)象叫做:"向上轉(zhuǎn)型",也被稱為多態(tài)的引用跋破。

cat.sleep();這句 編譯器會(huì)提示 編譯報(bào)錯(cuò)。 表明:當(dāng)我們當(dāng)子類的對(duì)象作為父類的引用使用時(shí)瓶蝴,只能訪問子類中和父類中都有的方法毒返,而無(wú)法去訪問子類中特有的方法

值得注意的是:向上轉(zhuǎn)型是安全的。但是缺點(diǎn)是:一旦向上轉(zhuǎn)型舷手,子類會(huì)丟失的子類的擴(kuò)展方法拧簸,其實(shí)就是 子類中原本特有的方法就不能再被調(diào)用了。所以cat.sleep()這句會(huì)編譯報(bào)錯(cuò)男窟。

cat.eat();這句的結(jié)果打咏铺瘛:吃魚。程序這塊調(diào)用我們Cat定義的方法蝎宇。

cat.work();這句的結(jié)果打拥芫ⅰ:動(dòng)物可以幫助人類干活! 我們上面Cat類沒有定義work方法姥芥,但是卻使用了父類的方法兔乞,這是不是很神奇。其實(shí)此處調(diào)的是父類的同名方法

Animal dog=new Dog();dog.eat();這句的結(jié)果打印為:吃骨頭凉唐。此處調(diào)用子類的同名方法庸追。

由此我們可以知道當(dāng)發(fā)生向上轉(zhuǎn)型,去調(diào)用方法時(shí)台囱,首先檢查父類中是否有該方法淡溯,如果沒有,則編譯錯(cuò)誤簿训;如果有咱娶,再去調(diào)用子類的同名方法。如果子類沒有同名方法强品,會(huì)再次去調(diào)父類中的該方法

我們現(xiàn)在知道了 向上轉(zhuǎn)型時(shí)會(huì)丟失子類的擴(kuò)展方法膘侮,哎,但我們就是想找回來的榛,這可咋辦琼了? 向下轉(zhuǎn)型可以幫助我們,找回曾經(jīng)失去的

我們來看part3:

    Cat cat_real = (Cat)cat;  //注意 此處的cat 對(duì)應(yīng)上面父類Animal夫晌,可不是子類
    cat_real.sleep(); 
復(fù)制代碼

Cat cat = (Cat)cat; cat222.sleep(); 這個(gè)向下轉(zhuǎn)型非常像"強(qiáng)轉(zhuǎn)"雕薪。

打印的結(jié)果:貓會(huì)睡懶覺昧诱。此處又能調(diào)用了 子類Cat 的 sleep()方法了。

一道簡(jiǎn)單的面試題

我們?cè)賮砜匆坏烙幸馑嫉念}所袁,來強(qiáng)化理解

public class Main {

    static class Animal{
        int weight = 10;

        public void print() {
            System.out.println("this Animal Print:" + weight);
        }

        public Animal() {
            print();
        }
    }

    static class Dog extends Animal {
        int weight = 20;

        @Override
        public void print() {
            System.out.println("this Dog Print:" + weight);
        }

        public Dog() {
            print();
        }
    }

    public static void main(String[] args) {
        Dog dog = new Dog();

        System.out.println("---------------");
        Animal dog222 = new Dog();
        Dog dog333 =  (Dog)dog222;

        System.out.println("---------------");
        Dog dog444 = (Dog)new Animal();

    }
}
復(fù)制代碼

執(zhí)行結(jié)果:

this Dog Print:0 this Dog Print:20


this Dog Print:0

this Dog Print:20


this Animal Print:10 Exception in thread "main" java.lang.ClassCastException: com.zj.Main Animalcannotbecasttocom.zj.MainDog at com.zj.Main.main(Main.java:15)

做對(duì)了嘛鳄哭,不對(duì)的話,復(fù)制代碼去idea中debug看看 [圖片上傳失敗...(image-f7f8e1-1666835336011)]

我們先看第一部分

Dog dog = new Dog();

程序內(nèi)部的執(zhí)行順序:

  1. 先 初始化 父類Animal 的屬性 int weight=10
  2. 然后 調(diào)用父類Animal的構(gòu)造方法,執(zhí)行print()
  3. 實(shí)際調(diào)用子類Dog的print()方法,打痈傺:this Dog Print:0,由于此時(shí)的子類屬性weight 并未初始化
  4. 初始化 子類Dog 的屬性 int weight=20
  5. 調(diào)用 子類Dog的構(gòu)造方法锄俄,執(zhí)行print()
  6. 實(shí)際調(diào)用當(dāng)前類的print()方法局劲,打印this Dog Print:20

其中有幾處我們需要注意一下:實(shí)例化子類dog,程序會(huì)去默認(rèn)優(yōu)先實(shí)例化父類奶赠,即子類實(shí)例化時(shí)會(huì)隱式傳遞Dog的this調(diào)用父類構(gòu)造器進(jìn)行初始化工作鱼填,這個(gè)和JVM的雙親委派機(jī)制有關(guān),這里就不展開講了毅戈,先挖個(gè)坑苹丸,以后再來填??。

當(dāng)程序調(diào)用父類Animal的構(gòu)造方法時(shí)苇经,會(huì)隱式傳遞Dog的this,類似于:

Dog dog = new Dog(this);

static class Animal{
  public Animal(this) {
      print(this);//子類又把print()給重寫了
  }
}

static class Dog extends Animal {
  public Dog(this) {
      print(this);//此時(shí)子類的 屬int weight 被沒有初始化默認(rèn)為0
  }
}
復(fù)制代碼

這塊其實(shí)和JVM的虛方法表有關(guān)赘理,這又是一個(gè)大坑,以后慢慢填??扇单。

我們接著看第2部分 Animal dog222 = new Dog();這句是向上轉(zhuǎn)型商模,程序加載順序和第一部分Dog dog = new Dog();是一樣的,都是實(shí)例化類的過程 Dog dog333 = (Dog)dog222;這個(gè)是向下轉(zhuǎn)型蜘澜,并沒有調(diào)用類構(gòu)造器施流,這塊等會(huì)和第3部分結(jié)合講

最后我們來看下第3部分Dog dog444 = (Dog)new Animal();這句先實(shí)例化Andimal類,它沒有父類鄙信,就直接實(shí)例化當(dāng)前類瞪醋,打印this Animal Print:10。然后(Dog)表示向下轉(zhuǎn)型装诡,但是為啥運(yùn)行會(huì)報(bào)ClassCastException 異常呢?且第2部分Dog dog333 = (Dog)dog222;卻沒有問題?

我們可以發(fā)現(xiàn)银受,向下轉(zhuǎn)型可以讓子類找回其獨(dú)有的方法 但是向下轉(zhuǎn)型是不安全的,實(shí)現(xiàn) 向下轉(zhuǎn)型 前需要先實(shí)現(xiàn) 向上轉(zhuǎn)型鸦采。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚓土,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赖淤,更是在濱河造成了極大的恐慌蜀漆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咱旱,死亡現(xiàn)場(chǎng)離奇詭異确丢,居然都是意外死亡绷耍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門鲜侥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褂始,“玉大人,你說我怎么就攤上這事描函∑槊纾” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵舀寓,是天一觀的道長(zhǎng)胆数。 經(jīng)常有香客問我,道長(zhǎng)互墓,這世上最難降的妖魔是什么必尼? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮篡撵,結(jié)果婚禮上判莉,老公的妹妹穿的比我還像新娘。我一直安慰自己育谬,他們只是感情好券盅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膛檀,像睡著了一般渗饮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宿刮,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天互站,我揣著相機(jī)與錄音,去河邊找鬼僵缺。 笑死胡桃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的磕潮。 我是一名探鬼主播翠胰,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼自脯!你這毒婦竟也來了之景?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤膏潮,失蹤者是張志新(化名)和其女友劉穎锻狗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轻纪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年油额,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刻帚。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡潦嘶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崇众,到底是詐尸還是另有隱情掂僵,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布顷歌,位于F島的核電站锰蓬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏衙吩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一溪窒、第九天 我趴在偏房一處隱蔽的房頂上張望坤塞。 院中可真熱鬧,春花似錦澈蚌、人聲如沸摹芙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浮禾。三九已至,卻和暖如春份汗,著一層夾襖步出監(jiān)牢的瞬間盈电,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工杯活, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匆帚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓旁钧,卻偏偏與公主長(zhǎng)得像吸重,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歪今,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容