title: Java運行時多態性:繼承和接口的實現
date: 2017-11-04 09:50:44
tags: javaSE
categories: javaSE
先插一句題外話:
繼承與多態的區別
-
繼承,子類繼承父類中所以的屬性和方法,但是對於private的屬相和方法,由於這個是父類的隱私,所以子類雖然是繼承了,但是沒有可以訪問這些屬性和方法的引用,所以相當於沒有繼承到。很多時候,可以理解爲,沒有繼承。
-
多態:就是父類引用可以持有子類對象。這時候只能調用父類中的方法,而子類中特有方法是無法訪問的,因爲這個時候(編譯時)你把他看作父類對象的原因,但是到了運行的時候,編譯器就會發現這個父類引用中原來是一個子類的對像,所以如果父類和子類中有相同的方法時,調用的會是子類中的方法,而不是父類的。可以這麼說:編譯時看父類,運行時看子類。
進入正題:
Java運行時多態性:繼承和接口的實現
Java是面向對象的語言,而運行時多態性是面向對象程序設計代碼重用的一個最強大機制,動態性的概念也可以被說成“一個接口,多個方法”。Java實現運行時多態性的基礎是動態方法調度,它是一種在運行時而不是在編譯期調用重載方法的機制,下面就繼承和接口實現兩方面談談java運行時多態性的實現。
一、通過繼承中超類對象引用變量引用子類對象來實現
舉例說明:
//定義超類superA
class superA {
int i = 100;
void fun() {
System.out.println(“This is superA”);
}
}
//定義superA的子類subB
class subB extends superA {
int m = 1;
void fun() {
System.out.println(“This is subB”);
}
}
//定義superA的子類subC
class subC extends superA {
int n = 1;
void fun() {
System.out.println(“This is subC”);
}
}
class Test {
public static void main(String[] args) {
superA a;
subB b = new subB();
subC c = new subC();
a = b;
a.fun();
(1)
a = c;
a.fun();
(2)
}
} 12345678910111213141516171819202122232425262728293031323334353637383940
運行結果爲:
This is subB
This is subC123
上述代碼中subB和subC是超類superA的子類,我們在類Test中聲明瞭3個引用變量a, b, c,通過將子類對象引用賦值給超類對象引用變量來實現動態方法調用。也許有人會問:“爲什麼(1)和(2)不輸出:This is superA”。java 的這種機制遵循一個原則:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。
所以,不要被上例中(1)和(2)所迷惑,雖然寫成a.fun(),但是由於(1)中的a被b賦值,指向了子類subB的一個實例,因而(1)所調用的fun()實際上是子類subB的成員方法fun(),它覆蓋了超類superA的成員方法fun();同樣(2)調用的是子類subC的成員方法fun()。
另外,如果子類繼承的超類是一個抽象類,雖然抽象類不能通過new操作符實例化,但是可以創建抽象類的對象引用指向子類對象,以實現運行時多態性。具體的實現方法同上例。
不過,抽象類的子類必須覆蓋實現超類中的所有的抽象方法,否則子類必須被abstract修飾符修飾,當然也就不能被實例化了。
二、通過接口類型變量引用實現接口的類的對象來實現
接口的靈活性就在於“規定一個類必須做什麼,而不管你如何做”。我們可以定義一個接口類型的引用變量來引用實現接口的類的實例,當這個引用調用方法時,它會根據實際引用的類的實例來判斷具體調用哪個方法,這和上述的超類對象引用訪問子類對象的機制相似。
舉例說明:
//定義接口InterA
interface InterA
{
void fun();
}
//實現接口InterA的類B
class B implements InterA
{
public void fun()
{
System.out.println(“This is B”);
}
}
//實現接口InterA的類C
class C implements InterA
{
public void fun()
{
System.out.println(“This is C”);
}
}
class Test
{
public static void main(String[] args)
{
InterA a;
a= new B();
a.fun();
a = new C();
a.fun();
}
} 1234567891011121314151617181920212223242526272829303132
輸出結果爲:
This is B
This is C123
上例中類B和類C是實現接口InterA的兩個類,分別實現了接口的方法fun(),通過將類B和類C的實例賦給接口引用a而實現了方法在運行時的動態綁定,充分利用了“一個接口,多個方法”展示了Java的動態多態性。
需要注意的一點是:Java在利用接口變量調用其實現類的對象的方法時,該方法必須已經在接口中被聲明,而且在接口的實現類中該實現方法的類型和參數必須與接口中所定義的精確匹配。
結束語:以上就是java運行時多態性的實現方法,大家在編程過程中可以靈活運用,但是在性能要求較高的代碼中不提倡運用運行時多態,畢竟Java的運行時動態方法調用較之普通的方法調用的系統開銷是比較大的。
Java靜態方法不具有多態性詳解
動態綁定機制使得基類的引用能夠指向正確的子類對象,從而使得面向基類編程成爲可能。
然而動態綁定在以下兩種情況會失效。
1、基類方法是private或final修飾的
這個很好理解,因爲private說明該方法對子類是不可見的,子類再寫一個同名的方法並不是對父類方法進行復寫(Override),而是重新生成一個新的方法,也就不存在多態的問題了。同理也可以解釋final,因爲方法同樣是不可覆蓋的。
2、方法是static修飾的
代碼如下所示.
class Base {
public static void staticMethod() {
System.out.println("Base staticMehtod");
}
public void dynamicMehtod() {
System.out.println("Base dynamicMehtod");
}
}
class Sub extends Base {
public static void staticMethod() {
System.out.println("Sub staticMehtod");
}
public void dynamicMehtod() {
System.out.println("Sub dynamicMehtod");
}
}
public class TJ4 {
public static void main(String args[]) {
Base c = new Sub();
c.staticMethod();
c.dynamicMehtod();
}
}123456789101112131415161718192021222324252627
輸出結果如下:
Base staticMehtod
Sub dynamicMehtod
123
輸出結果並不像設想的那樣,輸出 “Sub staticMehtod”。因爲靜態方法是與類而不是與某個對象相關聯,c.staticMethod();等同於Car.staticMethod(); 所以儘量不要使用實例變量去調用靜態方法,避免混淆。