天天看點

面向對象(四)——繼承多态

一、繼承

1.繼承概述:

把多個類中相同的内容給提取出來定義到一個類中

如何實作繼承呢?

java提供了一個關鍵字:extends

格式:

class 子類名 extends 父類名 {}

繼承的好處:

A:提高了代碼的複用性

B:提高了代碼的維護性

C:讓類與類之間産生關系,是多态的前提。

類與類産生了關系,其實也是繼承的一個弊端:
        類的耦合性增強了。

    開發的原則:低耦合,高内聚
    耦合:類與類的關系
    内聚:就是自己完成某件事情的能力
           
class Person{
        public void eat(){
        System.out.println("吃飯");
    }

    public void sleep(){
        System.out.println("睡覺");
    }
}

class Student extends Person{}

class Teacher extends Person{}

class ExtendsDemo{
    public static void main(String[] args){

        Student s = new Student();
        s.eat();
        s.sleep();
        System.out.println("-------");
        Teacher t = new Teacher();
        t.eat();
        t.sleep();
    }
}
           

2.繼承的特點:

A:java隻支援單繼承,不支援多繼承。

有些語言是支援多繼承的,格式extends 類1,類2…

B:java支援多層繼承(繼承體系)

/*
class Father{}

class Mother{}

class Son extends Father,Mother{}//錯誤的
*/

class GrandFather{
    public void show(){
        System.out.println("我是爺爺");
    }
}

class Father extends GrandFather{
    public void method(){
        System.out.println("我是老子");
    }
}

class Son extends Father{}
class ExtendsDemo{
    public static void main(String[] args){
        Son s = new Son();
        s.method();//使用父親的
        s.show();//使用爺爺的
    }
}
           

3.繼承的注意事項

A:子類隻能繼承父類所有非私有的成員(成員方法和成員變量)

B:子類不能繼承父類的構造方法,但是可以通過super(後面講)關鍵字去通路父類的構造方法

C:不要為了部分功能去使用繼承

class A{
            public void show1(){}
            public void show2(){}
        }

        class B{
            public void show2(){}
            public void show3(){}
        }
        我們發現B類中出現了和A類中一樣的show2()方法,
        是以,我們就用繼承來展現。
        class B extends A{
            public void show3(){}
        }
        這樣其實不好,因為這樣你不但有了show2(),還多了show1(),
        有可能show1()并不是你想要的。


        那麼,我們什麼時候考慮使用繼承呢?
            繼承其實展現的是一種關系:"is a".
                Person
                    Student
                    Teacher

                水果
                    蘋果
                    香蕉
                    橘子

            采用假設法。
                如果有兩個類A,B,隻要他們符合A是B的一種,或者
                B是A的一種,就可以考慮使用繼承。
           
class Father{
    private int num = ;
    public int num2 = ;

    //私有方法,子類不能繼承
    private void method(){
        System.out.println(num);
        System.out.println(num2);
    }

    public void show(){
        System.out.println(num);
        System.out.println(num2);
    }
}

class Son extends Father{
    public void function(){
        //num可以在Father中通路private
        //子類不能繼承父類的私有成員變量
        //System.out.println(num);
        System.out.println(num2);
    }
}


class ExtendsDemo{
    public static void main(String[] args){
        //建立對象
        Son s = new Son();
        //s.method();//子類不能繼承父親的私有方法
        s.show();
    }
}
           

4.繼承中成員變量的關系

類的組成:

成員變量

構造方法

成員方法

而現在我們又講解了繼承,是以,我們就應該來考慮一下,類的

組成部分的各自關系。

繼承中成員變量的關系:
        A:子類中的成員變量和父類中的成員變量名稱不一樣,這個太簡單。
        B:子類中的成員變量和父類中的成員變量名稱一樣,結果呢?
            在子類方法中通路一個變量的查找順序:
                a:在子類方法的局部範圍找,有就使用
                b:在子類的成員範圍找,有就使用
                c:在父類的成員範圍找,有就使用
                d:如果還找不到,就報錯

            簡單記:就近原則
           
class Father{
    public int num = ;
}

class Son extends Father{
    public int num2 = ;
    public int num = ;

    public void show(){
        System.out.println(num);
        System.out.println(num2);
    }
}

class ExtendsDemo{
    public static void main(String[] args){
        //建立對象
        Son s = new Son();
        s.show();
    }
}
           

5.this和super的差別和應用

問題是:我不僅僅要輸出局部範圍的num,還要輸出本類成員範圍的num。怎麼辦?

我還想要輸出父類成員範圍的num,怎麼辦呢?

如果有一個東西和this相似,但是可以直接通路父類的資料就好了

這個關鍵字是:super

