Java學習筆記 第十天

The_______shy發表於2020-11-25

第一章 繼承

1.1 概述:

當多個類中存在相同屬性和行為時,可將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只要繼承那一個類即可。其中,多個類可以稱為子類,單獨那一個類稱為父類、超類(superclass)或者基類。

繼承描述的是事物之間的所屬關係,這種關係是:的關係。例如,圖中兔子屬於食草動物,食草動物屬於動物。可見,父類更通用,子類更具體。我們通過繼承,可以使多種事物之間形成一種關係體系。

繼承的定義: 就是子類繼承父類的屬性行為,使得子類物件具有與父類相同的屬性、相同的行為。子類可以直接訪問父類中的非私有的屬性和行為。

1.2 繼承的格式

通過extends關鍵字,可以宣告一個子類繼承另外一個父類,定義格式如下:

class 父類{
	...
}
class 子類 extends 父類{
	...
}

程式碼演示如下:

//  定義一個員工:父類
public class Employee {
    public void method(){
        System.out.println("方法執行!");
    }
}


//  定義了一個員工的子類:講師
public class Teacher extends Employee {

}


//  定義了員工的另一個子類:助教
public class Assistant extends Employee{
}


public class Demo01Extends {
    public static void main(String[] args) {
        //  建立了一個子類物件
        Teacher teacher = new Teacher();
        //  Teacher類當中雖然什麼都沒寫,但是會繼承來自父類的method方法
        teacher.method();

        //  建立另一個子類助教的物件
        Assistant assistant = new Assistant();
        //  Assistant類當中雖然什麼都沒寫,但是也會繼承來自父類的method方法
        assistant.method();
    }
}

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

  • 成員變數不重名

如果子類父類中出現不重名的成員變數,這時的訪問是沒有影響的。程式碼如下:

//	定義父類
class Fu{
	int numFu = 5;
}


//	定義子類
class Zi extends Fu{
	int numZi = 10;
	public void show(){
		//	繼承而來且不重名,故可直接訪問
		System.out.println("父類當中的numFu = " + numFu);
		System.out.println("子類當中的numZi = " + numZi);	
	}
}


//	定義測試類
class ExtendDemo02{
	public void main(String[] args){
		//	建立子類物件
		Zi zi = new Zi();
		//	呼叫子類中的show方法
		zi.show();
	}
}



演示結果:
父類當中的numFu = 5
子類當中的numZi = 10
  • 成員變數重名
    如果子類父類中出現重名的成員變數,這時的訪問是有影響的(按照就近原則選取成員變數,如果沒有就向上查詢,而絕不會向下查詢)。程式碼如下:
//	定義父類
class Fu{
	int num = 5;
}

//	定義子類
class Zi extends Fu{
	int num = 10;
	public void show(){
	
		//	此時父類和子類當中都有名為num的成員變數,所以根據就近原則,會輸出子類當中的num
		System.out.println("父類當中的numFu = " + num);
		System.out.println("子類當中的numZi = " + num);			
	}
}


//	定義測試類
class ExtendDemo02{
	public void main(String[] args){
		//	建立子類物件
		Zi zi = new Zi();
		//	呼叫子類中的show方法
		zi.show();
	}
}



演示結果:
父類當中的numFu = 10
子類當中的numZi = 10

子父類中出現了同名的成員變數時,在子類中需要訪問父類中非私有成員變數時,需要使用super關鍵字,修飾父類成員變數,類似於之前學過的this
使用格式:

super.父類成員變數名

對上面的程式碼進行修改,程式碼如下:

//	定義父類
class Fu{
	int num = 5;
}

//	定義子類
class Zi extends Fu{
	int num = 10;
	public void show(){
		//	使用super關鍵字選取
		System.out.println("父類當中的numFu = " + super.num);
		System.out.println("子類當中的numZi = " + num);			
	}
}


//	定義測試類
class ExtendDemo02{
	public void main(String[] args){
		//	建立子類物件
		Zi zi = new Zi();
		//	呼叫父類中的fuShow方法
		zi.fuShow();
		zi.ziShow();
	}
}

小貼士: Fu類中的成員變數是非私有的,子類中可以直接訪問。若Fu類中的成員變數私有了,子類是不能直接訪問的。通常編碼時,我們遵循封裝的原則,使用private修飾成員變數,那麼如何訪問父類的私有成員變數呢?對!可以在父類中提供公共的getXxx方法和setXxx方法。

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

