天天看點

java基礎---方法的重載(overload)和覆寫(重寫)(override)

重載(overload):對于類的方法(包括從父類中繼承的方法),方法名相同,參數清單不同,就稱之為函數的重載。 重載是一個類中多态性的一種表現。 Java 的方法重載,就是在類中可以建立多個方法,它們具有相同的名字,但具有不同的參數和不同的定義。 調用方法時通過傳遞給它們的不同參數個數和參數類型來決定具體使用哪個方法, 這就是多态性。

(1) 參數清單:參數清單又叫參數簽名,指三樣東西:參數的類型,參數的個數,參數的順序(一般不用)。這三者隻要有一個不同就叫做參數清單不同。傳回值類型可以相同也可以不相同。無法以傳回型别作為重載函數的區分标準。

(2)重載關系發生在:同一個類中、子類中的某個方法和父類中繼承下來的方法也可以發生重載的關系、構造器也可以重載。

(3)重載實作父類方法時,參數可以被放大。

(4)重載實作父類方法時,傳回值可以被縮小。

問:區分重載方法時,為什麼隻有類名和方法形參清單作為标準?為什麼不根據傳回值對方法加以區分?

比如對下面這兩個方法來說,雖然它們有同樣的名字和形式參數,但其實是很容易區分它們的:

void f() {}

int f() {}

若編譯器可根據上下文(語境)明确判斷出含義,比如在int x=f()中,那麼這樣做完全沒有問題。然而,我們也可能調用一個方法,同時忽略傳回值;我們通常把這稱為“為它的副作用去調用一個方法”,因為我們關心的不是傳回值,而是方法調用的其他效果。是以假如我們象下面這樣調用方法:f();

Java 怎樣判斷f()的具體調用方式呢?而且别人如何識别并了解代碼呢?由于存在這一類的問題,是以不能根據傳回值類型來區分重載的方法。

基本類型的重載:

public class PrimitiveOverloading {
	static void prt(String s) {
		System.out.print(s);
		}
		void f1(char x) { prt("f1(char)"); }
		void f1(byte x) { prt("f1(byte)"); }
		void f1(short x) { prt("f1(short)"); }
		void f1(int x) { prt("f1(int)"); }
		void f1(long x) { prt("f1(long)"); }
		void f1(float x) { prt("f1(float)"); }
		void f1(double x) { prt("f1(double)"); }

		void f2(byte x) { prt("f2(byte)"); }
		void f2(short x) { prt("f2(short)"); }
		void f2(int x) { prt("f2(int)"); }
		void f2(long x) { prt("f2(long)"); }
		void f2(float x) { prt("f2(float)"); }
		void f2(double x) { prt("f2(double)"); }

		void f3(short x) { prt("f3(short)"); }
		void f3(int x) { prt("f3(int)"); }
		void f3(long x) { prt("f3(long)"); }
		void f3(float x) { prt("f3(float)"); }
		void f3(double x) { prt("f3(double)"); }

		void f4(int x) { prt("f4(int)"); }
		void f4(long x) { prt("f4(long)"); }
		void f4(float x) { prt("f4(float)"); }
		void f4(double x) { prt("f4(double)"); }

		void f5(long x) { prt("f5(long)"); }
		void f5(float x) { prt("f5(float)"); }
		void f5(double x) { prt("f5(double)"); }

		void f6(float x) { prt("f6(float)"); }
		void f6(double x) { prt("f6(double)"); }

		void f7(double x) { prt("f7(double)"); }

