1. 程式人生 > >java基礎:父類與子類之間變數和方法的呼叫

java基礎:父類與子類之間變數和方法的呼叫

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處理成員變數和成員方法的繼承時是有區別的。