1.4.1 成員方法不重名

如果子類父類中出現不重名的成員方法,這時的呼叫是沒有影響的。物件呼叫方法時,會先在子類中查詢有沒有對應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。簡單來說就是,訪問規則依然按照就近原則訪問,且絕對不會向下查詢。

程式碼如下:

//	定義父類
class Fu{
	public void fuShow(){
		System.out.println("Fu類當中的show方法執行");
	}
}

//	定義子類
class Zi extends Fu{
	public void ziShow(){
		System.out.println("Zi類當中的show方法執行");
	}
}

//	定義測試類
public class ExtendsDemo04{
	public static void main(String[] args){
		Zi zi = new Zi();
		
		//	子類中沒有show方法,但是可以找到父類方法去執行
		zi.show()
	}
}

1.4.2 成員方法重名—方法重寫

如果子類父類中出現重名的成員方法,這時的訪問是一種特殊情況,叫做方法重寫(Override)
子類中出現與父類一模一樣的方法時(返回值型別,方法名和引數列表都相同),會出現覆蓋效果,也稱為重寫或者複寫。宣告不變,重新實現。
程式碼如下:

//	定義父類
class Fu{
	public void show(){
		System.out.println("Fu show");
	}
}

//	定義子類
class Zi extends Fu{
	//	子類重寫了父類的show方法
	@Override
	public void show(){
		System.out.println("Zi show");
	}
}

//	定義測試類
public class ExtendsDemo05{
	public static void main(String[] args){
		Zi zi = new Zi();
		//	子類當中有show方法,只執行重寫後的show方法
		zi.show();
	}
}

方法重寫的5大注意事項:

  • 【1】.必須保證父子類之間的方法名稱相同,引數列表也相同。
    @Override:寫在方法前面,用來檢測是不是有效的正確覆蓋重寫。
    這個註解就算不寫,只要滿足要求,也是正確的方法覆蓋重寫。但是推薦寫上,以防出現錯誤

  • 【2】.子類方法的返回值必須【小於等於】父類方法的返回值範圍
    詳細來說,分以下幾種情況:

1. 父類被重寫的方法的返回值型別是void,則子類重寫的方法的返回值型別只能是void

