1. 程式人生 > 實用技巧 >Java 基礎之繼承

Java 基礎之繼承

訪問許可權

Java中,可以使用訪問控制符來保護對類、變數、方法和構造方法的訪問。Java 支援 4 種不同的訪問許可權。

  • default (即預設,什麼也不寫): 在同一包內可見,不使用任何修飾符。使用物件:類、介面、變數、方法。
  • private : 在同一類內可見。使用物件:變數、方法。 注意:不能修飾類(外部類)
  • public : 對所有類可見。使用物件:類、介面、變數、方法
  • protected : 對同一包內的類和所有子類可見。使用物件:變數、方法。 注意:不能修飾類(外部類)。

請注意以下方法繼承的規則:

  • 父類中宣告為 public 的方法在子類中也必須為 public。
  • 父類中宣告為 protected 的方法在子類中要麼宣告為 protected,要麼宣告為 public,不能宣告為 private。
  • 父類中宣告為 private 的方法,不能夠被繼承。

抽象類與介面

1. 抽象類

抽象類和抽象方法都使用 abstract 關鍵字進行宣告。如果一個類中包含抽象方法,那麼這個類必須宣告為抽象類。
抽象類和普通類最大的區別是,抽象類不能被例項化,只能被繼承。

public abstract class AbstractClassDemo {

    protected int x;
    private int y;

    public abstract void func1();

    public void func2() {
        System.out.println("func2");
    }
}
public class AbstractExtendClassDemo extends AbstractClassDemo {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}
// AbstractClassDemo ac1 = new AbstractClassDemo(); // 'AbstractClassDemo' is abstract; cannot be instantiated
AbstractClassDemo ac2 = new AbstractExtendClassDemo();
ac2.func1();

2. 介面

介面是抽象類的延伸,在 Java 8 之前,它可以看成是一個完全抽象的類,也就是說它不能有任何的方法實現。

從 Java 8 開始,介面也可以擁有預設的方法實現,這是因為不支援預設方法的介面的維護成本太高了。在 Java 8 之前,如果一個介面想要新增新的方法,那麼要修改所有實現了該介面的類,讓它們都實現新增的方法。

介面的成員(欄位 + 方法)預設都是 public 的,並且不允許定義為 private 或者 protected。

介面的欄位預設都是 static 和 final 的。

public interface InterfaceExample {

    void func1();

    default void func2(){
        System.out.println("func2");
    }

    int x = 123;
    // int y;               // Variable 'y' might not have been initialized
    public int z = 0;       // Modifier 'public' is redundant for interface fields
    // private int k = 0;   // Modifier 'private' not allowed here
    // protected int l = 0; // Modifier 'protected' not allowed here
    // private void fun3(); // Modifier 'private' not allowed here
}
public class InterfaceImplementExample implements InterfaceExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}
// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);

3. 比較

  • 從設計層面上看,抽象類提供了一種 IS-A 關係,需要滿足裡式替換原則,即子類物件必須能夠替換掉所有父類物件。而介面更像是一種 LIKE-A 關係,它只是提供一種方法實現契約,並不要求介面和實現介面的類具有 IS-A 關係。
  • 從使用上來看,一個類可以實現多個介面,但是不能繼承多個抽象類。
  • 介面的欄位只能是 static 和 final 型別的,而抽象類的欄位沒有這種限制。
  • 介面的成員只能是 public 的,而抽象類的成員可以有多種訪問許可權。

4. 使用選擇

使用介面:

  • 需要讓不相關的類都實現一個方法,例如不相關的類都可以實現 Comparable 介面中的 compareTo() 方法;
  • 需要使用多重繼承。

使用抽象類:

  • 需要在幾個相關的類中共享程式碼。
  • 需要能控制繼承來的成員的訪問許可權,而不是都為 public。
  • 需要繼承非靜態和非常量欄位。

在很多情況下,介面優先於抽象類。因為介面沒有抽象類嚴格的類層次結構要求,可以靈活地為一個類新增行為。並且從 Java 8 開始,介面也可以有預設的方法實現,使得修改介面的成本也變的很低。

super

  • 訪問父類的建構函式:可以使用 super() 函式訪問父類的建構函式,從而委託父類完成一些初始化的工作。應該注意到,子類一定會呼叫父類的建構函式來完成初始化工作,一般是呼叫父類的預設建構函式,如果子類需要呼叫父類其它建構函式,那麼就可以使用 super() 函式。
  • 訪問父類的成員:如果子類重寫了父類的某個方法,可以通過使用 super 關鍵字來引用父類的方法實現。
public class SuperExample {

    protected int x;
    protected int y;

    public SuperExample(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void func() {
        System.out.println("SuperExample.func()");
    }
}
public class SuperExtendExample extends SuperExample {

    private int z;

    public SuperExtendExample(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public void func() {
        super.func();
        System.out.println("SuperExtendExample.func()");
    }
}
SuperExample e = new SuperExtendExample(1, 2, 3);
e.func();

SuperExample.func()
SuperExtendExample.func()

重寫與過載

1. 重寫(Override)

存在於繼承體系中,指子類實現了一個與父類在方法宣告上完全相同的一個方法。
為了滿足裡式替換原則,重寫有以下三個限制:

  • 子類方法的訪問許可權必須大於等於父類方法;
  • 子類方法的返回型別必須是父類方法返回型別或為其子型別。
  • 子類方法丟擲的異常型別必須是父類丟擲異常型別或為其子型別。

重寫方法呼叫的優先順序如下:

  • this.func(this)
  • super.func(this)
  • this.func(super)
  • super.func(super)

2. 過載(Overload)

在同一個類中,指一個方法與已存在的方法名稱相同,但是引數型別、個數、順序至少一個不同。
注意:返回值不同,其他都相同不算數過載。