Java基礎|繼承

面向對象的三大特徵:封裝性、繼承性、多態性。
繼承是多態的前提,如果沒有繼承,就沒有多態。

繼承是什麼

  • 這裏說一個師傅和徒弟的例子。師傅把自己所有會的技藝都教給了徒弟,那從徒弟的角度來看,我們就可以說徒弟繼承了師傅的技藝。在面向對象中,師傅叫做父類基類超類,徒弟就叫做子類派生類。這裏父類和子類,並不能對應於現實生活中的兒子繼承家產的例子。兒子繼承家產,兒子拿了錢,父親就沒錢了。在面向對象中,繼承強調的是一種行爲和屬性的複製。如果硬要舉父子的例子,你可以說父親不禿頂,兒子也不禿頂,繼承了他優秀的基因。父親能運動喫飯,兒子也能運動喫飯,這個例子就比較合適。

繼承解決了什麼問題

  • 還是說上面那個師傅和徒弟的例子。一方面,師傅一個人學習了技藝,他就可以把這種能力傳授給很多徒弟。表現在面向對象中,就是父類完成了共性抽取,子類通過繼承父類就可以獲得這種共性。另一方面,我們知道徒弟不僅僅獲得了師傅的技藝,他還有可能擁有自己獨特的技藝。注意,這裏獨特的技藝並不是直接通過其它師傅得到的,java 中類與類之間只允許單繼承,不允許有多個直接父類,也就是說,徒弟不能直接拜多個師傅。但是可以間接有多個師傅,比如徒弟->師傅b->師傅a,徒弟繼承師傅a,師傅a繼承師傅b。再回到徒弟獨特的技藝上說,子類通過繼承父類,不僅僅擁有了父類的成員變量和成員方法,還可以擁有自己獨特的成員變量和方法,這樣就完成了對父類的擴展。

