1. 程式人生 > >Java基礎6——繼承和多型

Java基礎6——繼承和多型

1、繼承

超類是子類的公共屬性和方法的集合,子類除了繼承超類的所有功能,也可以修改繼承或增加新屬性和方法。繼承也是一種抽象,提高了類的重用性,讓類與類之間產生了關係(多型的基礎),降低了編碼和維護的工作量。

類與類、物件與物件之間除了繼承關係,還有組合等關係。繼承表達的是從屬關係“是一種(is-a)”,而組合是將已存在類的物件放到新類中,表達包含關係“有一個(has-a)”。

[classModifier] class ClassName extends SuperClassName
{
	//類體
}

注意:

(1)繼承是為“是一種”關係建模的,不要為了獲取其它類的功能而盲目擴充套件一個類。

(2)Java僅支援單繼承,即一個子類只能直接繼承一個超類。多繼承容易帶來安全隱患,如當多個父類中有相同方法名,但內容不同。但是支援多層繼承。

2、方法重寫和super

(1)方法重寫

子類雖然繼承了父類的功能,但有時需要修改演算法或增加、取消功能,此時儘量不要修改原始碼,修改不止一處,呼叫它的程式也得改。為了提高程式的擴充套件性,需要重寫父類該功能。

當子類的方法和父類方法的簽名和返回值型別均相同時,父類的方法被覆蓋。複寫時還要用父類中的同名方法時,用super.方法名(<引數>);。在一個類之前新增重寫標註@Override,之後必須重寫父類方法,不然編譯器會報錯。

注意:

①子類覆蓋父類時,必須保證子類許可權大於父類許可權,否則會出現編譯錯誤。父類中的私有方法在類外不能被訪問,所以不能被覆蓋。

②父類中的抽象方法必須被覆蓋,否則子類也為抽象類。靜態方法能被繼承,但不能被覆蓋,如果父類中的靜態方法被子類重新定義,通過<父類名>.<靜態方法名>呼叫隱藏的靜態方法。final方法也不能被覆蓋(類的繼承打破了封裝性,final避免類和方法被繼承和複寫)。

(2)屬性的隱藏

當子類聲明瞭與超類相同的成員變數名,可以不同型別,從超類繼承的變數將被隱藏。當子類執行繼承自超類的操作時,處理繼承自超類的變數,當子類操作它自己宣告的方法時,操作它自己的變數。在本類宣告的方法中使用“super.屬性”訪問從超類繼承的屬性。

(3)呼叫父類的構造方法

父類的構造方法不會被子類繼承,只能在子類的構造方法中使用關鍵字super呼叫。super()必須放在子類構造方法的第一行,如果沒有被顯示地呼叫,自動將super()作為子類構造方法的第一句。從最遠超類Object類的構造方法開始執行,最後執行當構造方法體的其它語句,因為子類先參考父類初始化動作。

最好為每個類提供無參構造方法,否則必須在其子類中顯式指定構造方法。

子類中建構函式通過this呼叫子類中其它建構函式時,子類中至少有一個函式訪問super。this和super不能同時出現,因為它們都要寫第一行,初始化動作必須先做。

對於構造方法,儘量少的動作把物件的狀態初始化,如果可以避免,不要呼叫任何方法。如果方法具有多型性,被覆蓋,會出現潛在問題,能夠安全呼叫的僅有final和private方法。

3、多型

多型指在有繼承的情況下,超類及其子類的物件可以響應同名的訊息(覆蓋),具體的實現方法卻不同;父類或介面的引用指向了其子類的物件。多型提高了程式的擴充套件性。

Object o=new Circle();//型別提升,向上轉型
o.toString();//o呼叫哪個toString()由o的實際型別決定,稱為動態繫結
Circle c=(Circle)o;//向下轉型,強制將父類引用轉換為子類型別

在多型中非靜態方法成員的特點: 編譯時,參閱引用型變數所屬類中是否有呼叫的方法,沒有則編譯失敗;執行時,參閱物件所屬類中是否有呼叫的方法,從物件建立時的類開始,沿類層次向上查詢,一旦找到一個實現就停止查詢。簡單地說,編譯看左邊,執行看右邊。

(限面試)成員變數和靜態方法的特點:無論編譯執行,都參考左邊,引用型變數所屬類。靜態方法與物件無關,靜態區只有類名呼叫。而非靜態方法儲存在方法區的非靜態區,非靜態區方法被物件所引用

引用變數的型別轉換,將引用轉換為另一型別的引用,並不改變物件本身的型別。只能被轉為:任何一個直接或間接超類的型別或實現的介面(向上轉型,一般隱式轉換),引用所指的物件的型別(唯一向下轉型)。當一個引用被轉換為其超類引用後,通過它能夠訪問的只有超類中宣告的方法。需要訪問子類中的方法,必須轉型為子類的引用。

型別比較運算子instanceof,比較某個物件是否屬於某個型別。o instanceof Circle相當於if(this.getClass()!=o.getClass()) return flase;。

4、Object類

Object類處在類層次的最高點,是所有類的直接或間接的超類,包含所有Java類都具有的屬性和行為。當一個類沒有指定繼承性,它的父類預設是Object類,建構函式第一句的super()即為Object()。Object類不是抽象類,因為如果抽象,子類必須強制實現其中的所有抽象方法,使類的實現更復雜。

public String toString()返回該物件的字串表示。預設返回該物件所屬類名+該物件用十六進位制表示的雜湊碼getClass().getName()+”@”+Integer.toHexString(hashCode());。(getClass()在Object類中,返回此物件執行時的類;用Class類描述所有類的共同特徵,其中getName()返回此Class物件所表示的類的名稱)通常被覆蓋,返回該物件的描述性字串。

boolean equals(Object obj)通過==比較兩引用是否指向同一物件,即記憶體地址是否相同。通常該方法會被重寫,用來判斷兩物件是否具有相同內容。

class Demo //extends Object
{
	private int num;

	Demo(int num)
	{
		this.num=num;
	}

	public boolean equals(Object obj)
	//複寫Object類中的equals方法,引數必須是Object類,不然是過載
	{
		//return this.num==obj.num//編譯失敗,obj類中沒有num變數
		if(!(obj instanceof Demo))//obj是否是Demo型別
			return false;
		Demo d=(Demo)obj;//向下轉型
		return num==d.num;
	}
}

class Person
{
}

class ObjectDemo
{
	public static void main(String[] args) 
	{
		Demo d1=new Demo(2);
		Demo d2=new Demo(2);
		Person p=new Person();

		System.out.println(d1.equals(d2));//true
		System.out.println(d1.equals(p));//false,丟擲異常更確切
	}
}