2. 父類被重寫的方法的返回值型別是A型別,則子類重寫的方法的返回值型別可以是A類或A類的子類(java.lang.Object類是所有類的公共最高父類(祖宗類),java.lang.String就是Object的子類

3. 父類被重寫的方法的返回值型別是基本資料型別(比如:double),則子類重寫的方法的返回值型別必須是相同的基本資料型別(必須也是double)

4. 父類被重寫的方法的返回值型別是基本資料型別(比如:double),則子類重寫的方法的返回值型別必須是相同的基本資料型別(必須也是double)

  • 【3】.子類方法的許可權必須【大於等於】父類方法的許可權修飾符
    小擴充套件提示:public > protected > (default) > private
    備註:(default)不是關鍵字default,而是什麼都不寫,留空

  • 【4】.子類重寫的方法丟擲的異常型別不大於父類被重寫的方法丟擲的異常型別(具體放到異常處理時候講)

  • 【5】.子類和父類中的同名同引數的方法要麼都宣告為非static的(考慮重寫),要麼都宣告為static的(不是重寫)。

1.4.2 方法重寫的應用

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

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

//	定義子類
class NewPhone extends Phone{

	//	重寫父類的showNum成員方法
	@Override
	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.5 繼承後的特點—構造方法

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

1.構造方法的名字是與類名一致的。所以子類是無法繼承父類構造方法的
2.構造方法的作用是初始化成員變數的。所以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中預設有一個super(),表示呼叫父類的構造方法,父類成員變數初始化後,才可以給子類使用。程式碼如下:

//	定義父類
class Fu{
	private int n;

	//	定義父類構造方法
	public Fu(){
		System.out.println("Fu()");
	}
}

//	定義子類
class Zi extends Fu{
	//	定義子類構造方法
	public Zi{
		//	super()	呼叫父類構造方法
		super();
		System.out.println("Zi()");
	}
}

//	定義測試類
public class ExtendsDemo07{
	public static void main(String[] args){
		Zi zi = new Zi();
	}
}

輸出結果:
Fu()
Zi()

繼承關係中父子類構造方法的訪問特點

  • 子類構造方法當中有一個預設隱含的“super()”呼叫,所以一定是先呼叫的父類構造,後執行的子類構造
  • 子類構造可以通過super關鍵字來呼叫父類過載構造
  • super的父類構造呼叫,必須是子類構造方法的第一個語句。也就是說,不能一個子類構造呼叫多次super構造

第二章 易混淆概念思考

本人由於之前學過一丁點Python語言,所以在幾類變數之間理解與Python當中有所混淆,這裡進行區分總結(如有錯誤,請予以私信、評論等指正)。ps:再次宣告,本人只是學生,請以批判的眼光看待所發文章

首先宣告,Java當中沒有Python當中所謂的全域性變數的概念

  • 成員變數、this
    類當中,方法外定義的變數是成員變數,類似於Python當中的例項屬性作用域是整個類(即可以在整個類中都被訪問到)。
    但是不同於Python的是,Python當中訪問例項屬性,一定是通過self.例項屬性來訪問。而Java當中通常情況下都可直接訪問成員變數,只有在當區域性變數和成員變數重名的時候,才會使用和self功能相似的this關鍵字,用this.成員變數來訪問這裡使用this關鍵字的目的僅僅是用於區分成員變數和區域性變數。而this實際上是代表所在類當前物件的記憶體地址

  • 類變數(類屬性,static修飾)
    類當中,方法外,用static關鍵字修飾的變數就是類變數,類變數相當於Python當中的類屬性,和Python中的類屬性理解相同,類變數作用域和成員變數一樣都是整個類,可通過物件名或類名來呼叫,但是推薦使用類名來呼叫類變數,以強調這是類變數

  • 類方法(靜態方法,static修飾)
    static關鍵字修飾的成員方法就是類方法,和類變數一樣,可通過物件名或類名來呼叫,但是推薦使用類名來呼叫類方法,以強調這是類方法

  • 區域性變數
    1.形參(形式引數)
    作用域:在整個方法內有效
    2.方法區域性變數 (方法內定義)
    從定義這個變數開始到方法結束這一段時間內有效

  • 繼承和super關鍵字
    當類之間產生了關係以後,如果子類和父類當中的成員變數不重名,則在訪問時是沒有影響的。但是,當子類和父類當中出現重名的**成員變數(成員方法)**時候,這時的訪問是有影響的。此時,在子類當中需要訪問父類中非私有成員變數時,需要使用super關鍵字,修飾父類成員變數,類似於之前學過的this關鍵字,作用在於區別父類當中的成員變數(成員方法)和子類當中的成員變數(成員方法)

  • this和super關鍵字的詳細彙總
    this關鍵字三種用法:

1.當在本類當中成員變數和方法的區域性變數相同時,可以用this.成員變數來加以和方法的區域性變數進行區分
2.和Python當中一樣,在本類的成員方法A中,可以通過用this.成員方法B,來在成員方法A中訪問成員方法B
3.在本類的構造方法中,訪問本類的另一個構造方法,程式碼示範如下;

//	定義父類
public class Fu{
	int num = 30;
}

public class Zi extends Fu {
    int num = 20;
    public Zi(){
//        super();  //  這一行不再贈送
        this(123);  //  本類的無參構造,呼叫本類的有參構造
//        this(1,2);    //  錯誤寫法
    }
    public Zi(int n){
        this(1,2);
    }
    public Zi(int n,int m){

    }
}   

注意:
在上面第三點用this訪問另外一個構造方法時候有以下兩點考慮:
A.this(…)呼叫也必須是構造方法的第一個語句,即只能呼叫唯一一個。
B.super和this兩種呼叫不能同時使用,不能同時使用。

  • super關鍵字三種用法
    1.在子類的成員方法中訪問父類的成員變數,用以區別變數名相同時的情形
    2.在子類的成員方法中訪問父類的成員方法,用以區別成員方法名相同時的情形
    3.在子類的構造方法中訪問父類的構造方法
//	定義父類
public class Fu {
    int num = 10;
    public void method(){
        System.out.println("父類方法");
    }
}


//	定義子類
public class Zi extends Fu{
    int num = 20;
    public Zi(){
        super();
    }
    public void methodZi(){
        System.out.println(super.num);  //  父類當中的num
    }
    public void method(){
        super.method(); //  訪問父類中的method
        System.out.println("子類方法");
    }
}

相關文章