this和super的差別:
    分别是什麼
        this:代表本類對象的引用
        super:代表父類存儲空間的辨別(可以了解為父類引用,可以操作父類的成員)
    怎麼用呢?
        A:調用成員變量
            this:成員變量  調用本類的成員變量
            super:成員變量  調用父類的成員變量

        B:調用構造方法
            this(..) 調用本類的構造方法
            super(..)  調用父類的構造方法

        C:調用成員方法
        this.成員方法  調用本類的成員方法
        super.成員方法 調用父類的成員方法
           
class Father{
    public int num = ;
}

class Son extends Father{
    public int num = ;

    public void show(){
        int num = ;
        System.out.println(num);
        System.out.println(this.num);
        System.out.println(super.num);
    }
}

class ExtendsDemo{
    public static void main(String[] args){
        Son s = new Son();
        s.show();
    }
}
           

6.繼承中構造方法的關系

A:子類中所有的構造方法預設都會通路父類中空參數的構造方法

為什麼呢?

因為子類會繼承父類中的資料,可能還會使用父類的資料

是以,子類初始化之前,一定要先完成父類資料的初始化。

注意:子類每一個構造方法的第一條語句預設都是:super()(調用父類的無參構造)
           
class Father{
    public Father(){
        System.out.println("這是Father的無參構造方法");
    }

        public Father(String name){
        System.out.println("這是Father的帶參數構造方法");
    }
}

class Son extends Father{
    public Son(){
        System.out.println("這是son的無參構造方法");
    }

    public Son(String name){
        System.out.println("這是son的帶參數構造方法");
    }
}

class ExtendsDemo{
    public static void main(String[] args){
        //建立對象
        Son s = new Son();
        /*
            得到的結果是:
                這是Father的無參構造方法
                這是son的無參構造方法
        */
        System.out.println("-------");

        Son s2 = new Son("林青霞");
        /*
            得到的結果是:
                這是Father的無參構造方法
                這是son的帶參數構造方法
        子類中所有的構造方法預設都會通路父類中空參數的構造方法
        */

    }
}
           

7.繼承中構造方法的注意事項

如果父類沒有無參構造方法,那麼子類的構造方法會出現什麼現象呢?如何解決呢?

報錯。
    如何解決呢?
    A:在父類中加一個無參構造方法
    B:通過使用super關鍵字去顯示的調用父類的帶參構造方法super(參數名1,參數名2...)
    C:子類通過this去調用本類的其他構造方法
        子類中一定要有一個去通路了父類的構造方法,否則父類資料就沒有初始化

    注意事項:
        this(..)或者super(..)必須出現在第一條語句上。
        如果不是放在第一條語句上,就可能對父類的資料進行了多次初始化,是以必須放在第一條語句上。
           
class Father{
    public Father(String name){
        System.out.println("Father的帶參構造方法");
    }
}

class Son extends Father{
    public Son(){
        System.out.println("Son的無參構造方法");
    }

    public Son(String name){
        System.out.println("Son的帶參構造方法");
    }
}


class ExtendsDemo{
    public static void main(String[] args){

    }
}
           

8.面試題

題1:

/*
    看程式寫結果:
        A:成員變量  就近原則
        B:this和super的問題
            this通路通路本類的成員
            super通路父類的成員
        C:子類構造方法執行前預設先執行父類的無參構造方法。
        D:一個類的初始化過程
            成員變量進行初始化
                預設初始化
                顯示初始化
                構造方法初始化

*/
class Fu{
    public int num = ;
    public Fu(){
        System.out.println("fu");
    }
}

class Zi extends Fu{
    public int num = ;
    public Zi(){
        System.out.println("zi");
    }
    public void show(){
        int num = ;
        System.out.println(num);
        System.out.println(this.num);
        System.out.println(super.num);
    }
}

class ExtendsTest{
    public static void main(String[] args){
        Zi z = new Zi();
        z.show();
    }
}

/*
    程式的結果是:
    fu
    zi
    30
    20
    10
*/
           

題2:

/*
    看程式寫結果:
        A:一個類的靜态代碼塊,構造代碼塊,構造方法的執行流程
            靜态代碼塊>構造代碼塊>構造方法

        B:靜态的内容是随着類的加載而加載
            靜态代碼塊的内容會優先執行
        C:子類初始化之前先會進行父類的初始化
        D:
*/

class Fu{
    static {
        System.out.println("靜态代碼塊");
    }

    {
        System.out.println("構造代碼塊Fu");
    }

    public Fu(){
        System.out.println("構造方法Fu");
    }
}

class Zi extends Fu{
    static{
        System.out.println("靜态代碼塊Zi");
    }

    {
        System.out.println("構造代碼塊zi");
    }

    public Zi(){
        System.out.println("構造方法zi");
    }
}




class ExtendsTest2{
    public static void main(String[] args){
        Zi z = new Zi();
    }
}

