Java 轉型問題其實並不複雜,只要記住一句話:父類引用指向子類對象。java
什麼叫父類引用指向子類對象?編程
從 2 個名詞開始提及:向上轉型(upcasting) 、向下轉型(downcasting)。安全
舉個例子:有2個類,Father 是父類,Son 類繼承自 Father。函數
第 1 個例子:spa
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 引用不能指向父類對象。io
總結:編譯
一、父類引用指向子類對象,而子類引用不能指向父類對象。
二、把子類對象直接賦給父類引用叫upcasting向上轉型,向上轉型不用強制轉換嗎,如:
Father f1 = new Son();
三、把指向子類對象的父類引用賦給子類引用叫向下轉型(downcasting),要強制轉換,如:
f1 就是一個指向子類對象的父類引用。把f1賦給子類引用 s1 即 Son s1 = (Son)f1;
其中 f1 前面的(Son)必須加上,進行強制轉換。
通俗地講便是將子類對象轉爲父類對象。此處父類對象能夠是接口。
一、向上轉型中的方法調用:
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 全部。
二、向上轉型的做用
看上面的代碼:
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關鍵字