父類與子類之間變量 方法的調用

1)父類構造函數

java中當調用某個類的構造方法的時候,系統總會調用父類的非靜態初始化塊進行初始化,這個調用是隱式的,而且父類的靜態初始化代碼

塊總是會被執行,接着調用父類的一個或者多個構造器執行初始化,這個調用也可以通過super進行顯式調用。

例如:

父類代碼如下:

public class Creature {//父類

{//非靜態代碼塊

System.out.println("creature的非靜態代碼塊正在執行");

}

 

public Creature(){

System.out.println("creature的構造函數正在執行");

}

}

子類代碼如下:

public class Animal extends Creature {

{

System.out.println("animal的初始化代碼塊正在執行");

}

public Animal(){

System.out.println("animal的構造方法正在執行");

}

 

public static void main(String[] args){

Animal a = new Animal()  ;

}

}

則運行程序後的結果爲:

creature的非靜態代碼塊正在執行

creature的構造函數正在執行

animal的初始化代碼塊正在執行

animal的構造方法正在執行

從結果中可以看出:調用某個類的構造方法的時候總是會先執行父類的非靜態代碼塊,然後執行父類的構造方法,最後纔是執行當前類的

非靜態代碼塊和構造方法。執行過程中有先後順序。

若果想要顯式調用父類的構造方法則可以使用super(),來調用,但是super關鍵字和this關鍵字都必須放在構造放的第一行,而且只能使

用一個,爲什麼要放在第一行呢?因爲如果不放在第一行則先調用子類的初始化代碼,再調用父類的初始化代碼,則父類中的初始化後的值

會覆蓋子類中的初始化的值。

注:super用於顯式調用父類的構造器,this可以顯式調用本類中的重載的構造器。

 

 

2)訪問子類對象的實例變量

子類的方法可以訪問父類中的實例變量,這是因爲子類繼承父類就會獲得父類中的成員變量和方法,但是父類方法不能訪問子類的實例變量

,因爲父類根本無法知道它將被哪個類繼承,它的子類將會增加怎麼樣的成員變量。但是,在極端的情況下,父類也可以訪問子類中的變量。

例如:

父類代碼如下:

public class Base {//父類

private int i = 2 ;

public Base(){

this.display() ;

}

public void display(){

System.out.println(i);

}

}

子類中代碼如下:

public class Derived extends Base {

private int i = 22 ;

public Derived(){

i = 222 ;

}

public void display(){

System.out.println(i);

}

}

測試用例如下:

public class Test {

public static void main(String[] args) {

new Derived() ;

}

}

程序的執行結果爲:

0

也許你會感到奇怪,爲什麼不是2?22?222?怎麼會是0呢,下面我們看一下程序的執行過程。當我們調用new Derived() ;創建Derived

實例的時候,系統會爲Derived對象分配內存空間,Derived會有兩個i實例變量,會分配兩個空間來保存i的值。分配完空間以後i的值爲0

,如果有引用類型則引用類型的值爲null。接下來程序在執行Derived的構造器之前會執行Base的構造器,表面上看Base的構造器中只有

一行代碼,但是在父類中定義i的時候執行的初始值2,因此經過編譯之後,該構造方法中應該包含如下兩行代碼:

i =2 ;

this.display() ;

程序先將Base中的i賦值爲2,然後執行display方法。此處有一個關鍵字this,this到底代表誰呢?表面上看this代表的是Base的當前實例,

但是實際上代碼是放在Derived的構造器中的,所以this最終代表的是Derived的當前實例(編譯類型是Base而實際引用一個Derived對象),

所以如果在父類的構造方法中直接輸出System.out.println(this.i) ;則輸出的結果爲2。但是調用this.display()方法,此時調用的是

子類中重寫的display方法,輸出的變量i也是子類中的i,但是此時子類中的變量i還沒有賦值,所以輸出結果爲0。

爲了詳細的看清楚this變量到底代表什麼實例,我們將Base的構造方法修改如下:

public Base(){

System.out.println(this.i);

System.out.println(this.getClass());

this.display() ;

}

再次運行程序,結果爲:

2

class edu.qichao.chapter2.Derived

0

可以看到this代表的是Derived的實例,但是編譯的時候類型爲Base,所以輸出this.i的值爲2。

 

 

3)調用被子類重寫的方法

默認情況下,子類可以調用父類的方法,但是父類不能調用子類的方法,因爲父類不知道它將被哪個子類繼承,也不知道子類將增加怎麼

樣的方法。

例如:

父類Animal的代碼如下:

public class Animal {

private String desc ;

 

public Animal(){

this.desc = getDesc() ;

}

 

public String getDesc(){

return "Animal" ;

}

 

public String toString(){

return desc ;

}

}

子類Wolf的代碼如下:

public class Wolf extends Animal {

private String name ;

private double weight ;

public Wolf(String name , double weight){

this.name = name ;

this.weight = weight ;

}

public String getDesc(){

return "Wolf[name=" + name + ",weight=" + weight + "]" ;

}

 

public static void main(String[] args){

System.out.println(new Wolf("灰太狼" , 3));

}

}

程序的運行結果爲:

Wolf[name=null,weight=0.0]

現在我們分析一下程序執行的過程。在main方法中通過new Wolf("灰太狼" , 3);來創建一個Wolf的實例,子類會隱式調用父類的構造方

法,在父類構造方法中初始化desc變量this.desc = getDesc() ;此處需要注意this變量,雖然這個this放在Animal的構造放中,但是是在

Wolf的構造方法中調用父類的構造方法,所以this編譯時類型爲Animal,運行時類型爲Wolf,此處調用的getDesc方法是子類Wolf的方法,

但是子類中的name和weight變量都沒有初始化,默認爲null和0.0.所以程序的最終結果爲:Wolf[name=null,weight=0.0]

 

 

4)繼承成員變量和成員方法的區別

java中隊成員變量的繼承和成員方法的繼承是不同的。

例如:

父類代碼如下:

public class Base {

int count = 2 ;

public void display(){

System.out.println(this.count);

}

}

子類代碼如下:

public class Derived extends Base {

int count = 20 ;

@Override

public void display(){

System.out.println(this.count);

}

}

測試用例如下:

public class Test {

public static void main(String[] args) {

Base b = new Base() ;

System.out.println(b.count);

b.display() ;

System.out.println("-----------------");

Derived d = new Derived() ;

System.out.println(d.count);

d.display() ;

System.out.println("-----------------");

Base bd = new Derived() ;

System.out.println(bd.count);

bd.display() ;

System.out.println("-----------------");

Base d2b = d ;

System.out.println(d2b.count);

}

}

程序運行結果爲:

2

2

-----------------

20

20

-----------------

2

20

-----------------

2

在上面的程序中,不管是d變量、還是bd變量、還是都d2b變量。只要他們指向一個Derived對象,則不管他們聲明時用了什麼類型,當通過

這些變量調用方法時,方法的行爲總是表現出他們的實際類型的行爲,但是如果通過這些變量來訪問他們所指向對象的實例變量的時候,

這些實例變量的值總是表現出聲明這些變量所用類型的行爲。由此可見,java處理成員變量和成員方法的繼承時是有區別的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章