Java提供了編譯時多態和運行時多態兩種多態機制。前者是通過方法重載實現的,后者是通過方法的覆蓋實現的。
在方法覆蓋中,子類可以覆蓋父類的方法,因此同類的方法會在父類與子類中有着不同的表現形式。
在Java語言中,基類的引用變量不僅可以指向基類的實例對象,也可以指向其子類中的實例對象。同樣,接口中的引用變量也可以指向其實現類的實例對象。而程序調用的方法在運行時期才動態綁定(綁定是指將一個方法調用和一個方法主體聯系在一起),綁定的是引用變量所指向的具體實例對象的方法,也就是內存中正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。通過這種動態綁定實現了多態。由於只有在運行時才能確定調用哪個方法,因此通過方法覆蓋實現的多態也可以被稱為運行時多態。
示例一:
1 public class Base { 2 public Base(){ 3 g(); 4 } 5 6 public void g() { 7 System.out.println("Base g()"); 8 } 9 10 public void f() { 11 System.out.println("Base f()"); 12 } 13 }
1 public class Derived extends Base{ 2 3 public void g() { 4 System.out.println("Derived g()"); 5 } 6 7 public void f() { 8 System.out.println("Derived f()"); 9 } 10 11 public static void main(String[] args) { 12 Base base=new Derived(); 13 base.f(); 14 base.g(); 15 } 16 }
程序運行結果:
在上面的例子中,由於子類Derived的f()方法和g()方法與父類Base方法同名,因此Derived的方法會覆蓋Base的方法。在執行 Base base=new Derived(); 語句時,會調用Base類的構造函數,而在Base的構造函數中,執行了g()方法,由於Java語言的多態性,此時會調用子類Derived的g()方法,而不是父類Base 的g()方法,因此會輸出"Derived g()".由於實際創建的是Derived對象,后面的方法調用都會調用子類Derived的方法。
但要注意,若此時父類中沒有f()方法和g()方法,會編譯報錯。
示例二:
1 package Test; 2 3 public class Base { 4 private String baseName="base"; 5 public Base(){ 6 callName(); 7 } 8 9 public void callName(){ 10 System.out.println(baseName); 11 } 12 13 static class Sub extends Base{ 14 private String baseName="sub"; 15 public void callName(){ 16 System.out.println(baseName); 17 } 18 } 19 20 public static void main(String[] args) { 21 Base base=new Sub(); 22 } 23 }
程序運行結果:
在上面的例子中,new Sub();在創造派生類的過程中首先創建基類對象,然后才能創建派生類。創建基類即默認調用Base()方法,在方法中調用callName()方法,由於派生類中存在此方法,則被調用的callName()方法是派生類中的方法,此時派生類中普通成員變量(private String baseName="sub";)還未構造,所以變量baseName的值為null。
此外,只有類的方法才有多態的概念,類的成員變量沒有多態的概念。示例如下:
1 public class Base { 2 public int i=1; 3 }
1 public class Derived extends Base{ 2 3 private int i=2; 4 5 public static void main(String[] args) { 6 Base base=new Derived(); 7 System.out.println(base.i); 8 } 9 }
程序運行結果:
由此可見,成員變量是無法實現多態的,類的成員變量的值取父類還是子類並不取決於創建對象的類型,而是取決於所定義變量的類型,這是在編譯期間確定的。