1. 程式人生 > >方法能重寫,屬效能重寫嗎?

方法能重寫,屬效能重寫嗎?

覆寫是多型的一種表現,我們平時所說的覆寫一般是針對方式來說,在網上看到過有人討論試著覆寫屬性,於是有點興趣,屬性真能覆寫嗎?回答問題之前,我們還是回憶一下方法的覆寫具備哪些條件,或都說哪些方法能覆寫。
先回顧一下方法覆寫要注意的地方:
1、過載(也叫過載)時只與方法特徵有關,但重寫(覆寫)是會進一步檢查兩個方法的返回型別是否相同、訪問修飾許可權是否縮小(假設public->protected方向是縮小,反之叫擴大)和丟擲的異常範圍是否擴大。那麼什麼是方式特徵呢?一個方法的特徵(也可叫方法簽名)僅包括方法的名字、引數的個數、型別、順序(實質上就是引數列表),而不包括方法的返回型別、訪問修飾許可權與所丟擲的異常。

Java程式碼 複製程式碼
  1. publicclass A {   
  2. publicvoid overwrite(int i) throws IOException {}   
  3. }   
  4. class B extends A {   
  5. // !! 編譯通不過,不能縮小訪問許可權
  6. //    void overwrite(int i) throws IOException {}
  7. // !! 編譯通不過,不能擴大異常範圍
  8. //    public void overwrite(int i) throws Exception {}
  9. // 正常,編譯沒問題,可以不丟擲異常
  10. // public void overwrite(int i) {}
  11. // 覆寫父類方法
  12. publicvoid overwrite(int i) throws IOException {}   
  13. protectedvoid overload(int i) {}   
  14. //過載上面的方法
  15. int overload(long i) throws IOException {   
  16. return0;   
  17.     }   
  18. }  
public class A {
    public void overwrite(int i) throws IOException {}
}

class B extends A {
    // !! 編譯通不過,不能縮小訪問許可權
    //    void overwrite(int i) throws IOException {}

    // !! 編譯通不過,不能擴大異常範圍
    //    public void overwrite(int i) throws Exception {}

    // 正常,編譯沒問題,可以不丟擲異常
    // public void overwrite(int i) {}

    // 覆寫父類方法
    public void overwrite(int i) throws IOException {}

    protected void overload(int i) {}

    //過載上面的方法
    int overload(long i) throws IOException {
        return 0;
    }
}

另外,要補充說明的是過載一般是指在同一個類中多個方法間,但也可重父類的的方法,而重寫只發生面父子與子類的方法間。

2、非私有非靜態方法不能被任何靜態方法覆寫,如果子類中試著以靜態方式(不管訪問許可權修飾符是什麼)來覆寫父類的方法,編譯時會報錯。

Java程式碼 複製程式碼
  1. publicclass A {   
  2. void f() {}   
  3. }   
  4. class B extends A {   
  5. // !! 編譯出錯,下面錯誤都一樣
  6. //    static void f(){};
  7. //    private static void f(){};
  8. //    protected static void f(){};
  9. //    public static void f(){};
  10. }  
public class A {
    void f() {}
}

class B extends A {
    // !! 編譯出錯,下面錯誤都一樣
    //    static void f(){};
    //    private static void f(){};
    //    protected static void f(){};
    //    public static void f(){};
}

3、非私有靜態方法不能被任何非靜態方法覆寫,如果子類中試著以非靜態方式(不管訪問許可權修飾符是什麼)來覆寫父類的方法,編譯時會報錯。

Java程式碼 複製程式碼
  1. publicclass A {   
  2. publicstaticvoid f() {}   
  3. }   
  4. class B extends A {   
  5. // !! 編譯出錯,下面錯誤都一樣
  6. //    public void f(){}
  7. //    protected void f() {}
  8. //    void f() {}
  9. //    private void f() {}
  10. }  
public class A {
    public static void f() {}
}

