Java 轉型問題(向上轉型和向下轉型)


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關鍵字


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM