本文目錄一覽:
- 1、JAVA中關於繼承,上轉型下轉型的問題。
- 2、java中關於向上轉型的問題
- 3、JAVA里向上轉型有什麼意義?為什麼會需要向上轉型?它和多態有什麼關係?
- 4、java中的向上轉型和向下轉型問題。為什麼一步到位的向下轉型會報錯,而分兩步的向下轉型卻沒錯
- 5、java多態向上轉型向下轉型的問題
- 6、JAVA中對象向上轉型的問題
JAVA中關於繼承,上轉型下轉型的問題。
一個引用類型變數如果聲明為父類的類型,但實際引用的是子類對象,那麼該變數就不能再訪問子類中添加的屬性和方法這句話是有問題的。
1。該變數可以訪問子類的方法,不過方法必須是子類重寫的父類的方法,並且編譯的時候調用的是父類的方法,而在運行的時候就是訪問的子類重寫的方法。一句話:因為多態。
2。該變數不能再訪問子類中的屬性。因為多態是針對重寫的方法的,不是覆蓋的屬性。
關於多態的理解
比如A a = new B();是創建了一個子類對象並把它當成父類對象A用
也就是父類引用指向子類對象
此時,引用變數a有2個類型,編譯時的類型為A,運行時的類型為B.在代碼編譯過程中,a 只能調用屬於A的方法. 不能調用B類裡面的方法.注意,由於繼承關係,如果B重寫了A的某個方法,比如說eat(),而在代碼編譯過程中,a.eat()調用的是A的eat(),但在程序運行時,卻運行的是B的eat(). 這就是多態
比如Animal a = new Tiger(); Tiger繼承並重寫了Animal的eat()方法.這也是父類引用指向子類對象.首先, a 是 一隻老虎.但不幸的是,Animal a= new Tiger(); 也就是說a雖然有了老虎的實質, 就是說有了老虎的爪子,身材….. , 但卻沒有老虎的名分.它雖然身體是老虎, 但名字或者說它的類別卻是動物,而不是老虎.而作為動物的定義,你當然不能使用屬於老虎的定義的方法.比如說,雖然二者都有吃的行為, 但老虎吃肉,動物都吃肉么? 所以雖然a實質上是老虎,但在書面描述a的行為時,你只能使用動物的定義. 這個階段就相當於代碼的編譯的階段.而此時a的類型為編譯時的類型-動物.而如果具體實施吃的行為時, 比如說給a喂吃的, 雖然書面定義了a只能有動物的籠統的吃的方法,比如說用嘴,沒有規定要吃肉.但是現在是具體操作了,由於a實質上是老虎,所以a實質上履行的還是老虎的吃的方法. 具體的吃的過程,就相當於程序的運行的階段. 而此時a的類型為運行時的類型-老虎
java中關於向上轉型的問題
RTTI機制
向上轉型的目的仍然是抽象。
比如
水果有顏色、味道。不同的水果有不同的顏色和味道。那麼當
水果
sg
=
new
橘子();
水果
sg1
=
new
蘋果();
sg.color();sg.taste();
sg1.color();sg1.taste();
sg,sg1都是水果實例的引用。但會調用不同的具體子類的方法。
其目地都是抽象。通過訪問父類或介面的方法(公共)達到一種對具體實現的封裝。對於介面的使用者來說,不需要關心實現細節。提高內聚,降低不必要的耦合。
再多說一句:注意轉型時帶來的
內存切片。
JAVA里向上轉型有什麼意義?為什麼會需要向上轉型?它和多態有什麼關係?
問題的由來:
首先是方法的參數是父類對象,傳入子類對象是否可行
然後引出Parent p = new Children();
這句代碼不是很理解,google的過程中引出向上轉型
要理解向上轉型又引出了動態綁定
從動態綁定又引出了靜態綁定
程序綁定的概念:
綁定指的是一個方法的調用與方法所在的類(方法主體)關聯起來。對java來說,綁定分為靜態綁定和動態綁定;或者叫做前期綁定和後期綁定
靜態綁定:
在程序執行前方法已經被綁定,此時由編譯器或其它連接程序實現。例如:C。
針對java簡單的可以理解為程序編譯期的綁定;這裡特別說明一點,java當中的方法只有final,static,private和構造方法是前期綁定
動態綁定:
後期綁定:在運行時根據具體對象的類型進行綁定。
若一種語言實現了後期綁定,同時必須提供一些機制,可在運行期間判斷對象的類型,並分別調用適當的方法。也就是說,編譯器此時依然不知道對象的類型,但方法調用機制能自己去調查,找到正確的方法主體。不同的語言對後期綁定的實現方法是有所區別的。但我們至少可以這樣認為:它們都要在對象中安插某些特殊類型的信息。
動態綁定的過程:
虛擬機提取對象的實際類型的方法表;
虛擬機搜索方法簽名;
調用方法。
關於綁定相關的總結:
在了解了三者的概念之後,很明顯我們發現java屬於後期綁定。在java中,幾乎所有的方法都是後期綁定的,在運行時動態綁定方法屬於子類還是基類。但是也有特殊,針對static方法和final方法由於不能被繼承,因此在編譯時就可以確定他們的值,他們是屬於前期綁定的。特別說明的一點是,private聲明的方法和成員變數不能被子類繼承,所有的private方法都被隱式的指定為final的(由此我們也可以知道:將方法聲明為final類型的一是為了防止方法被覆蓋,二是為了有效的關閉java中的動態綁定)。java中的後期綁定是有JVM來實現的,我們不用去顯式的聲明它,而C++則不同,必須明確的聲明某個方法具備後期綁定。
java當中的向上轉型或者說多態是藉助於動態綁定實現的,所以理解了動態綁定,也就搞定了向上轉型和多態。
前面已經說了對於java當中的方法而言,除了final,static,private和構造方法是前期綁定外,其他的方法全部為動態綁定。而動態綁定的典型發生在父類和子類的轉換聲明之下:
比如:Parent p = new Children();
其具體過程細節如下:
1:編譯器檢查對象的聲明類型和方法名。假設我們調用x.f(args)方法,並且x已經被聲明為C類的對象,那麼編譯器會列舉出C類中所有的名稱為f的方法和從C類的超類繼承過來的f方法
2:接下來編譯器檢查方法調用中提供的參數類型。如果在所有名稱為f 的方法中有一個參數類型和調用提供的參數類型最為匹配,那麼就調用這個方法,這個過程叫做「重載解析」
3:當程序運行並且使用動態綁定調用方法時,虛擬機必須調用同x所指向的對象的實際類型相匹配的方法版本。假設實際類型為D(C的子類),如果D類定義了f(String)那麼該方法被調用,否則就在D的超類中搜尋方法f(String),依次類推
上面是理論,下面看幾個示例(示例來自網路):
Java代碼
view plaincopy to clipboardprint?
public class Father {
public void method() {
System.out.println(“父類方法,對象類型:” + this.getClass());
}
}
public class Son extends Father {
public static void main(String[] args) {
Father sample = new Son();//向上轉型
sample.method();
}
}
聲明的是父類的引用,但是執行的過程中調用的是子類的對象,程序首先尋找子類對象的method方法,但是沒有找到,於是向上轉型去父類尋找
Java代碼
public class Son extends Father {
public void method() {
System.out.println(“子類方法,對象類型:” + this.getClass());
}
public static void main(String[] args) {
Father sample = new Son();//向上轉型
sample.method();
}
}
由於子類重寫了父類的method方法,根據上面的理論知道會去調用子類的method方法去執行,因為子類對象有method方法而沒有向上轉型去尋找
前面的理論當中已經提到了java的綁定規則,由此可知,在處理java類中的成員變數時,並不是採用運行時綁定,而是一般意義上的靜態綁定。所以在向上轉型的情況下,對象的方法可以找到子類,而對象的屬性還是父類的屬性。
代碼如下:
Java代碼
public class Father {
protected String name=”父親屬性”;
public void method() {
System.out.println(“父類方法,對象類型:” + this.getClass());
}
}
public class Son extends Father {
protected String name=”兒子屬性”;
public void method() {
System.out.println(“子類方法,對象類型:” + this.getClass());
}
public static void main(String[] args) {
Father sample = new Son();//向上轉型
System.out.println(“調用的成員:”+sample.name);
}
}
結論,調用的成員為父親的屬性。
這個結果表明,子類的對象(由父類的引用handle)調用到的是父類的成員變數。所以必須明確,運行時(動態)綁定針對的範疇只是對象的方法。
現在試圖調用子類的成員變數name,該怎麼做?最簡單的辦法是將該成員變數封裝成方法getter形式。
代碼如下:
Java代碼
public class Father {
protected String name = “父親屬性”;
public String getName() {
return name;
}
public void method() {
System.out.println(“父類方法,對象類型:” + this.getClass());
}
}
public class Son extends Father {
protected String name=”兒子屬性”;
public String getName() {
return name;
}
public void method() {
System.out.println(“子類方法,對象類型:” + this.getClass());
}
public static void main(String[] args) {
Father sample = new Son();//向上轉型
System.out.println(“調用的成員:”+sample.getName());
}
}
結果:調用的是兒子的屬性
java中的向上轉型和向下轉型問題。為什麼一步到位的向下轉型會報錯,而分兩步的向下轉型卻沒錯
情況1: aa保存的是一個BB類型的實例,在編譯過程中引用為AA類型,數據存儲過程中含有全部BB類的方法和變數;bb只是把aa從AA類型的引用重新定義為BB類型的引用,由於aa中包含BB類的全部方法和變數,所以轉型沒有問題
情況2: new AA() 中可能不包含BB類的全部變數和方法,強制轉換無法實現
思考方法:JAVA中的變數名看成數據指針,new 是在內存中實際的創造一個實例。當創造了一個子類的實例時,指針類型是父類或子類都沒有關係。但父類的實例不可能用子類的指針來表達。
java多態向上轉型向下轉型的問題
引用a的類型是Animal,是Cat的父類。所以是「父類引用指向子類對象」。如果是「子類引用指向父類對象」,那應該寫成 Cat a = new Animal();但這顯然是不和邏輯的。
你說的沒錯——「向上轉型後,父類也只能調用父類已經有的方法」。但是子類如果覆蓋了父類的方法,那麼即使向上轉型,方法的行為也表現為覆蓋後的行為。這也是多態的一種體現。向上轉型更多的用來體現一種編程上的「約定」。所有繼承某個類或者實現某個介面的類,一定包含某個方法,這樣在調用的時候,使得你也不必關係具體的實現細節,只要知道「這個類型中的某個方法能幫我完成工作」就可以了。
向下轉型,是「引用的類型」的變化,不是對象實例類型的變化。new什麼,就是什麼。不會因為向上或者向下轉型而改變自己的類型。
最後一個問題,不管向上還是向下轉型,調用的都是實際類型中的那個方法。比如,Animal a = new Cat(); a.叫()。那這個叫就是Cat類型實例中的「叫」方法。
JAVA中對象向上轉型的問題
子類繼承父類,會繼承父類的屬性。如果屬性名稱相同,在子類中訪問父類的屬性,可以通過super來指定。如果使用父類的引用,直接訪問的是super的屬性,而不是子類的屬性。
原創文章,作者:簡單一點,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/127757.html