class B extends A {
    // !! 編譯出錯,下面錯誤都一樣
    //    public void f(){}
    //    protected void f() {}
    //    void f() {}
    //    private void f() {}
}

4、子類與父類中有方法特徵相同的靜態方法時,覆寫規則與非靜態方法覆寫規則一樣,但一般我們不把靜態方法的覆寫叫覆寫,雖然語法規則上與非靜態方法相同。

Java程式碼 複製程式碼
  1. publicclass A {   
  2. publicstaticvoid overwrite(int i) throws IOException {   
  3.         System.out.println("父類靜態方法...");   
  4.     }   
  5. }   
  6. class B extends A {   
  7. // !! 編譯通不過,不能縮小訪問許可權
  8. //    static void overwrite(int i) throws IOException {}
  9. // !! 編譯通不過,不能擴大異常範圍
  10. //    public static void overwrite(int i) throws Exception {}
  11. // 正常,編譯沒問題,可以不丟擲異常
  12. // public static void overwrite(int i) {}
  13. // 覆寫父類靜態方法
  14. publicstaticvoid overwrite(int i) throws IOException {   
  15.         System.out.println("子類靜態方法...");   
  16.     }   
  17. publicstaticvoid main(String[] args) throws IOException {   
  18.         A a = new B();   
  19. //覆寫靜態在語法上是合法的,但沒有多型效果
  20.         a.overwrite(0);//print: 父類靜態方法...
  21.         A.overwrite(0);//print: 父類靜態方法...
  22.         B.overwrite(0);//print: 子類靜態方法...
  23.     }   
  24. }  
public class A {
    public static void overwrite(int i) throws IOException {
        System.out.println("父類靜態方法...");
    }
}

class B extends A {
    // !! 編譯通不過,不能縮小訪問許可權
    //    static void overwrite(int i) throws IOException {}

    // !! 編譯通不過,不能擴大異常範圍
    //    public static void overwrite(int i) throws Exception {}

    // 正常,編譯沒問題,可以不丟擲異常
    // public static void overwrite(int i) {}

    // 覆寫父類靜態方法
    public static void overwrite(int i) throws IOException {
        System.out.println("子類靜態方法...");
    }

    public static void main(String[] args) throws IOException {
        A a = new B();
        //覆寫靜態在語法上是合法的,但沒有多型效果
        a.overwrite(0);//print: 父類靜態方法...
        A.overwrite(0);//print: 父類靜態方法...
        B.overwrite(0);//print: 子類靜態方法...
    }
}

上面重寫了父類的靜態方法,但卻沒有多型作用,也就是說,靜態方法不存在重寫這一說,只不過在語法規則上與非靜態方法一樣罷了。

5、私有方法對子類同名方法不產生任何影響,也就是說私有方法不能被重寫,即使試著在子類覆寫了父類的私有方法,不管訪問許可權修飾符是什麼,在編譯時也不會報錯。原因就是私有方法對子類也是不可見的。

Java程式碼 複製程式碼
  1. publicclass A {   
  2. privatevoid f() {   
  3.     }   
  4. // 加上static修飾符,其結果都一樣
  5. // private static void f() {}
  6. }   
  7. class B extends A {   
  8. // 嘗試每個方式,都會編譯能過
  9. // private void f() {}
  10. // void f() {}
  11. // protected void f() {}
  12. // public void f() {}
  13. // private static void f() {}
  14. // static void f() {}
  15. // protected static void f() {}
  16. publicstaticvoid f() {}   
  17. }  
public class A {
    private void f() {
    }

    // 加上static修飾符,其結果都一樣
    // private static void f() {}
}

class B extends A {
    // 嘗試每個方式,都會編譯能過
    // private void f() {}
    // void f() {}
    // protected void f() {}
    // public void f() {}

    // private static void f() {}
    // static void f() {}
    // protected static void f() {}
    public static void f() {}
}

好了,上面總結並理解方法覆後,我們再來看屬效能不能覆寫,其實,這是一個很無聊的問題,不像上面討論方法那有點價值,可以說屬性是沒有覆寫這一說的,這有點像靜態方法一樣,不過還是稍微看一下:

