方法能重寫,屬效能重寫嗎?
覆寫是多型的一種表現,我們平時所說的覆寫一般是針對方式來說,在網上看到過有人討論試著覆寫屬性,於是有點興趣,屬性真能覆寫嗎?回答問題之前,我們還是回憶一下方法的覆寫具備哪些條件,或都說哪些方法能覆寫。
先回顧一下方法覆寫要注意的地方:
1、過載(也叫過載)時只與方法特徵有關,但重寫(覆寫)是會進一步檢查兩個方法的返回型別是否相同、訪問修飾許可權是否縮小(假設public->protected方向是縮小,反之叫擴大)和丟擲的異常範圍是否擴大。那麼什麼是方式特徵呢?一個方法的特徵(也可叫方法簽名)僅包括方法的名字、引數的個數、型別、順序(實質上就是引數列表),而不包括方法的返回型別、訪問修飾許可權與所丟擲的異常。
- publicclass A {
- publicvoid 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) {}
- // 覆寫父類方法
- publicvoid overwrite(int i) throws IOException {}
- protectedvoid overload(int i) {}
- //過載上面的方法
- int overload(long i) throws IOException {
- return0;
- }
- }
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、非私有非靜態方法不能被任何靜態方法覆寫,如果子類中試著以靜態方式(不管訪問許可權修飾符是什麼)來覆寫父類的方法,編譯時會報錯。
- publicclass A {
- void f() {}
- }
- class B extends A {
- // !! 編譯出錯,下面錯誤都一樣
- // static void f(){};
- // private static void f(){};
- // protected static void f(){};
- // public static void f(){};
- }
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、非私有靜態方法不能被任何非靜態方法覆寫,如果子類中試著以非靜態方式(不管訪問許可權修飾符是什麼)來覆寫父類的方法,編譯時會報錯。
- publicclass A {
- publicstaticvoid f() {}
- }
- class B extends A {
- // !! 編譯出錯,下面錯誤都一樣
- // public void f(){}
- // protected void f() {}
- // void f() {}
- // private void f() {}
- }
public class A {
public static void f() {}
}
class B extends A {
// !! 編譯出錯,下面錯誤都一樣
// public void f(){}
// protected void f() {}
// void f() {}
// private void f() {}
}
4、子類與父類中有方法特徵相同的靜態方法時,覆寫規則與非靜態方法覆寫規則一樣,但一般我們不把靜態方法的覆寫叫覆寫,雖然語法規則上與非靜態方法相同。
- publicclass A {
- publicstaticvoid 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) {}
- // 覆寫父類靜態方法
- publicstaticvoid overwrite(int i) throws IOException {
- System.out.println("子類靜態方法...");
- }
- publicstaticvoid main(String[] args) throws IOException {
- A a = new B();
- //覆寫靜態在語法上是合法的,但沒有多型效果
- a.overwrite(0);//print: 父類靜態方法...
- A.overwrite(0);//print: 父類靜態方法...
- B.overwrite(0);//print: 子類靜態方法...
- }
- }
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、私有方法對子類同名方法不產生任何影響,也就是說私有方法不能被重寫,即使試著在子類覆寫了父類的私有方法,不管訪問許可權修飾符是什麼,在編譯時也不會報錯。原因就是私有方法對子類也是不可見的。
- publicclass A {
- privatevoid 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() {}
- publicstaticvoid f() {}
- }
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() {}
}
好了,上面總結並理解方法覆後,我們再來看屬效能不能覆寫,其實,這是一個很無聊的問題,不像上面討論方法那有點價值,可以說屬性是沒有覆寫這一說的,這有點像靜態方法一樣,不過還是稍微看一下:
- publicclass A {
- // 這行不能去掉,否則①編譯通不過
- int i = 1;
- A() {
- System.out.println("父類構造方法 i=" + this.i);//①
- this.f();//②
- System.out.println("父類中 " + (this == getThis()));//③ print:true
- }
- publicvoid f() {
- System.out.println("父類方法f() i=" + this.i);
- }
- public A getThis() {
- System.out.println("父類getThis");
- returnthis;
- }
- }
- class B extends A {
- int i = 2;
- B() {
- System.out.println("子類構造方法 i=" + this.i);
- System.out.println("子類中 " + (this == super.getThis()));//④
- }
- publicvoid f() {
- System.out.println("子類方法f() i=" + this.i);
- }
- public A getThis() {
- System.out.println("子類getThis");
- returnthis;
- }
- publicstaticvoid 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
- */
- }
- }
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無關,這進一步說明了屬性是不具有多型這一特性的。