類與接口(五)java多態、方法重寫、隱藏


一、Java多態性

面向對象的三大特性:封裝、繼承、多態。

多態的類型,分為以下兩種:

  • 編譯時多態: 指的是 方法重載編譯時多態是在編譯時確定調用處選擇那個重載方法,所以也叫 靜態多態,算不上真正的多態。所以,一般說的多態都是運行時的多態。
  • 運行時多態: 由於 方法重寫,所以想要確定引用變量所調用的方法的入口,必須根據運行時的引用變量所指向的實例對象來確定。從而使得同一個引用變量調用同一個方法,但不同的實例對象表現出不同的行為。再簡單點來說,就是在運行時,可以通過指向基類的指針,來調用實現子類中的方法。

下面講的多態都是指 運行時的多態。

多態的定義: 指允許不同類的對象對同一消息做出不同的響應。即同一消息可以根據發送對象的不同而采用多種不同的行為方式。(發送消息就是函數調用);

上面的定義參考了網上的說法,定義中的不同類,都是由同一個基類擴展而來。

多態的好處:

  • 可替換性(substitutability)。多態對已存在代碼具有可替換性。例如,draw函數對圓Circle類工作,對其他任何圓形幾何體,如圓環,也同樣工作。
  • 可擴充性(extensibility)。多態對代碼具有可擴充性。增加新的子類不影響已存在類的多態性、繼承性,以及其他特性的運行和操作。實際上新加子類更容易獲得多態功能。例如,在實現了圓錐、半圓錐以及半球體的多態基礎上,很容易增添球體類的多態性。
  • 接口性(interface-ability)。多態是超類通過方法簽名,向子類提供了一個共同接口,由子類來完善或者覆蓋它而實現的。如圖8.3 所示。圖中超類Shape規定了兩個實現多態的接口方法,computeArea()以及computeVolume()。子類,如Circle和Sphere為了實現多態,完善或者覆蓋這兩個接口方法。
  • 靈活性(flexibility)。它在應用中體現了靈活多樣的操作,提高了使用效率。
  • 簡化性(simplicity)。多態簡化對應用軟件的代碼編寫和修改過程,尤其在處理大量對象的運算和操作時,這個特點尤為突出和重要。

java的多態性三要素:

  1. 繼承;
  2. 重寫;
  3. 父類的引用指向子類的引用

多態性的實現: 依靠動態綁定

綁定: 將一個方法調用與方法主體關聯起來。
前期綁定: 在程序執行前綁定,由編譯器和鏈接程序完成,C語言的函數調用便是前期綁定。
動態綁定: 也稱 后期綁定。在運行時,根據具體的對象類型進行方法調用綁定。除了static方法、final方法(private方法也是final方法),其他方法都是動態綁定;

二、方法重寫 與 隱藏

方法重寫: 就是子類中覆蓋了從父類繼承下來的方法(不是所有的父類方法都可以覆蓋),從而使得通過父類的引用來調用重寫后的方法。也就是說,父類類型與子類類型只保留重寫后的方法。

隱藏: 覆蓋是相對重寫而言的。當子類中出現與父類相同的方法時,重寫是子類與父類只保持一份,但方法隱藏則是子類與父類各自獨立保持一份,也就兩份。從父類繼承下來的成員中,除了部分方法是可以重寫外,其余成員都是隱藏,如變量、內部類、靜態方法等。

注意: final方法既不能重寫,也不能隱藏。

class ParentClass{
	public int a = 5;
	protected final String name = "parentClass";
	
	public final void finalMethod() {//final方法,子類既不能重寫,也不能隱藏
		System.out.println("final方法");
	}
	
	public static void monday() {//靜態方法
		System.out.println("父類ParentClass的monday()方法");
	}
	
	public void count() {//可繼承的成員方法
		System.out.println("父類ParentClass的count()方法");
	}
	
	class InnerClass{//內部類
		public InnerClass() {
			System.out.println("父類ParentClass的內部類");
		}
	}
}

class ChildClass extends ParentClass{
	public int a = 5;
	protected final String name = "ChildClass";
	
	/*//編譯不通過
	 * public final void finalMethod() {
		System.out.println("final方法");
	}*/
	
	public static void monday() {//靜態方法
		System.out.println("子類ChildClass的monday()方法");
	}
	
	public void count() {//可繼承的成員方法
		System.out.println("子類ChildClass的count()方法");
	}
	
	class InnerClass{//內部類
		public InnerClass() {
			System.out.println("子類ChildClass的內部類");
		}
	}	
}

public class MyTest {
	public static void main(String[] args) {
		ChildClass child = new ChildClass2();
		ParentClass parent = child; //類型上轉
		
		System.out.println("---------------變量的隱藏測試-----------------");
		child.a = 10;
		System.out.println("parent.a: "+parent.a);
		System.out.println("child.a: "+child.a);
		
		System.out.println("\n---------------靜態方法的隱藏測試-----------------");
        parent.monday();
        child.monday();
		
		System.out.println("\n---------------方法的重寫測試-----------------");
		parent.count();
		child.count();
		
		System.out.println("\n---------------內部類的隱藏測試-----------------");
		ParentClass.InnerClass pa = parent.new InnerClass();
		ChildClass.InnerClass ch = child.new InnerClass();
	}
}

---------------變量的隱藏測試-----------------
parent.a: 5
child.a: 10

---------------靜態方法的隱藏測試-----------------
父類ParentClass的monday()方法
子類ChildClass的monday()方法

---------------方法的重寫測試-----------------
子類ChildClass的count()方法
子類ChildClass的count()方法

---------------內部類的隱藏測試-----------------
父類ParentClass的內部類
子類ChildClass的內部類

  上面的例子中,只有count()方法是被重寫了,父類類型與子類類型只保持重寫后的方法,而其他成員都是隱藏,父類類型保持一份,子類類型也保持一份。

方法重寫的條件

  • 重寫的方法是子類從父類繼承下來的實例方法,不能是靜態方法
  • 子類重寫后的方法的 返回類型 必須是 原父類方法的返回類型的可替換類型
  • 子類重寫后的方法的訪問權限 不能比 原父類方法的訪問權限低;
  • 子類重寫后的方不能比父類方法拋出更多的異常;
  • 當重寫泛型方法時,先進行類型擦除。再按照上面的4個小點,重寫類型擦除后的方法;

可替換類型補充:

  • 對於返回類型是基本類型、void,重寫方法的返回類型必須是一樣;
  • 對於返回類型是引用類型,返回類型可替換成該類型的 子類型;

class ParentClass{//父類
	
	public int count() {//
		return 0;
	}
	
	Object method() {
		return "aa";
	}
	
	<T extends ParentClass> T getValue(T t) {//泛型方法
		System.out.println();
		return t;
	}
}

class ChildClass extends ParentClass{//子類
	
	public int count() {//重寫count()方法,由於返回類型是基本類型,不能變,必須是一致
		return 0;
	}
	
	public String method() {//重寫method():訪問權限增大,返回類型是Object的子類String
		return "aa";
	}
	
	ChildClass getValue(ParentClass ch) {//重寫泛型方法getValue()
		return null;
	}
}

  解析一下此例子中的泛型方法重寫,父類中的泛型方法getValue()進行類型擦除后,是:

ParentClass getValue(ParentClass t){
    return null;
}

所以,子類ChildClass的方法重寫是合理的。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM