java中類繼承,到底繼承了什嗎?

來源:互聯網
上載者:User

標籤:對比   ++   版本   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中類繼承,到底繼承了什嗎?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.