公眾號偶然看到的一個帖子,構造方法,類方法,final方法,哪些能覆蓋,哪些能重載,初學時也是被這些術語搞的很迷糊
現在有時間了對這些做一個總結。全是自己的語言,可能不是很全面,表達意思應該夠清楚
一、叫法
- 構造方法
又叫構造器,構造函數。通常有無參構造器和帶參數的構造器2種,每個類都有一個構造方法(如果沒有顯式的給出來,
那么也有一個默認的無參構造器)無返回類型修飾符。訪問修飾符可以是public,也可以是private,比如常見的單例模式就要求構
造函數私有化。
- 類方法
static修飾符修飾的方法。因為static修飾的方法是屬於類的而不是屬於實例的(這個描述各種書籍上非常常見),因此不必
去new 一個實例來調用,而是直接類名.方法名來調用這種方法,因此也稱為類方法。
- final方法
最終的、不可改變的、終極的方法。怎么叫都行,單詞修飾很清楚了,用了final表明設計上不再會去修改他,很巧,一
個叫abstract的修飾符就是要設計者去實現去重寫的,因此可以知道final和abstract永遠不能共存。
- 覆蓋
又叫重寫,Override。多態是java的特性,覆蓋是表現多態特性的具體做法。《Effective Java》有一個章節特別提出了,如
果是覆蓋方法,請一定記得加上@Override,在接下來的代碼描述中你會看到,很多種方法混雜在一起的時候看起來是多么的難受
- 重載
重載只發生在一個類中,記住這點很重要,這也是跟覆蓋這個概念撇清關系的最重要一點。重載要求同一個類中方法名相同
而參數列表不同。參數列表,就是入參的個數,類型,順序。抓住定義中的這兩點,其他的通過什么返回值,訪問權限,異常來重
載一個方法那就是扯淡,混淆,不行。
二、行還是不行?
建議直接拷貝類到你的idea中去理解!重要的提示都加了注釋
首先看下重載。最經典最常見的莫過於這兩個例子:構造方法的重載和System.out.println()這個方法了。我們直接看代碼:
public abstract class ClassTest { private int a; /** * abstract 和 final不能共存,報錯 */ abstract final void test(); /** * 構造方法 */ public ClassTest() { System.out.println("調用父類無參構造方法"); } /** * 構造方法重載為帶參的構造器 */ public ClassTest(int a) { this.a = a; System.out.println("調用重載的帶參構造方法"); System.out.println(this.a); } /** * 類方法(靜態方法) */ public static void staticMethod() { System.out.println("調用父類static方法(類方法)"); } /** * 類方法可以被重載 */ public static void staticMethod(String s) { System.out.println("調用重載的類方法,s=" + s); } /** * final方法 */ public final void finalMethod() { System.out.println("調用父類final方法"); } /** * final方法可以被重載 */ public final void finalMethod(String s) { System.out.println("調用重載的final方法,s=" + s); } }
寫一個客戶端調用一下:
public class Client {
public static void main(String[] args) {
ClassTest.staticMethod();
ClassTest.staticMethod("hello");
}
}
我們得出如下結論:構造方法,類方法,final方法均可以被重載
接着我們看下覆蓋的情況:
還是用之前的定義的類,不過我們加入了一些別的情況:父類private的final方法和private的方法
public class ClassTest {
/**
* 構造方法
*/
public ClassTest() {
System.out.println("調用父類無參構造方法");
}
/**
* 類方法(靜態方法)
*/
public static void staticMethod() {
System.out.println("調用父類static方法(類方法)");
}
/**
* final方法
*/
private final void finalMethod0() {
System.out.println("調用父類private final方法");
}
/**
* final方法
*/
public final void finalMethod() {
System.out.println("調用父類public final方法");
}
/**
* 普通的私有方法
*/
private void privateMethod() {
System.out.println("調用父類public final方法");
}
}
子類是:
public class SubClassTest extends ClassTest {
/**
* 報錯提示:父類構造器不可覆蓋
*/
@Override
public ClassTest() {
System.out.println("覆蓋父類構造方法");
}
/**
* 報錯提示:父類的靜態方法不能覆蓋
*/
@Override
public static void staticMethod() {
System.out.println("覆蓋父類靜態方法");
}
/**
* 上面的@Override去掉,不再報錯
* 說明這個方法只是和父類的靜態方法同名了而已,他是子類獨有的,與父類的沒半毛錢關系
* 如果決定覆蓋,請敲上@Override,一般做項目也不會像我這樣測這些奇怪的情況,所以不加也知道是覆蓋了,但是還是請加上
*/
public static void staticMethod() {
System.out.println("子類的同名方法");
}
/**
* 報錯提示:final方法不能被覆蓋
*/
// @Override
// public final void finalMethod() {
// System.out.println("覆蓋final方法");
// }
/**
* 未加@Override
* 錯誤提示:final方法不能覆蓋
* 說明子類的final方法連名字都不能跟父類的同名,否則認為是覆蓋
*/
public final void finalMethod() {
System.out.println("覆蓋final方法");
}
/**
* 未加@Override
* 父類中該方法是private final的
* 子類可以擁有對應的public權限的final同名方法
*/
public final void finalMethod0() {
System.out.println("調用子類同名的final方法");
}
/**
* 錯誤提示:父類私有方法不能被覆蓋
*/
@Override
public void privateMethod() {
System.out.println("調用普通的private方法");
}
}
編譯結果我在注釋里都寫了,我們可以得出:構造方法,類方法,final方法都不能覆蓋
注意:
1、子類可以擁有和父類同名的static方法,參看上面的staticMethod方法的注釋,這個方法和父類的那個沒半毛錢關系
2、父類的public final方法,子類不能覆蓋,也不能有自己的同樣的方法(這就是我為啥,effective java也,強調加上@Override的原因)
3、父類的private final方法,子類可以有對應的public final的,但是也和父類對應的這個方法沒半毛錢關系,只是語法上允許
注意:
以上的類直接復制在開發工具上會報錯的,這也是我們想要的
三、為什么不行?從意義和設計上窺探下
static方法為什么不能被覆蓋,我們試着從意義上理解下,static是類的不是實例的,意味着它是無狀態的,而覆蓋發生在多態中,也就是
每個實例各自去show,從java運行角度講,static的方法在編譯時就綁定完了,而多態要在運行時才確定,該調用誰覆蓋后的方法。
final方法為什么不能覆蓋,設計final關鍵字的意義是,開發者知道這個方法一旦完成,就不打算再修改它,子類如果需要,可以直接調用
如果各個子類也去覆蓋這個final方法,那父類的這個方法再用final修飾就沒啥意義了
最后private修飾的方法會被隱形的指定為final的,所以也不能覆蓋,這個可以在Thinking in Java中找到
以上是個人一些小總結,不到之處,還請指正~~