/*
    程式的結果:
        靜态代碼塊fu
        靜态代碼塊zi
        構造代碼塊fu
        構造方法fu
        構造代碼塊zi
        構造方法zi
*/
           

題3:

/*
    看程式寫結果
    A:成員變量的問題
        int x = 10;//成員變量是基本類型
        Student s = new Student();//成員變量是引用類型
    B:一個類的初始化過程
        成員變量的初始化
            預設初始化
            顯示初始化
            構造方法初始化
    C:子父類的初始化(分層初始化)
        先進行父類初始化,然後進行子類初始化。

    問題:
        雖然子類中構造方法預設有一個super()
        初始化的時候,不是按照那個順序進行的。
        而是按照分層初始化進行的。
        它僅僅表示要先初始化父類資料,再初始化子類資料。
    */

class X{
    Y b = new Y();
    X(){
        System.out.print("x");
    }
}

class Y(){
    Y(){
        System.out.print("Y");
    }
}

public class Z extends X{
    Y y = new Y();
    Z(){

        //super()     
        System.out.print("Z");
    }
    public static void main(String[] args){
        new Z();
    }
}

/*
    程式的結果;
        YxYz        
*/
           

9、繼承中成員方法的關系

A:子類中的方法和父類中的方法聲明不一樣

B:子類中的方法和父類中的方法聲明一樣

通過子類調用方法:

a:先找子類中,看有沒有這個方法,有就使用

b:再看父類中,有沒有這個方法,有就使用

c:如果沒有就報錯

class Father{
    public void show(){
        System.out.println("show father");
    }
}

class Son extends Father{
    public void method(){
        System.out.println("method son");
    }

    public void show(){
        System.out.println("show son");
    }
}

class ExtendsDemo{
    public static void main(String[] args){
        //常見對象
        Son s = new Son();
        s.show();
        s.method();
    }
}
           

10、方法重寫的應用

方法重寫:子類中出現了和父類中方法聲明一模一樣的方法。

方法重載:
    本類中出現的方法名一樣,參數清單不同的方法。與傳回值無關

子類對象調用方法時候:
    先找子類本身,再找父類。

方法重寫的應用:
    當子類需要父類的功能,而功能主題子類有自己特有内容時,可以重寫父類中的方法
    這樣,即沿襲了父類的功能,又定義了子類特有的内容。

案例:
    A:定義一個手機類
    B:通過研究,發明了一個新手機,這個手機的作用是在打完電話後,可以聽天氣預報。
        按照我們基本設計,我們把代碼寫出來了
        但是呢?我們又發現新手機應該是手機,是以,它應該繼承自手機。
    其實這個時候的設計,并不是最好的。
    因為手機打電話功能,是手機本身就具備的最基本的功能
    是以,我的新手機是不用再提供這個功能的
    但是,這個時候,打電話功能就沒有了,這個不好
    最終,還是加上這個功能,由于它繼承了手機類,是以,我們就直接使用父類的功能即可
    那麼,如何使用父類的功能呢?通過super關鍵字調用。
           
class Phone{

    public void call(String name){
        System.out.println("給"+name+"打電話");
    }
}

class NewPhone extends Phone{
    public void call(String name){
        //System.out.println("給"+name+"打電話");
        super.call(name);
        System.out.println("可以聽天氣預報了");
    }
}

class ExtendsDemo{
    public static void main(String[] args){
        NewPhone np = new NewPhone();
        np.call("梅西");
    }
}
           

11.方法重寫的注意事項

class Father{
    //private void show(){}

    /*
    public void show(){
        System.out.println("show father");
    }
    */
    void show(){
        System.out.println("show father");
    }
    /*
    //被覆寫的方法為static
    public static void  method(){}
    */
    public void method(){}
}

class Son extends Father{
    //private void show(){}

    public  void show(){
        System.out.println("show son");
    }

    //覆寫的方法為 static
    public static void method(){}

    //public void method(){}
}

class ExtendsDemo{
    public static void main(String[] args){
        Son s = new Son();
        s.show();
    }
}
           

12.面試題

方法重寫和方法重載的差別?方法重載能改變傳回值類型嗎?

方法重寫:
        在子類中,出現和父類中一模一樣的方法聲明的現象。

方法重載:
        同一個類中,出現的方法名相同,參數清單不同的現象。

方法重載能改變傳回值類型,因為他和傳回值類型無關。

override:方法重寫
overload:方法重載

this關鍵字和super關鍵字分别代表什麼?一級他們各自的使用場景和作用
    this:代表目前類的對象引用
    super:代表父類存儲空間的辨別。可以了解為父類的引用,通過這個東西可以通路父親的成員

場景:
    成員變量:
        this.成員變量
        super.成員變量
    構造方法:
        this(...)
        super(...)
    成員方法:
        this.成員方法
        super.成員方法