Java程式碼 複製程式碼
  1. publicclass A {   
  2. // 這行不能去掉,否則①編譯通不過
  3. int i = 1;   
  4.     A() {   
  5.         System.out.println("父類構造方法 i=" + this.i);//①
  6. this.f();//②
  7.         System.out.println("父類中 " + (this == getThis()));//③ print:true
  8.     }   
  9. publicvoid f() {   
  10.         System.out.println("父類方法f() i=" + this.i);   
  11.     }   
  12. public A getThis() {   
  13.         System.out.println("父類getThis");   
  14. returnthis;   
  15.     }   
  16. }   
  17. class B extends A {   
  18. int i = 2;   
  19.     B() {   
  20.         System.out.println("子類構造方法 i=" + this.i);   
  21.         System.out.println("子類中 " + (this == super.getThis()));//④
  22.     }   
  23. publicvoid f() {   
  24.         System.out.println("子類方法f() i=" + this.i);   
  25.     }   
  26. public A getThis() {   
  27.         System.out.println("子類getThis");   
  28. returnthis;   
  29.     }   
  30. publicstaticvoid main(String[] args) {   
  31. /*  
  32.          * 從下面 a.i 與 b.i 的輸出可以看出,某物件的屬性只與宣告 的型別相  
  33.          * 關,與物件本身是什麼型別無關,這與方法是完全不同的  
  34.          */
  35.         A a = new B();   
  36.         System.out.println("main方法 a.i=" + a.i);   
  37.         B b = (B) a;   
  38.         System.out.println("main方法 b.i=" + b.i);   
  39. /*  
  40.          * output:  
  41.         父類構造方法 i=1  
  42.         子類方法f() i=0  
  43.         子類getThis  
  44.         父類中 true  
  45.         子類構造方法 i=2  
  46.         父類getThis  
  47.         子類中 true  
  48.         main方法 a.i=1  
  49.         main方法 b.i=2  
  50.          */
  51.     }   
  52. }  
public class A {
    // 這行不能去掉,否則①編譯通不過
    int i = 1;

    A() {
        System.out.println("父類構造方法 i=" + this.i);//①
        this.f();//②
        System.out.println("父類中 " + (this == getThis()));//③ print:true
    }

    public void f() {
        System.out.println("父類方法f() i=" + this.i);
    }

    public A getThis() {
        System.out.println("父類getThis");
        return this;
    }
}

class B extends A {
    int i = 2;

    B() {
        System.out.println("子類構造方法 i=" + this.i);
        System.out.println("子類中 " + (this == super.getThis()));//④
    }

    public void f() {
        System.out.println("子類方法f() i=" + this.i);
    }

    public A getThis() {
        System.out.println("子類getThis");
        return this;
    }

    public static void main(String[] args) {
        /*
         * 從下面 a.i 與 b.i 的輸出可以看出,某物件的屬性只與宣告 的型別相
         * 關,與物件本身是什麼型別無關,這與方法是完全不同的
         */
        A a = new B();
        System.out.println("main方法 a.i=" + a.i);
        B b = (B) a;
        System.out.println("main方法 b.i=" + b.i);
       
        /*
         * output:
        父類構造方法 i=1
        子類方法f() i=0
        子類getThis
        父類中 true
        子類構造方法 i=2
        父類getThis
        子類中 true
        main方法 a.i=1
        main方法 b.i=2
         */
    }
}

這裡一直有個疑問:上面①②處的this肯定都是同一個物件,且都指向子類物件,還並且與子類中this是同一象,由第③④行程式碼可以證明這一點。但為什麼①處的this好像指向了父類物件,不然為什麼①處輸出的i是父類中的i呢?這說明屬性除了只與定義他的型別有關外,如果在類的內部自身訪問時,就會訪問當前所在類的屬性,與this無關,這進一步說明了屬性是不具有多型這一特性的。