標籤:對比 ++ 版本 www end std 水平 load 操作
繼承的最大好處就是為了實現代碼的複用。那麼,子類到底從父類得到的什麼呢?
執行個體成員
父類的private成員不會被子類繼承,子類不能訪問。但是子類對象的確包含父類的私人成員。父類的 包訪問成員 繼承為子類的包訪問成員。就好像他們直接定義在子類中一樣。父類的 protected 成員繼承為子類的protected 成員。就好像他們直接定義在子類中一樣。父類的 public 成員繼承為子類的public 成員,就好像他們直接定義在子類中一樣。
執行個體方法
繼承到的執行個體方法在子類中可以直接被使用,還需重點理解是方法的重寫和重載。
重寫override
一個繼承鏈中,父類的方法對於子類來說具有相同的語義,但是不同的細節操作,因此子類需要override父類的這個方法以滿足自己的需求。
注意的點:
1、方法名,參數表一定和父類中的相同,傳回型別相同,或者是子類。
1、存取權限一定不低於父類的執行個體方法
2、拋出的異常一定是父類方法拋出的異常相同,或者子類。
如果拿C++和java對比,那麼java中的執行個體方法預設都是virtual的(java中沒有virtual這個key word),因此在java中,子類可以直接重寫父類方法的任何非final執行個體方法,但是在C++中,除非父類使用virtual標記一個方法為虛方法,子類才可以override這個方法。
對於重寫的方法,javac是不能確定的具體要調用那個類的方法,而是產生特殊的位元組碼讓jvm去動態決定什麼方法。這個就是所謂的前期綁定和後期綁定的差異。
public class Test{ public static void main(String [] args){ Object o = new SubClass(); o.toString(); }}class SubClass extends Object{ public String toString() //重寫Object 的toString方法 { return "SubClass"; }}
Compiled from "Test.java"public class Test { public Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class SubClass 3: dup 4: invokespecial #3 // Method SubClass."<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4 // Method java/lang/Object.toString:()Ljava/lang/String; 12: pop 13: return}
其中3處紅色標記 的代碼,重要的區別就是invokespecial 和 invokevirtual :invokespecial 代表前期綁定,在編譯期就能決定調用什麼方法,由javac確定調用什麼方法。invokevirtual 則是方法的後期綁定,由JVM決定調用什麼方法。
第一處是Test類的建構函式調用父類Object的建構函式,編譯期確定,他是前期綁定。
第二處是因為new 了一個SubClass對象,調用SubClass的建構函式,編譯期確定,他也是前期綁定的。
第三處是因為我們的 Object o 引用了重寫了toString方法的SubClass對象,javac不能知道具體調用Object中的toString,還是SubClass中的toString,於是產生特殊代碼讓JVM去決定。
修飾為 static 、 final 、private 的方法一定是前期綁定,因為他們根本都不存在override。
JVM需要在運行時動態決定調用那個版本的方法,這個過程對JVM來說就是 virtual method lookup(虛方法尋找)。JVM將會從實際對象所屬的類 和 他的最近的父類中尋找。如果自己定義了,則調用自己的版本,如果沒有則調用父類的版本。
重載overload
重載的定義:函數重載是指在同一範圍內,可以有一組具有相同函數名,不同參數列表的函數,這組函數被稱為重載函數。
注意:重載與否,不考慮函數的傳回型別。C++也是如此。也就是說,2個函數的傳回型別同不同都不影響他們能不能形成重載,只要他們函數名相同,參數表不同就滿足重載。
Java是預設是支援跨類重載的。但是C++就預設不支援在子類中重載父類的執行個體方法。那也就是說:java中的執行個體方法會自動匯入到子類的範圍中,而C++則不是。
public class Test{ public static void main(String[] args) { Derive d = new Derive(); d.print("hello", 2); d.print("world");
/* output
hello
hello
world
world
world
world
world
*/
}}class Base{ public void print(String msg) { for(int i=0;i<5;++i) { System.out.println(msg); } }}class Derive extends Base{ public void print(String msg,int times) //在子類中重載父類方法 { for(int i=0;i<times;++i) { System.out.println(msg); } } }
對於C++
#include<iostream>#include<string>using namespace std;class Base{ public : void print(const string& msg) const { for(int i=0;i<5;++i) { std::cout<<msg<<std::endl; } }};class Derive:public Base{ public : /* *需要使用using Base::print將父類中的版本引如到子類的範圍中,這樣才能形成跨類重載,否則子類在使用一個參數版本的print函數時,會出現以下編譯錯誤: * * error: no matching function for call to ‘Derive::print(const char [6])’ *意思是編譯器在Derive類中找不到print(const char [6])版本的函數 * */ using Base::print; void print(const string& msg,const int times) const { for(int i=0;i<times;++i) { std::cout<<msg<<std::endl; } }};int main(){ Derive d ; d.print("hello",2); d.print("world");}View Code
執行個體欄位
執行個體欄位沒有什麼要說的,要說的就是執行個體欄位的隱藏了:在子類中定義一個和父類同名的欄位,那麼子類中的名稱將會隱藏父類中的同名欄位。
幾乎沒有人使用這個技術,如果用到了,那麼說明代碼設計有問題(bad code)。
class Base{ protected int i = 100;}class Derive extends Base{ private int i = 1000; //隱藏了父類欄位 i public void foo() { System.out.println(i); //代表this.i System.out.println(this.i); System.out.println(super.i); //使用父類被隱藏的i } }
也可以使用cast,這是最終極的手段。因為super只能引用直接父類的成員,而不能引用父類的父類的成員。但使用cast可以做到。
class A{ protected int i = 100;}class B extends A{ protected int i = 1000;}class C extends B{ private int i = 10000; public void foo() { System.out.println("this.i:"+this.i); //10000 System.out.println("B.this.i:"+ ((B)this).i ); //1000 System.out.println("A.this.i:"+ ((A)this).i ); //100 }}
static成員
static會被子類繼承嗎?答案是會。他們會被繼承為子類的static成員,而不是子類執行個體的成員。
同樣,private static成員不會被繼承,只有 包訪問 許可權 ,protected public 成員才會被繼承。
父類的private成員不會被子類繼承,子類不能訪問。父類的 包訪問成員 繼承為子類的包訪問成員。就好像他們直接定義在子類中一樣。父類的 protected 成員繼承為子類的protected 成員。就好像他們直接定義在子類中一樣。父類的 public 成員繼承為子類的public 成員,就好像他們直接定義在子類中一樣。
static 成員同樣也可以使用執行個體成員的存取修飾詞 public ,包訪問,protected , priavcte。
static方法
static方法不能被override,只能被隱藏。
static欄位
和執行個體欄位一樣,static欄位也可以被隱藏。如果要引用被隱藏的父類static欄位,則需要顯式的通過父類的類名來使用。隱藏static欄位通常也最好不要使用。
建構函式
建構函式不能繼承,但是子類一定可以(也必須)借用父類的建構函式。java保證:除了Object類對象,每一個類的執行個體在構造時,先去調用父類的建構函式。
我們自訂類的建構函式的第一句一定是super(xx,...),如果不是,那麼第一句就一定是this(xx,...)去調用本類的另一個建構函式。
如果子類建構函式不顯式的調用super(),那麼,javac會自動插入super(),也就是父類無參數的建構函式。
對於建構函式,其實類中所有建構函式都是隱式static的。很明顯的例證就是 建構函式無需通過執行個體就可以調用。
歡迎轉載,請註明出處:www.cnblogs.com/lulipro
為了獲得更好的閱讀體驗,請訪問原部落格地址。
限於本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。
代碼鋼琴家
java中類繼承,到底繼承了什嗎?