java繼承向上轉型和向下轉型和動態繫結
阿新 • • 發佈:2019-02-10
1 概念: 把引用變數轉化為子類型別,則成為向下轉型。如果把引用變數轉化為父類型別,則成為向上轉型。
Java程式碼- publicclass Base {
- /**
- * 父類例項變數
- */
- String var = "baseVar";
- /**
- * 父類的靜態變數
- */
- static String staticVar = "staticBaseVar";
- /**
- * 父類例項方法
- */
- void method() {
- System.out.println("Base method"
- }
- /**
- * 父類靜態方法
- */
- staticvoid staticMethod() {
- System.out.println("Base static Method");
- }
- }
- publicclass Sub extends Base {
- /**
- * 子類的例項變數
- */
- String var = "subVar";
- /**
- * 子類的靜態變數
- */
- static String staticVar = "staticSubVar";
- // 覆蓋父類的method()方法
- void method() {
- System.out.println("Sub static Method");
- }
- String subVar = "var only belonging to Sub";
- void subMethod() {
- System.out.println("Method only belonging to Sub");
- }
- publicstaticvoid main(String args[]) {
- // who 被宣告為Base型別,引用Sub例項
- Base who =
- System.out.println("who.var=" + who.var);// print:who.var=baseVar
- System.out.println("who.staticVar=" + who.staticVar);// print:who.staticVar=staticBaseVar
- who.method();// print:Sub static Metho
- // 這裡為什麼不列印Base method呢 這是java動態機制的表現,
- // 雖然who的型別是Base 但是 實際引用的是Sub類 new Sub()會在堆區分配記憶體空間
- // 當who.method()方法時,jvm會根據who持有的引用定位到堆區的Sub例項
- // 再根據Sub持有的引用 定位到方法區Sub類的型別資訊 獲得method的位元組
- // 在當前環境下(上面程式碼所示)獲得method的位元組碼,此時Sub類複寫了Base的method的方法,
- // 獲得method的位元組碼,直接執行method包含的指令,
- // 如果沒有複寫method方法 則去獲得Base類的位元組碼 執行包含的指令(這個機制實現有待去研究有關資料)
- who.staticMethod();// print:Base static Method
- // who.subVar="123";//編譯錯誤
- // who.subMethod();//編譯錯誤
- // 對於一個引用變數,java編譯器按照它什麼的型別來處理,這裡who 的型別是Base型別的引用變數.不存在subVar
- // 和subMethod方法
- // 如果要訪問Sub類成員,可以進行強制型別轉換(向下轉型)
- Sub sub = (Sub) who;
- sub.subVar = "23";
- sub.subMethod();
- Base base2 = new Base();
- Sub sub2 = (Sub) base2;
- sub2.subMethod();
- // 編譯通過 但是丟擲ClassCastException
- // sub2實際引用的是Base例項
- // 對應一個引用型別的變數,執行時jvm按照它實際引用的物件來處理,假設上面能夠通過,但是
- // 當我們sub2引用變數呼叫subMethod()方法時,我們看到在Base類中並沒有subMethod方法。
- // 由此可見 ,子類物件可以向上轉型為父類物件,但是父類物件不能轉換為子類物件,父類擁有的成員子類
- // 子類肯定也有,而子類擁有的成員父類不一定有。上面就是一個例子。
- // 在執行時環境中,通過引用型別變數來訪問所引用的方法和屬性時,java虛擬機器採用如下繫結機制。
- // 1 例項方法與引用變數 實際引用的物件 的方法繫結 屬於動態繫結.由執行時jvm動態決定的。
- // 2 靜態方法與引用變了所宣告的物件 的方法繫結 屬於靜態繫結 在編譯階段就已經做了繫結
- // 3 成員變數 (靜態和例項)與引用變數所宣告的型別的成員變數繫結屬於靜態繫結。
- }
- }
public class Base {
/**
* 父類例項變數
*/
String var = "baseVar";
/**
* 父類的靜態變數
*/
static String staticVar = "staticBaseVar";
/**
* 父類例項方法
*/
void method() {
System.out.println("Base method");
}
/**
* 父類靜態方法
*/
static void staticMethod() {
System.out.println("Base static Method");
}
}
public class Sub extends Base {
/**
* 子類的例項變數
*/
String var = "subVar";
/**
* 子類的靜態變數
*/
static String staticVar = "staticSubVar";
// 覆蓋父類的method()方法
void method() {
System.out.println("Sub static Method");
}
String subVar = "var only belonging to Sub";
void subMethod() {
System.out.println("Method only belonging to Sub");
}
public static void main(String args[]) {
// who 被宣告為Base型別,引用Sub例項
Base who = new Sub();
System.out.println("who.var=" + who.var);// print:who.var=baseVar
System.out.println("who.staticVar=" + who.staticVar);// print:who.staticVar=staticBaseVar
who.method();// print:Sub static Metho
// 這裡為什麼不列印Base method呢 這是java動態機制的表現,
// 雖然who的型別是Base 但是 實際引用的是Sub類 new Sub()會在堆區分配記憶體空間
// 當who.method()方法時,jvm會根據who持有的引用定位到堆區的Sub例項
// 再根據Sub持有的引用 定位到方法區Sub類的型別資訊 獲得method的位元組
// 在當前環境下(上面程式碼所示)獲得method的位元組碼,此時Sub類複寫了Base的method的方法,
// 獲得method的位元組碼,直接執行method包含的指令,
// 如果沒有複寫method方法 則去獲得Base類的位元組碼 執行包含的指令(這個機制實現有待去研究有關資料)
who.staticMethod();// print:Base static Method
// who.subVar="123";//編譯錯誤
// who.subMethod();//編譯錯誤
// 對於一個引用變數,java編譯器按照它什麼的型別來處理,這裡who 的型別是Base型別的引用變數.不存在subVar
// 和subMethod方法
// 如果要訪問Sub類成員,可以進行強制型別轉換(向下轉型)
Sub sub = (Sub) who;
sub.subVar = "23";
sub.subMethod();
Base base2 = new Base();
Sub sub2 = (Sub) base2;
sub2.subMethod();
// 編譯通過 但是丟擲ClassCastException
// sub2實際引用的是Base例項
// 對應一個引用型別的變數,執行時jvm按照它實際引用的物件來處理,假設上面能夠通過,但是
// 當我們sub2引用變數呼叫subMethod()方法時,我們看到在Base類中並沒有subMethod方法。
// 由此可見 ,子類物件可以向上轉型為父類物件,但是父類物件不能轉換為子類物件,父類擁有的成員子類
// 子類肯定也有,而子類擁有的成員父類不一定有。上面就是一個例子。
// 在執行時環境中,通過引用型別變數來訪問所引用的方法和屬性時,java虛擬機器採用如下繫結機制。
// 1 例項方法與引用變數 實際引用的物件 的方法繫結 屬於動態繫結.由執行時jvm動態決定的。
// 2 靜態方法與引用變了所宣告的物件 的方法繫結 屬於靜態繫結 在編譯階段就已經做了繫結
// 3 成員變數 (靜態和例項)與引用變數所宣告的型別的成員變數繫結屬於靜態繫結。
}
}
Java程式碼
- abstractclass A {
- abstractvoid method();
- void test() {
- method();// 這裡呼叫哪個類的method方法呢
- }
- }
- publicclass B extends A {
- @Override
- void method() {
- System.out.println("B method");
- }
- publicstaticvoid main(String[] args) {
- new B().test(); // print:B method
- // 方法test()在父類A中定義,它呼叫了方法method
- // 但是method在A中是抽象的 但是仍然可以呼叫
- // 因為在執行時環境中jvm會執行B的例項的method方法
- // 一個例項所屬的類肯定是實現了父類中所有的抽象方法
- }
- }
abstract class A {
abstract void method();
void test() {
method();// 這裡呼叫哪個類的method方法呢
}
}
public class B extends A {
@Override
void method() {
System.out.println("B method");
}
public static void main(String[] args) {
new B().test(); // print:B method
// 方法test()在父類A中定義,它呼叫了方法method
// 但是method在A中是抽象的 但是仍然可以呼叫
// 因為在執行時環境中jvm會執行B的例項的method方法
// 一個例項所屬的類肯定是實現了父類中所有的抽象方法
}
}
Java程式碼
- class A {
- void method() {
- System.out.println("A method");
- };
- void test() {
- method();// 這裡呼叫哪個類的method方法呢
- }
- }
- publicclass B extends A {
- @Override
- void method() {
- System.out.println("B method");
- }
- publicstaticvoid main(String[] args) {
- //new B().test(); // print:B method
- // 方法test()在父類A中定義,它呼叫了方法method
- // 但是method在A中是抽象的 但是仍然可以呼叫
- // 因為在執行時環境中jvm會執行B的例項的method方法
- // 一個例項所屬的類肯定是實現了父類中所有的抽象方法
- new A().test();
- new B().test();
- //test()方法在A類中定義,它呼叫了method()方法,和上面的例子的區別是父類A的method方法
- //不是抽象的,但是通過new B().test()執行的仍然是子類B的method方法,由此可見
- //在執行時環境中,當通過B類的例項去呼叫一系列的例項方法(包括一個方法呼叫另外一個方法)
- //將優先和B類本身包含的例項方法動態繫結,如果沒有這個例項方法,才會從父類A中繼承來的
- //例項方法動態繫結。
- }
- }