繼承的格式

  • public class 子類名 extends 父類名稱 {//類內容}
public class Fu {
    int numFu;//父類成員變量
    public void methodFu() {
        System.out.println("這裏是父類成員方法");
    }
}

public class Zi extends Fu {
}

public class Demo {
        public static void main(String[] args) {
            Zi zi = new Zi();
            zi.numFu = 1;
            zi.methodFu();
        }
}
  • 在上面的 demo 中我們可以看到,子類繼承父類後就可以訪問 父類的成員變量和成員方法。

繼承中成員方法的訪問特點

public class Fu {
    public void method() {//父子都有
        System.out.println("這裏是父類method方法");
    }
    public void methodFu() {//父類獨有,被繼承後子類也會擁有
    	System.out.println("這裏是父類methodFu方法");
    }
}

public class Zi extends Fu {
    public void method() {//父子都有
        System.out.println("這裏是子類method方法");
    }
     public void methodZi() {//子類獨有
     System.out.println("這裏是子類methodZi方法");
    }
}
  • 上面的代碼展示了成員方法存在的三種情況。
    訪問規則: 創建的對象是誰,就優先用誰中的方法。沒有則向上找。
  1. 父類和子類有重名的方法。(這裏涉及到一個重寫(Override)的概念,下面會講)
		Fu fu = new Fu();
		fu.method();//輸出:"這裏是父類method方法"
		
		Zi zi = new Zi();
		zi.method();//輸出:"這裏是子類method方法"。優先用Zi類中的method方法
  1. 父類擁有獨有的方法,此時子類繼承後也會擁有。
		Fu fu = new Fu();
		fu.methodFu();//輸出:"這裏是父類methodFu方法"。父親調用自己的成員方法,當然可以。
		
		Zi zi = new Zi();
		zi.methodFu();//輸出:"這裏是父類methodFu方法" 。Zi類中沒有methodFu方法,則向上找。
  1. 子類擁有獨有的方法。
		Fu fu = new Fu();
		fu.methodZi();//錯誤,父類不會向下找子類中的方法
		
		Zi zi = new Zi();
		zi.methodZi();//輸出:"這裏是子類methodZi方法"

繼承中成員變量的訪問特點

  1. 直接通過 對象名.成員變量名 訪問。
    規則: new的時候等號左邊是誰,就優先去誰中找相應的成員變量。沒有則向父類找。
public class Fu {
    int num;
    int numFu;
}

public class Zi extends Fu {
    int num;
}

public class Demo {
        public static void main(String[] args) {
            Fu fu = new Fu();
			fu.num;//訪問父類的num
			
			Zi zi = new Zi();
			zi.num;//訪問子類的num
			zi.numFu;//先去Zi類中找numFu,沒有則向上找父類中的numFu。
        }
}		

 2. 通過成員方法訪問成員變量
  規則: 方法屬於誰,就優先去誰中找相應的成員變量。沒有則向父類找。

public class Fu {
    int num = 10;
    public int methodFu(){
    	return num;
    }
}

public class Zi extends Fu {
    int num = 20;
    public int methodZi(){
    	return num;
    } 
}

public class Demo {
    public static void main(String[] args) {
        Zi fu = new Zi();
        zi.methodZi();//返回20,methodZi屬於Zi類,所以會輸出Zi中的num
        zi.methodFu();//返回10,methodFu屬於Fu類,所以會輸出Fu中的num
    }
}

方法重寫

  • 如果子類對父類繼承過來的方法不滿意,想要增加邏輯或者自己編寫邏輯,就需要重寫。
public class Fu {
    public void method() {
        System.out.println("這是父類method方法");
    }
}

public class Zi extends Fu {
    @Override//這個註解可以幫助我們檢查父類是否有這個方法,同時表明這是一個覆寫的方法。
    public void method() {
        System.out.println("這是子類method方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        Zi zi = new Zi();
		zi.method();//輸出:"這是子類method方法"
    }
}
  • 重寫和重載的區別:
    • 重寫(Override):方法的名稱一樣,參數列表也一樣。
    • 重載(Overload):方法的名稱一樣,參數列表不一樣。
  • 方法重寫的注意點:
  1. 子類必須保證重寫的方法的名稱和參數列表與父類的相同。
  2. 子類重寫的方法的返回值必須小於等於父類方法的返回值範圍。
public class Fu {
    public Object method() {
        return null;
    }
}

public class Zi extends Fu {
    @Override
    public String method() {//這樣寫是可以的,如果父類是返回String,這裏返回Object就不行了。
        return null;
    }   
}
  1. 子類重寫的方法的權限必須大於等於父類方法的權限修飾符。不過當父類方法的權限修飾符爲 private 時,無法被重寫。
    權限修飾符 :public > protected > default(代表什麼都不寫) > private
public class Fu {
    String method() {
        return null;
    }
}

public class Zi extends Fu {
    @Override
    public String method() {//這裏權限修飾符不寫,寫protected,public都行
        return null;
    }
}

this 和 super 關鍵字

  • this 代表本類對象,super 代表父類對象。
  1. 從成員變量的使用上來說:
public class Fu {
    int num;
}

public class Zi extends Fu {
    int num;
    private void method(int num) {
        System.out.println(num);//輸出method方法上的num參數
        System.out.println(this.num);//輸出Zi類的成員變量num
        System.out.println(super.num);//輸出Fu類的成員變量num
    }
}
  1. 從成員方法的使用來說:
public class Fu {
    public void method() {}
}

public class Zi extends Fu {
    public void method() {
        super.method();//調用父類的method方法
        this.methodZi();//調用本類的methodZi方法
    }
    public void methodZi() {}
}
  1. 從構造方法上來說:
public class Fu {
	//父類無參構造
    public Fu() { }
}

public class Zi extends Fu {
    public Zi() {
        super();//調用父類的無參構造
    }

    public Zi(int num) {
        this(num,null);//調用下面的構造方法
    }

    public Zi(int num,Object obj) {
	}
}
  • this 和 super 關鍵字內存圖解
    在這裏插入圖片描述
    講到super關鍵字,在這就可以補充一個小知識點:爲什麼 java 類與類只允許單繼承?假設一個類繼承了兩個父類,父類a有一個成員變量obj,父類b也有一個成員變量obj。那麼在子類中調用super.obj的時候就會出問題,我調用的到底是父類a的obj還是父類b的obj那?很明顯,我們不清楚。所以java不允許多繼承。

繼承中構造函數的訪問特點

明確一點:new 一個子類對象,會先去調用父類的構造方法,再調用子類的構造方法。沒有子類,哪來父類。所以必須先有父類。所以在子類的構造方法中,一定要調用父類構造方法。

  1. 情形一:父類沒寫構造函數(沒寫會默認贈送一個無參構造)或者只寫了一個無參構造。那麼在子類的構造方法必須調用父類的無參構造。如果子類也沒寫構造函數:沒寫會默認贈送一個無參構造,並且在其中調用父類的無參構造。如果子類顯式寫了無參構造,那就必須調用父類的無參構造。如果子類寫了有參構造,那就必須調用父類的無參構造。
public class Fu {
    public Fu() {}//不寫會贈送一個無參構造,如果寫了有參構造就不會贈送了
}

public class Zi extends Fu {
    public Zi() {//不寫會贈送一個無參構造,如果寫了有參構造就不會贈送了
        super();//這行代碼不寫也行,會默認贈送一個
    }
	//這兩個構造函數都行,明確我們的目的:構造出一個父類對象
    public Zi(Object obj) {
        super();//這行代碼不寫也行,會默認贈送一個
    }
}

  1. 情形二:父類只有有參構造。此時不會給父類贈送一個無參構造了。所以在我們的子類構造函數中,必須調用父類的有參構造。
public class Fu {
    public Fu(Object obj) {}
}

public class Zi extends Fu {
    public Zi() {
        super(null);
    }
	//這兩種構造函數都行
    public Zi(Object obj) {
        super(obj);
    }
}
  1. 情形三:父類顯式寫出無參構造,同時還有有參構造。子類此時沒寫構造函數,沒寫會默認贈送一個無參構造,並且在其中調用父類的無參構造。子類此時寫了構造函數,可以在構造函數中調用父類的無參構造,也可以調用父類的有參構造。
public class Fu {
    public Fu() {}
    public Fu(Object obj) {}
}

public class Zi extends Fu {}//不會報錯

public class Zi extends Fu {
    public Zi() {}//會贈送一個super()
}

public class Zi extends Fu {
    public Zi() {
        super(null);
    }
	//這兩個構造函數都行
    public Zi(Object obj) {
        super(obj);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章