Java入門系列之重寫

前言

關於全部Java系列文章面向有必定基礎的童鞋,所寫每一篇但願有必定含金量,有些內容可能會從Java整個語法全局考慮穿插後續要講解的內容以成系統,若不理解,請看完後再學習。上一節咱們講解完了final關鍵字,本節咱們繼續來對比講解Java和C#中的重寫,兩者語言的重寫區分很是清晰,Java子類中基類方法簽名同樣或經過註解@Override顯式聲明,C#中基類經過virtual關鍵字修飾,子類經過ovveride關鍵字表示重寫,具體細節請往下看。ide

重寫

既然是重寫必然就涉及到繼承,咱們首先來看看Java中的重寫是怎樣的呢?以下:學習

public class Main {
    public void f() {
        System.out.println("Main.f");
    }

    public static void main(String[] args) {
        Main main = new Sub();
        main.f();
    }
}

class Sub extends Main {
    public void f() {
        System.out.println("Sub.f");
    }
}

當調用基類的f方法時,此時發現已被子類所重寫,因此如上正常打印出Sub.f,要是咱們將上述基類中方法修改成私有的呢spa

可能咱們期待輸出子類中的打印結果,可是修改成私有後,說明此時對子類再也不可見,也就至關於使用了final,在這種狀況下,子類中方法則是一個全新的方法,很顯然說明:只有非私有方法才能夠被重寫。對於這種狀況下編譯不會報錯, 可是也不會按照咱們所指望的結果來執行,因此建議對於基類中的私有方法命名爲和子類不一樣的名字,爲了讓重寫一目瞭然或更加明確,在1.5版本發佈了註解功能,咱們能夠經過註解來顯式聲明要重寫基類方法,若基類爲私有,此時經過註解則會編譯報錯,由於找不到要重寫的方法,這種體驗更加友好,好比以下:code

public class Main {
    private void f() {
        System.out.println("Main.f");
    }

    public static void main(String[] args) {
        Main main = new Sub();
        main.f();
    }
}

class Sub extends Main {

    //編譯錯誤,未找到基類(超類)中要重寫的方法
    @Override
    public void f() {
        System.out.println("Sub.f");
    }
}

觸類旁通,咱們來思考一個問題,是否是方法簽名一致,子類就能夠重寫基類方法呢?很顯然不是,如果靜態方法,必然不能被重寫,若是經過註解@Override聲明那麼必然編譯報錯,不然調用基類方法,對於接口中的靜態方法同理。因此仍是建議使用註解來聲明重寫。那麼爲何經過註解顯式聲明重寫基類方法或經過關鍵字final修飾方法就會在編譯階段報錯呢?由於它們在編譯階段就完成了靜態綁定,而不是運行時動態綁定。問題又來了,上述咱們講解到若在子類中不經過註解顯式聲明重寫,同時在基類中方法私有,此時必定能夠編譯經過(上述已演示),而且會調用基類方法並打印出結果,事實狀況必定是這樣嗎?很顯然也不是如此,以下示例:blog

class Super {
    private void f() {
        System.out.println("Super.f");
    }
}

class Derived extends Super {
    public void f() {
        System.out.println("Derived.f");
    }
    public static void main(String[] args) {
        Super aSuper = new Derived();
        
        //編譯報錯(由於基類方法私有)
        aSuper.f();
    }
}

初一看,這不是同樣麼,實際上是不同,上述能夠那是由於調用方在基類裏面,固然能夠調用內部的私有方法,如上狀況只對基類內部私有, 固然也就不能調用,這裏就又引出一個問題,是否是聲明爲基類的私有方法,子類就沒法進行重寫呢?根據咱們上述打印的結果來看,理論上不可行,事實狀況是能夠的,經過內部類(後續會出文章詳細講解)來實現。繼承

class Main {

    private void f() {
        System.out.println("Main.f");
    }

    class Inner extends Main {
        private void f() {
            System.out.println("Inner.f");
        }
    }

    public static void main(String args[]) {

        //內部類實例必須經過外部類實例建立
        Main outer = new Main();
        Inner inner = outer.new Inner();

        //內部類能夠在內部訪問外部的全部成員(包括私有)
        inner.f();

        // 調用外部類方法
        outer = inner;
        outer.f();
    }
}

C#若明確須要重寫,那麼基類方法聲明爲虛有的virtual,子類經過ovverride關鍵字修飾方法達到重寫目的,若沒有這兩個關鍵字,和Java中同樣只是方法簽名一致,那麼說明編譯器會提醒是否經過new關鍵字來代表隱藏基類的方法接口

class Program
{
    public void F()
    {
        Console.WriteLine("Main.f");
    }

    static void Main(string[] args)
    {
        Program program = new Sub();
        program.F();

        Console.ReadKey();
    }
}

class Sub : Program
{
    public new void F()
    {
        Console.WriteLine("Sub.f");
    }
}

總結

Java和C#中的重寫區分度很是清晰,Java中只要方法簽名一致就能夠達到重寫,不過建議經過註解方法來顯式聲明重寫以避免引發沒必要要的問題,同時,即便基類方法私有,咱們也可藉助於內部類來實現重寫。編譯器

相關文章
相關標籤/搜索