Java 轉型問題其實並不復雜,只要記住一句話:父類引用指向子類對象。
什么叫父類引用指向子類對象?
從 2 個名詞開始說起:向上轉型(upcasting) 、向下轉型(downcasting)。
舉個例子:有2個類,Father 是父類,Son 類繼承自 Father。
第 1 個例子:
Father f1 = new Son(); // 這就叫 upcasting (向上轉型) // 現在 f1 引用指向一個Son對象 Son s1 = (Son)f1; // 這就叫 downcasting (向下轉型) // 現在f1 還是指向 Son對象
第 2 個例子:
Father f2 = new Father(); Son s2 = (Son)f2; // 出錯,子類引用不能指向父類對象
你或許會問,第1個例子中:Son s1 = (Son)f1; 問為什么是正確的呢。
很簡單因為 f1 指向一個子類對象,Father f1 = new Son(); 子類 s1 引用當然可以指向子類對象了。
而 f2 被傳給了一個 Father 對象,Father f2 = new Father(); 子類 s2 引用不能指向父類對象。
總結:
1、父類引用指向子類對象,而子類引用不能指向父類對象。
2、把子類對象直接賦給父類引用叫upcasting向上轉型,向上轉型不用強制轉換嗎,如:
Father f1 = new Son();
3、把指向子類對象的父類引用賦給子類引用叫向下轉型(downcasting),要強制轉換,如:
f1 就是一個指向子類對象的父類引用。把f1賦給子類引用 s1 即 Son s1 = (Son)f1;
其中 f1 前面的(Son)必須加上,進行強制轉換。
一、向上轉型。
通俗地講即是將子類對象轉為父類對象。此處父類對象可以是接口。
1、向上轉型中的方法調用:
實例

1 public class Animal { 2 3 public void eat(){ 4 System.out.println("animal eatting..."); 5 } 6 } 7 class Bird extends Animal{ 8 9 public void eat(){ 10 System.out.println("bird eatting..."); 11 } 12 13 public void fly(){ 14 15 System.out.println("bird flying..."); 16 } 17 } 18 class Main{ 19 public static void doEat(Animal h) { 20 h.eat(); 21 } 22 public static void main(String[] args) { 23 24 Animal b=new Bird(); //向上轉型 25 b.eat(); 26 //! error: b.fly(); b雖指向子類對象,但此時丟失fly()方法 27 Animail c1=new Animal(); 28 Bird c2=new Bird(); 29 doEat(c1); 30 doEat(c2);//此處參數存在向上轉型 31 } 32 }
注意這里的向上轉型:
Animal b=new Bird(); //向上轉型 b.eat();
此處將調用子類的 eat() 方法。原因:b 實際指向的是 Bird 子類,故調用時會調用子類本身的方法。
需要注意的是向上轉型時 b 會遺失除與父類對象共有的其他方法。如本例中的 fly 方法不再為 b 所有。
2、向上轉型的作用
看上面的代碼:
public static void doEate(Animail h) { h.sleep(); }
這里以父類為參數,調有時用子類作為參數,就是利用了向上轉型。這樣使代碼變得簡潔。不然的話,如果 doEate 以子類對象為參數,則有多少個子類就需要寫多少個函數。這也體現了 JAVA 的抽象編程思想。
二、向下轉型。
與向上轉型相反,即是把父類對象轉為子類對象。
實例

public class Animail { private String name="Animail"; public void eat(){ System.out.println(name+" eate"); } } class Human extends Animail{ private String name="Human"; public void eat(){ System.out.println(name+" eate"); } } class Main { public static void main(String[] args) { Animail a1=new Human();//向上轉型 Animail a2=new Animail(); Human b1=(Human)a1;// 向下轉型,編譯和運行皆不會出錯 // Human c=(Human)a2;//不安全的向下轉型,編譯無錯但會運行會出錯 } } 實例
Animail a1=new Human();//向上轉型
Human b1=(Human)a1;// 向下轉型,編譯和運行皆不會出錯
這里的向下轉型是安全的。因為 a1 指向的是子類對象。
而
Animail a2=new Animail();
Human c=(Human)a2;//不安全的向下轉型,編譯無錯但會運行會出錯
運行出錯:
Exception in thread "main" java.lang.ClassCastException: study.轉型實例.Animail cannot be cast to study.轉型實例.Human
at study.轉型實例.Main.main(Main.java:8)
向下轉型的作用
向上轉型時 b會遺失除與父類對象共有的其他方法;可以用向下轉型在重新轉回,這個和向上轉型的作用要結合理解。
三、當轉型遇到重寫和同名數據
看下面一個例子,你覺得它會輸出什么?
public class A { public int i=10; void print(){ System.out.println("我是A中的函數"); } } class B extends A{ public int i=20; void print(){ System.out.println("我是B中的函數,我重寫了A中的同名函數"); } void speek(){ System.out.println("向上轉型時我會丟失"); } public static void main(String[] args) { B b=new B(); A a=b;//此處向上轉型 b.print(); System.out.println(b.i); b.speek(); a.print(); System.out.println(a.i); ((B) a).speek();//a在創建時雖然丟失了speek方法但是向下轉型又找回了 } }
結果

我是B中的函數,我重寫了A中的同名函數 20 向上轉型時我會丟失 我是B中的函數,我重寫了A中的同名函數 10 向上轉型時我會丟失
我們發現同名數據是根據創建對象的對象類型而確定,而這個子類重寫的函數涉及了多態,重寫的函數不會因為向上轉型而丟失
多態存在的三個必要條件
- 繼承
- 重寫
- 父類引用指向子類對象
所以不要弄混淆了,父類的方法在重寫后會被子類覆蓋,當需要在子類中調用父類的被重寫方法時,要使用super關鍵字