		void testConstVal() {
		prt("Testing with 5");
		f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5);
		}

		void testChar() {
		char x = 'x';
		prt("char argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		void testByte() {
		byte x = 0;
		prt("byte argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		void testShort() {
		short x = 0;
		prt("short argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		void testInt() {
		int x = 0;
		prt("int argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		void testLong() {
		long x = 0;
		prt("long argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		void testFloat() {
		float x = 0;
		prt("float argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		void testDouble() {
		double x = 0;
		prt("double argument:");
		f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
		}

		public static void main(String[] args) {
		PrimitiveOverloading p =
		new PrimitiveOverloading();
		p.testConstVal();
		p.testChar();
		p.testByte();
		p.testShort();
		p.testInt();
		p.testLong();
		p.testFloat();
		p.testDouble();
		}
}
           
Testing with 
5:              f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
char argument:  f1(char)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
byte argument:  f1(byte)f2(byte)f3(short)f4(int)f5(long)f6(float)f7(double)
short argument :f1(short)f2(short)f3(short)f4(int)f5(long)f6(float)f7(double)
int argument:   f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
long argument:  f1(long)f2(long)f3(long)f4(long)f5(long)f6(float)f7(double)
float argument:  f1(float)f2(float)f3(float)f4(float)f5(float)f6(float)f7(double)
double argument:f1(double)f2(double)f3(double)f4(double)f5(double)f6(double)f7(double)</span>
           

常數值5 被當作一個int 值處。

在其他所有情況下,若我們的傳入的資料類型(實際參數類型)小于方法中聲明的形式參數類型,實際資料類型就會提升。

char 獲得的效果稍有些不同,這是由于假設它沒有發現一個準确的char 比對,char直接提升為int型。

傳入的實際資料類型(實際參數類型)大于重載中方法聲明的形式參數:

public class PrimitiveOverloading {
	
	static void prt(String s) {
		System.out.println(s);
		}
		void f1(char x) { prt("f1(char)"); }
		void f1(byte x) { prt("f1(byte)"); }
		void f1(short x) { prt("f1(short)"); }
		void f1(int x) { prt("f1(int)"); }
		void f1(long x) { prt("f1(long)"); }
		void f1(float x) { prt("f1(float)"); }
		void f1(double x) { prt("f1(double)"); }
		
		void f2(char x) { prt("f2(char)"); }
		void f2(byte x) { prt("f2(byte)"); }
		void f2(short x) { prt("f2(short)"); }
		void f2(int x) { prt("f2(int)"); }
		void f2(long x) { prt("f2(long)"); }
		void f2(float x) { prt("f2(float)"); }

		void f3(char x) { prt("f3(char)"); }
		void f3(byte x) { prt("f3(byte)"); }
		void f3(short x) { prt("f3(short)"); }
		void f3(int x) { prt("f3(int)"); }
		void f3(long x) { prt("f3(long)"); }
		
		void f4(char x) { prt("f4(char)"); }
		void f4(byte x) { prt("f4(byte)"); }
		void f4(short x) { prt("f4(short)"); }
		void f4(int x) { prt("f4(int)"); }
		
		void f5(char x) { prt("f5(char)"); }
		void f5(byte x) { prt("f5(byte)"); }
		void f5(short x) { prt("f5(short)"); }
		
		void f6(char x) { prt("f6(char)"); }
		void f6(byte x) { prt("f6(byte)"); }
		
		void f7(char x) { prt("f7(char)"); }
		
		void testDouble() {
		double x = 0;
		prt("double argument:");
		f1(x);f2((float)x);f3((long)x);f4((int)x);
		f5((short)x);f6((byte)x);f7((char)x);
		}
		
		public static void main(String[] args) {
		PrimitiveOverloading p = new PrimitiveOverloading();
		p.testDouble();
		}
} 
           
double argument:
f1(double)
f2(float)
f3(long)
f4(int)
f5(short)
f6(byte)
f7(char)
           

方法采用了較小基本類型作為參數,若我們的傳入實際參數較大,就必須用括号中的類型名将其轉為适當的類型(窄化轉換)。如果不這樣做,編譯器會報告出錯。

覆寫 (override):也叫重寫,就是在當父類中的某些方法不能滿足要求時,子類中改寫父類的方法。當父類中的方法被覆寫了後,除非用super關鍵字,否則就無法再調用父類中的方法了。

發生覆寫的條件:

1、“三同一不低” 子類和父類的方法名稱,參數清單,傳回類型必須完全相同,而且子類方法的通路修飾符的權限不能比父類低。

2、子類方法不能抛出比父類方法更多的異常。即子類方法所抛出的異常必須和父類方法所抛出的異常一緻,或者是其子類,或者什麼也不抛出;

3、被覆寫的方法不能是final類型的。因為final修飾的方法是無法覆寫的。

4、被覆寫的方法不能為private。否則在其子類中隻是新定義了一個方法,并沒有對其進行覆寫。

5、被覆寫的方法不能為static。是以如果父類中的方法為靜态的,而子類中的方法不是靜态的,但是兩個方法除了這一點外其他都滿足覆寫條件,那麼會發生編譯錯誤。反之亦然。即使父類和子類中的方法都是靜态的,并且滿足覆寫條件,但是仍然不會發生覆寫,因為靜态方法是在編譯的時候把靜态方法和類的引用類型進行比對。

重載vs覆寫

相同點:

都要求方法同名

都可以用于抽象方法和非抽象方法之間

不同點:

方法覆寫要求參數清單(參數簽名)必須一緻,而方法重載要求參數清單必須不一緻。

方法覆寫要求傳回類型必須一緻,方法重載對此沒有要求。

方法覆寫隻能用于子類覆寫父類的方法,方法重載用于同一個類中的所有方法(包括從父類中繼承而來的方法)

方法覆寫對方法的通路權限和抛出的異常有特殊的要求,而方法重載在這方面沒有任何限制。

父類的一個方法隻能被子類覆寫一次,而一個方法可以在所有的類中可以被重載多次。

另外,對于屬性(成員變量)而言,是不能重載的,隻能覆寫。