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

 

繼承的最大好處就是爲了實現代碼的複用。那麼,子類到底從父類獲得的什麼呢?java

 

實例成員

 

父類的private成員不會被子類繼承,子類不能訪問。可是子類對象的確包含父類的私有成員。
父類的 包訪問成員 繼承爲子類的包訪問成員。就好像他們直接定義在子類中同樣。
父類的 protected 成員繼承爲子類的protected 成員。就好像他們直接定義在子類中同樣。
父類的 public 成員繼承爲子類的public 成員,就好像他們直接定義在子類中同樣。

 

實例方法ios

 

繼承到的實例方法在子類中能夠直接被使用,還需重點理解是方法的重寫和重載。jvm

 

重寫overrideide

一個繼承鏈中,父類的方法對於子類來講具備相同的語義,可是不一樣的細節操做,所以子類須要override父類的這個方法以知足本身的需求。函數

注意的點:this

一、方法名,參數表必定和父類中的相同,返回類型相同,或者是子類。spa

一、訪問權限必定不低於父類的實例方法設計

二、拋出的異常必定是父類方法拋出的異常相同,或者子類。code

 

 

若是拿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

 爲了得到更好的閱讀體驗,請訪問原博客地址。

限於本人水平,若是文章和代碼有表述不當之處,還請不吝賜教。

代碼鋼琴家

相關文章
相關標籤/搜索