1. 程式人生 > 實用技巧 >Java繼承後訪問成員的特點

Java繼承後訪問成員的特點

繼承後的特點——成員變數

物件訪問成員變數時,會先在子類中查詢有沒有定義對應的變數,若子類中存在就會就近使用子類中的變數,若子類中沒有定義就會沿著繼承關係往上找有沒有定義相應的變數,若父類中也沒有則編譯不通過。程式碼示例:

class Fu {
    // Fu類中的成員變數。
    int num = 5;
    int num2 = 7;
}
class Zi extends Fu {
    // Zi類中的成員變數
    int num = 6;
    public void show() {
        // 訪問父類中的num2
        System.out.println("num2=" + num2);
        // 訪問子類中的num
        System.out.println("num=" + num);
    }
}
public class ExtendsDemo0 {
    public static void main(String[] args) {
        // 建立子類物件
        Zi z = new Zi();
        // 呼叫子類中的show方法
        z.show();
    }
}

演示結果:
num2=7
num=6

子父類中出現了同名的成員變數時,在子類中訪問父類中非私有成員變數,需要使用 super 關鍵字,用法類似於 this

使用格式:

super.父類成員變數名

程式碼如下:

class Zi extends Fu {
	// Zi類中的成員變數
	int num = 6;
	public void show() {
		//訪問父類中的num
		System.out.println("Fu num2=" + num2);
		//訪問父類中的num
		System.out.println("Fu num=" + super.num);
	}
}
演示結果:
Fu num = 7
Zi num = 5

Fu 類中的私有成員變數,子類是不能直接訪問的。編碼時,遵循封裝的原則,使用private修飾成員變數,可以在父類中提供公共的getXxx方法和setXxx方法來訪問。

super和this

父類空間優先於子類物件產生

在每次建立子類物件時,先初始化父類空間,再建立其子類物件本身。目的在於子類物件中包含了其對應的父類空間,便可以包含其父類的成員。程式碼體現在子類的構造方法呼叫時,一定先呼叫父類的構造方法。理解圖解如下:

super和this的含義

  • super :代表父類的儲存空間標識(可以理解為父類的引用)。

  • this :代表當前物件的引用(誰呼叫就代表誰)。

super和this的用法

this.成員變數 -- 本類的
super.成員變數 -- 父類的

this.成員方法名() -- 本類的
super.成員方法名() -- 父類的

程式碼示例:

class Animal {
    public void eat() {
        System.out.println("animal : eat");
    }
}
 
class Cat extends Animal {
    public void eat() {
        System.out.println("cat : eat");
    }
    public void eatTest() {
        this.eat();   // this  呼叫本類的方法
        super.eat();  // super 呼叫父類的方法
    }
}
 
public class ExtendsDemo08 {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.eat();
        Cat c = new Cat();
        c.eatTest();
    }
}

輸出結果為:
animal : eat
cat : eat
animal : eat

注意:

  1. 通過super引用屬性、方法、構造器時,都要求該成員是可見的,即該成員的修飾符不能是private的,跨包的話還不能是預設的。

  2. super的追溯不僅限於直接父類

  3. 如果某個屬性或方法前面使用“this.”,那麼先從本類中查詢,如果未找到,會沿著繼承關係往上找

    如果某個屬性或方法前面使用“super.”,那麼先從直接父類中查詢,如果未找到,會沿著繼承關係往上找

    如果某個屬性或方法前面既沒有“this.”,也沒有“super.”,遵循就近原則,先從本類中查詢,如果未找到,會沿著繼承關係往上找

繼承後的特點——成員方法

成員方法不重名時

如果子、父類中的方法不重名,這時的呼叫是沒有影響的。物件呼叫方法時,會先在子類中查詢有沒有定義對應的方法,若子類中存在就執行子類中定義的方法,若子類中沒有定義就會沿著繼承關係往上找有沒有定義相應的方法,若父類中也沒有則編譯不通過。程式碼示例:

class Fu{
	public void show(){
		System.out.println("Fu類中的show方法執行");
	}
}
class Zi extends Fu{
	public void show2(){
		System.out.println("Zi類中的show2方法執行");
	}
}
public  class ExtendsDemo04{
	public static void main(String[] args) {
		Zi z = new Zi();
     	//子類中沒有定義show方法,但是可以找從父類繼承的show方法去執行
		z.show(); 
		z.show2();
	}
}

成員方法重名——重寫(Override)

