1. 程式人生 > >java繼承向上轉型和向下轉型和動態繫結

java繼承向上轉型和向下轉型和動態繫結

1 概念: 把引用變數轉化為子類型別,則成為向下轉型。如果把引用變數轉化為父類型別,則成為向上轉型。 

Java程式碼
  1. publicclass Base {   
  2. /**  
  3.      * 父類例項變數  
  4.      */
  5.     String var = "baseVar";   
  6. /**  
  7.      * 父類的靜態變數  
  8.      */
  9. static String staticVar = "staticBaseVar";   
  10. /**  
  11.      * 父類例項方法  
  12.      */
  13. void method() {   
  14.         System.out.println("Base method"
    );   
  15.     }   
  16. /**  
  17.      * 父類靜態方法  
  18.      */
  19. staticvoid staticMethod() {   
  20.         System.out.println("Base static Method");   
  21.     }   
  22. }   
  23. publicclass Sub extends Base {   
  24. /**  
  25.      * 子類的例項變數  
  26.      */
  27.     String var = "subVar";   
  28. /**  
  29.      * 子類的靜態變數  
  30.      */
  31. static String staticVar = "staticSubVar";   
  32. // 覆蓋父類的method()方法
  33. void method() {   
  34.         System.out.println("Sub static Method");   
  35.     }   
  36.     String subVar = "var only belonging to Sub";   
  37. void subMethod() {   
  38.         System.out.println("Method only belonging to Sub");   
  39.     }   
  40. publicstaticvoid main(String args[]) {   
  41. // who 被宣告為Base型別,引用Sub例項
  42.         Base who = 
    new Sub();   
  43.         System.out.println("who.var=" + who.var);// print:who.var=baseVar
  44.         System.out.println("who.staticVar=" + who.staticVar);// print:who.staticVar=staticBaseVar
  45.         who.method();// print:Sub static Metho
  46. // 這裡為什麼不列印Base method呢 這是java動態機制的表現,
  47. // 雖然who的型別是Base 但是 實際引用的是Sub類 new Sub()會在堆區分配記憶體空間
  48. // 當who.method()方法時,jvm會根據who持有的引用定位到堆區的Sub例項
  49. // 再根據Sub持有的引用 定位到方法區Sub類的型別資訊 獲得method的位元組
  50. // 在當前環境下(上面程式碼所示)獲得method的位元組碼,此時Sub類複寫了Base的method的方法,
  51. // 獲得method的位元組碼,直接執行method包含的指令,
  52. // 如果沒有複寫method方法 則去獲得Base類的位元組碼 執行包含的指令(這個機制實現有待去研究有關資料)
  53.         who.staticMethod();// print:Base static Method
  54. // who.subVar="123";//編譯錯誤
  55. // who.subMethod();//編譯錯誤
  56. // 對於一個引用變數,java編譯器按照它什麼的型別來處理,這裡who 的型別是Base型別的引用變數.不存在subVar
  57. // 和subMethod方法
  58. // 如果要訪問Sub類成員,可以進行強制型別轉換(向下轉型)
  59.         Sub sub = (Sub) who;   
  60.         sub.subVar = "23";   
  61.         sub.subMethod();   
  62.         Base base2 = new Base();   
  63.         Sub sub2 = (Sub) base2;   
  64.         sub2.subMethod();   
  65. // 編譯通過 但是丟擲ClassCastException
  66. // sub2實際引用的是Base例項
  67. // 對應一個引用型別的變數,執行時jvm按照它實際引用的物件來處理,假設上面能夠通過,但是
  68. // 當我們sub2引用變數呼叫subMethod()方法時,我們看到在Base類中並沒有subMethod方法。
  69. // 由此可見 ,子類物件可以向上轉型為父類物件,但是父類物件不能轉換為子類物件,父類擁有的成員子類
  70. // 子類肯定也有,而子類擁有的成員父類不一定有。上面就是一個例子。
  71. // 在執行時環境中,通過引用型別變數來訪問所引用的方法和屬性時,java虛擬機器採用如下繫結機制。
  72. // 1 例項方法與引用變數 實際引用的物件 的方法繫結 屬於動態繫結.由執行時jvm動態決定的。
  73. // 2 靜態方法與引用變了所宣告的物件 的方法繫結 屬於靜態繫結 在編譯階段就已經做了繫結
  74. // 3 成員變數 (靜態和例項)與引用變數所宣告的型別的成員變數繫結屬於靜態繫結。
  75.     }   
  76. }  
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程式碼
  1. abstractclass A {   
  2. abstractvoid method();   
  3. void test() {   
  4.         method();// 這裡呼叫哪個類的method方法呢
  5.     }   
  6. }   
  7. publicclass B extends A {   
  8. @Override
  9. void method() {   
  10.         System.out.println("B method");   
  11.     }   
  12. publicstaticvoid main(String[] args) {   
  13. new B().test(); // print:B method
  14. // 方法test()在父類A中定義,它呼叫了方法method
  15. // 但是method在A中是抽象的 但是仍然可以呼叫
  16. // 因為在執行時環境中jvm會執行B的例項的method方法
  17. // 一個例項所屬的類肯定是實現了父類中所有的抽象方法
  18.     }   
  19. }  
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程式碼
  1. class A {   
  2. void method() {   
  3.         System.out.println("A method");   
  4.     };   
  5. void test() {   
  6.         method();// 這裡呼叫哪個類的method方法呢
  7.     }   
  8. }   
  9. publicclass B extends A {   
  10. @Override
  11. void method() {   
  12.         System.out.println("B method");   
  13.     }   
  14. publicstaticvoid main(String[] args) {   
  15. //new B().test(); // print:B method
  16. // 方法test()在父類A中定義,它呼叫了方法method
  17. // 但是method在A中是抽象的 但是仍然可以呼叫
  18. // 因為在執行時環境中jvm會執行B的例項的method方法
  19. // 一個例項所屬的類肯定是實現了父類中所有的抽象方法
  20. new A().test();   
  21. new B().test();   
  22. //test()方法在A類中定義,它呼叫了method()方法,和上面的例子的區別是父類A的method方法
  23. //不是抽象的,但是通過new B().test()執行的仍然是子類B的method方法,由此可見
  24. //在執行時環境中,當通過B類的例項去呼叫一系列的例項方法(包括一個方法呼叫另外一個方法)
  25. //將優先和B類本身包含的例項方法動態繫結,如果沒有這個例項方法,才會從父類A中繼承來的
  26. //例項方法動態繫結。 
  27.     }   
  28. }