JAVA對象轉型分爲兩種:一種叫向下轉型,而另外一種是向上轉型(父類對象的引用或者叫基類對象的引用指向子類對象,這就是向上轉型)。那什麼叫轉型呢?好比把double類型轉成float類型,把float類型轉成int類型,把long類型轉成int類型,這些都叫轉型。把一種形式轉成另一種形式就叫轉型。除了基礎數據類型的轉型以外(基礎數據類型的轉型:小的能夠轉成大的,大的也能夠轉成小的。),對象領域裏面也有對象之間的轉型。java
1.1.對象轉型實例一編程
1 package javastudy.summary; 2 3 /** 4 * 父類Animal 5 * @author gacl 6 * 7 */ 8 class Animal { 9 10 public String name; 11 12 public Animal(String name) { 13 this.name = name; 14 } 15 } 16 17 /** 18 * 子類Cat繼承Animal 19 * @author gacl 20 * 21 */ 22 class Cat extends Animal { 23 24 /** 25 * Cat添加本身獨有的屬性 26 */ 27 public String eyeColor; 28 29 public Cat(String n, String c) { 30 super(n);//調用父類Animal的構造方法 31 this.eyeColor = c; 32 } 33 } 34 35 /** 36 * 子類Dog繼承Animal 37 * @author gacl 38 * 39 */ 40 class Dog extends Animal { 41 /** 42 * Dog類添加本身特有的屬性 43 */ 44 public String furColor; 45 46 public Dog(String n, String c) { 47 super(n);//調用父類Animal的構造方法 48 this.furColor = c; 49 } 50 51 } 52 53 /** 4 * 下面是這三個類的測試程序 55 * @author gacl 56 * 57 */ 58 public class TestClassCast { 59 60 /** 61 * @param args 62 */ 63 public static void main(String[] args) { 64 65 Animal a = new Animal("name"); 66 Cat c = new Cat("catname","blue"); 67 Dog d = new Dog("dogname", "black"); 68 /** 69 * a instanceof Animal這句話的意思是a是一隻動物嗎? 70 * a是Animal這個類裏面的是一個實例對象,因此a固然是一隻動物,其結果爲true。 71 */ 72 System.out.println(String.format("a instanceof Animal的結果是%s",a instanceof Animal));//true 73 /** 74 * c是Cat類的實例對象的引用,即c表明的就是這個實例對象, 75 * 因此「c是一隻動物」打印出來的結果也是true。 76 * d也同樣,因此「d是一隻動物」打印出來的結果也是true。 77 */ 78 System.out.println(String.format("c instanceof Animal的結果是%s",c instanceof Animal));//true 79 System.out.println(String.format("d instanceof Animal的結果是%s",d instanceof Animal));//true 80 /** 81 * 這裏判斷說「動物是一隻貓」,不符合邏輯,因此打印出來的結果是false。 82 */ 83 System.out.println(String.format("a instanceof Cat的結果是%s",a instanceof Cat)); 84 /** 85 * 這句話比較有意思了,a自己是Animal類的實例對象的引用, 86 * 但如今這個引用不指向Animal類的實例對象了,而是指向了Dog這個類的一個實例對象了, 87 * 這裏也就是父類對象的引用指向了子類的一個實例對象。 88 */ 89 a = new Dog("bigyellow", "yellow"); 90 System.out.println(a.name);//bigyellow 91 /** 92 * 這裏的furColor屬性是子類在繼承父類的基礎上新增長的一個屬性,是父類沒有的。 93 * 所以這裏使用父類的引用對象a去訪問子類對象裏面新增長的成員變量是不容許的, 94 * 由於在編譯器眼裏,你a就是Animal類對象的一個引用對象,你只能去訪問Animal類對象裏面所具備的name屬性, 95 * 除了Animal類裏面的屬性能夠訪問之外,其它類裏面的成員變量a都沒辦法訪問。 96 * 這裏furColor屬性是Dog類裏面的屬性,所以你一個Animal類的引用是沒法去訪問Dog類裏面的成員變量的, 97 * 儘管你a指向的是子類Dog的一個實例對象,但由於子類Dog從父類Animal繼承下來, 98 * 因此new出一個子類對象的時候,這個子類對象裏面會包含有一個父類對象, 99 * 所以這個a指向的正是這個子類對象裏面的父類對象,所以儘管a是指向Dog類對象的一個引用, 100 * 可是在編譯器眼裏你a就是隻是一個Animal類的引用對象,你a就是隻能訪問Animal類裏面所具備的成員變量, 101 * 別的你都訪問不了。 102 * 所以一個父類(基類)對象的引用是不能夠訪問其子類對象新增長的成員(屬性和方法)的。 103 */ 104 //System.out.println(a.furColor); 105 System.out.println(String.format("a指向了Dog,a instanceof Animal的結果是%s",a instanceof Animal));//true 106 /** 107 * 這裏判斷說「a是一隻Dog」是true。 108 * 由於instanceof探索的是實際當中你整個對象究竟是什麼東西, 109 * 並非根據你的引用把對象看出什麼樣來判斷的。 110 */ 111 System.out.println(String.format("a instanceof Dog的結果是%s",a instanceof Dog));//true112 /** 113 * 這裏使用強制轉換,把指向Animal類的引用對象a轉型成指向Dog類對象的引用, 114 * 這樣轉型後的引用對象d1就能夠直接訪問Dog類對象裏面的新增的成員了。115 */ 116 Dog d1 = (Dog)a; 117 System.out.println(d1.furColor);//yellow 118 } 119 120 }
運行結果:ide
a instanceof Animal的結果是ture測試
c instanceof Animal的結果是turethis
d instanceof Animal的結果是turespa
a instanceof Cat的結果是flase設計
bigyelloworm
a指向了Dog,instanceof Animal的結果是true對象
a instanceof Dog的結果是ture繼承
內存分析:
在內存中能夠看到,指向Dog類實例對象的引用對象a是一個Animal類型的引用類型,這就變得頗有意思l,Animal類型指向了Dog這個對象,那麼,程序會把這隻Dog當成一隻普通的Animal,既然是把Dog當成一隻普通的Animal,那麼Dog類裏面聲明的成員變量furColor就不能訪問了,由於Animal類裏面沒有這個成員變量。因此,從嚴格來講,這個a眼裏只看到了這個子類對象裏面的父類對象Animal,所以能訪問獲得的也只是這個Animal對象裏面的name屬性,而這個Animal對象外面的furColor屬性是訪問不到的,雖然Dog對象確實有這個屬性存在,但a就是看不到,a門縫裏看Dog——把Dog看扁了,不知道Dog還有furColor這個屬性存在,所以a訪問不了furColor屬性,所以從嚴格意義上來說,a指向的只是這個Dog對象裏面的Animal對象,也就是***箭頭指向的那部分,a就只看到了Dog裏面這部分,而Dog外面的部分都看不到了。這就是父類引用指向子類對象,父類引用指向子類對象的時候,它看到的只是做爲父類的那部分所擁有的屬性和方法,至於做爲子類的那部分它沒有看到。
如真的想訪問Dog對象的furColor屬性,那就採用對象轉型的辦法,把父類對象的引用轉型成子類對象的引用。Dog d1 = (Dog)a;這裏採用的就是對象轉型的辦法,把a強制轉換成一隻Dog對象的引用,而後將這個引用賦值給Dog型的引用變量d1,這樣d1和a都是指向堆內存裏面的Dog對象了,並且d1指向的就是這隻Dog全部的部分了,經過這個d1就能夠訪問Dog對象裏面全部的成員了。
1.2對象轉型實例二
1 public class TestClassCast { 2 3 public void f(Animal a) { 4 System.out.println(a.name); 5 if (a instanceof Cat) { 6 Cat cat = (Cat)a; 7 System.out.println(cat.eyeColor+" eye"); 8 }else if (a instanceof Dog) { 9 Dog dog = (Dog)a; 10 System.out.println(dog.furColor+" fur"); 11 } 12 } 13 14 /** 15 * @param args 16 */ 17 public static void main(String[] args) { 18 Animal a = new Animal("name"); 19 Cat c = new Cat("catname","blue"); 20 Dog d = new Dog("dogname", "black"); 21 TestClassCast testClassCast = new TestClassCast(); 22 testClassCast.f(a); 23 testClassCast.f(c); 24 testClassCast.f(d); 25 } 26 }
上面的代碼是對前面聲明的三個類Animal,Dog,Cat測試的延續,這裏咱們在TestClassCast這裏類裏面測試這個三個類,這裏咱們在TestClassCast類裏面new了一個testClassCast對象,爲的是調用TestClassCast類裏面聲明的f(Animal a)這個方法,這個f()方法裏面的參數類型是Animal類型,若是是Animal類型的參數,那麼咱們能夠把這個Animal類型的子類對象做爲參數傳進去,這是能夠的。如把一隻Dog或者是一隻Cat丟進f()方法裏面這都是能夠的,由於Dog和Cat也是Animal。所以當程序執行到testClassCast.f(a);,testClassCast.f(c);,testClassCast.f(d);的時候,由於f()方法裏面的參數是Animal類型的,因此咱們能夠把一個Animal對象傳進去,除此以外,咱們還能夠直接把從Animal類繼承下來的Dog類和Cat類裏面的Dog對象和Cat對象做爲實參傳遞過去,便是把Animal類型的子類對象做爲參數傳進去。這裏就體現出了繼承和父類對象的引用能夠指向子類對象的好處了,若是說沒有繼承關係的存在,若是說父類的引用不能夠指向子類對象,那麼咱們就得要在Test類裏面定義三個f()方法了,即要定義這樣的f()方法:f(Animal a)、f(Dog d)、f(Cat c)分別用來處理Animal、Dog和Cat,使用三個方法來處理,未來程序的擴展起來就不是簡單的事了,由於面向對象可以幫助咱們這些年來編程苦苦追求的一個境界是可擴展性比較好。可擴展性比較好的一個典型例子就是說當你建好一個建築以後或者是你寫好這個程序以後,把這個主建築給建好了,未來你要加一些其餘的功能的時候,儘可能不要去修改主結構,這叫可擴展性好,你蓋了一座大樓,你如今要在大樓的旁邊添加一個廚房,那你在它旁邊一蓋就好了,若是有人告訴你,我添加一個廚房我須要把你整個大樓的主要柱子都給拆了而後再蓋一遍,這你幹嘛,確定不幹。若是結構設計成這樣,那就是設計得很差,可擴展性很差。因此這裏若是要把f()方法寫成三個重載的f()方法,那麼未來我輸出一隻鳥的時候又得要添加一個f(Bird b)方法來處理鳥。這樣擴展起來就太麻煩了,由於每處理一隻動物都要添加一個新的方法,可是若是存在繼承關係,若是父類對象的引用能夠指向子類對象,那擴展起來就簡單了,你能夠把處理動物的方法寫在一個方法f(Animal a)裏面就夠了,由於全部動物的種類都是從Animal類繼承下來,所以給f()方法傳遞Animal類型的參數的時候能夠直接把這個Animal類的子類對象傳進去,這樣不論是要增長什麼動物的輸出,我均可以調用f(Animal a)方法去處理,這種擴展性比每次都要增長一個新的處理方法的擴展性要好得多,這就是繼承的一個好處,這就是對象轉型對於可擴展性來的好處:「對象的引用能夠指向子類對象」,是由於面向對象的編程裏面存在這樣的繼承關係,使得程序的可擴展性比較好。
對象轉型可使父類對象的引用能夠指向子類對象,給程序帶來了比較好的可擴展性:咱們能夠在一個方法的參數裏面定義父類的引用,而後在實際當中傳的時候傳的是子類的對象,而後咱們再在方法裏面判斷這個傳過來的子類對象到底屬於哪個子類,而後再去執行這個子類裏面的方法或者調用這個子類裏面的成員變量,所以程序的可擴展性要比單獨定義好多個方法好得多得多。不過這個可擴展性尚未達到最好,使用多態就可讓程序的擴展性達到極致。
PS:JAVA交流羣:457036818