如果子、父類中出現重名的成員方法,這時的訪問是一種特殊情況,叫做方法重寫 (Override)。

  • 方法重寫 :子類繼承了父類,可以獲得父類的成員變數和成員方法;可是當父類的某個方法不適合於子類本身的特徵時,在子類中可以根據需要對從父類中繼承來的方法進行改造,也稱為方法的重寫、覆寫。在程式執行時,子類的方法將覆蓋父類的方法。

程式碼如下:

class Fu {
	public void show() {
		System.out.println("Fu show");
	}
}
class Zi extends Fu {
	//子類重寫了父類的show方法
	public void show() {
		System.out.println("Zi show");
	}
}
public class ExtendsDemo05{
	public static void main(String[] args) {
		Zi z = new Zi();
     	// 子類中有show方法,只執行重寫後的show方法
		z.show();  // Zi show
	}
}

重寫的應用

子類可以根據需要,定義特定於自己的行為。既沿襲了父類的功能名稱,又根據子類的需要重新實現父類方法,從而進行擴充套件增強。比如新的手機增加來電顯示頭像的功能,程式碼如下:

class Phone {
	public void sendMessage(){
		System.out.println("發簡訊");
	}
	public void call(){
		System.out.println("打電話");
	}
	public void showNum(){
		System.out.println("來電顯示號碼");
	}
}

//智慧手機類
class NewPhone extends Phone {
	
	//重寫父類的來電顯示號碼功能,並增加自己的顯示姓名和圖片功能
	public void showNum(){
		//呼叫父類已經存在的功能使用super
		super.showNum();
		//增加自己特有顯示姓名和圖片功能
		System.out.println("顯示來電姓名");
		System.out.println("顯示頭像");
	}
}

public class ExtendsDemo06 {
	public static void main(String[] args) {
      	// 建立子類物件
      	NewPhone np = new NewPhone();
        
        // 呼叫父類繼承而來的方法
        np.call();
      
      	// 呼叫子類重寫的方法
      	np.showNum();

	}
}

重寫時,用到super.父類成員方法,表示呼叫父類的成員方法。

方法重寫注意事項

  1. 方法名:必須完全一致

  2. 形參列表:必須完全一致

  3. 返回值型別:

    如果是基本資料型別和void,必須一致

    如果是引用資料型別,重寫的方法的返回值型別 <= 被重寫方法的返回值型別,Student<Person

  4. 修飾符:重寫的方法的修飾符範圍 >= 被重寫方法的修飾符範圍(public > protected > 預設 > private)

  5. 被重寫方法不能被 privatestaticfinal 修飾,如果跨包的話,修飾符預設的也不能被重寫,因為預設的跨包不可見

繼承後的特點——構造方法

首先我們要回憶兩個事情,構造方法的定義格式和作用:

  1. 構造方法的名字是與類名一致的。所以子類是無法繼承父類構造方法的。
  2. 構造方法的作用是初始化成員變數的。所以子類的初始化過程中,必須先執行父類的初始化動作。
    • 子類不會繼承父類的構造器,但是一定會呼叫父類的構造器
    • 預設情況下,呼叫的是父類的無參構造,super()寫或不寫都會呼叫父類的無參構造,如果要寫,必須在子類構造器首行
    • 如果父類沒有無參構造,必須在子類的構造器首行使用super(實參列表)顯式呼叫父類的有參構造,否則編譯報錯
    • super([形參列表]) 和 this([形參列表]) 都必須是在構造方法的第一行,所以不能同時出現。

程式碼示例:

class Father{
    public Father(){
        System.out.println("父類的無參構造");
    }
}

class Son extends Father{
    private String str;
    private int num;

    public Son(){
        //隱含呼叫了super();  子類的構造器中一定會呼叫父類的構造器,預設呼叫父類的無參構造
        System.out.println("子類的無參構造");
    }

    public Son(String str){
        //隱含呼叫了super()
        this.str = str;
        System.out.println("子類的有參構造1");
    }

    public Son(String str,int num){
        //呼叫過載的構造器,隱含呼叫了super()
        this(str);
        this.num = num;
        System.out.println("子類的有參構造2");
    }
}

public class ConstructorTest {
    public static void main(String[] args) {
        Son s1 = new Son();
        /*
            父類的無參構造
            子類的無參構造
         */
        //Son s2 = new Son("java");
        /*
            父類的無參構造
            子類的有參構造1
         */

        Son s3 = new Son("java", 10);
        /*
            父類的無參構造
            子類的有參構造1
            子類的有參構造2
         */
    }
}