[Java] The details of Inheritance

在Java使用繼承時要注意幾個小細節,不然很容易出錯。

  • 建構子的呼叫順序
  • compiler會自動產生無參數的建構子,會自動初始化field變數

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Parent1{
int i;
String s;
/* compiler會自動產生無參數的建構子,
* 並且初始化field變數
* public Parent1() {
* i = 0;
* s = null;
* }
*/
}

class Parent2 extend Parent1{
public Parent2(int i){
//因為沒有指定要呼叫父類別的哪一個建構子,
//compiler會自動呼叫super()
}
}

class Son extend Parent2{
public Son(){
super(1);
}
}

new Son()的時候會呼叫Parent2的建構子(public Parent2(int i)),Parent2的建構子會呼叫Parent1的建構子(public Parent1())。

注意:當沒有寫建構子,compiler會自動產生一個無參數的建構子。在繼承過程中若沒有特別指定要呼叫父類別的哪一個建構子,compiler會自動呼叫super()。

來看一個例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Base {
int i;

public Base() {
add(1);
}

public void add(int j) {
i += j;
}
}

public class Extension extends Base {
int i = 3;

public Extension() {
add(2);
}

public void add(int j) {
i += j * 2;
}

public static void main(String[] args) {
exec(new Extension());
}

private static void exec(Extension base) {
base.add(8);
System.out.println(base.i);
}
}

會印出什麼樣的結果呢?

分析:

  1. new Extension()的時候會呼叫Base的建構子(public Base()),呼叫add(2)。
  2. 因為Extension有覆寫(override)Base的add function,所以Base建構子所呼叫的add(1)是呼叫Extension中的add function。
  3. 因為多型的關係,當Base建構子呼叫add(1),裡面的i是Extension的i,並不是Base的i。
  4. 當Base建構子呼叫add(1)完畢,此時Extension的i為2。
  5. 接著呼叫Extension的建構子,此時會先初始化field變數,所以i會被重設為3。
  6. 之後是呼叫add(2),此時i為7。Extension的初始化完畢。
  7. 呼叫add(8)時,可以得出i = 7 +16 = 23,所以印出23。

假如將

1
private static void exec(Extension base)

改成

1
private static void exec(Base base)

結果還是一樣嗎?
結果是0,因為此時的i指的是Base的i。