Java中的窘!境!!!

最近講到了Java的封裝、繼承和多態,現在總結一下需要注意的和遇到的問題

  • **繼承:**Java類只能單繼承(多繼承可以通過接口)。在子類的構造方法中,需要首先初始化父類(即將super(…)放在第一行)。

  • 多態:個人覺得多態是很難理解的,所以只能通過一些例子來理解。

  • 例1:
public class Ex1 {

    public static void main(String[] args) {
        Son s = new Son();
    }

}

class Father {
    protected int i = 10;
    protected static int j = 12;
    public Father() {
        print();
        i = 20;
    }

    public void print() {
        System.out.println("Father " + i+" "+j);
    }
}

class Son extends Father {
    private int i = 50;

    private static int j = 60;
    public Son() {
        print();
        super.print();
    }

    public void print() {
        System.out.println("Son " + i+" "+j);
    }
}

//======================執行結果==========================
Son 0
Son 50
Father 20

首先大概地說一下運行步驟:

  1. JVM從main()方法開始執行。這時候碰到new Son();語句,這是第一次new Son對象,所以JVM需要加載Son類。
  2. 現在,JVM去加載Son類了,而在加載Son類的時候又發現了它是繼承Father類,JVM不認識這個類,所以先去加載Father類了,在Father類中,有類變量,所以要爲它分配空間和完成(顯示)初始化。
  3. 加載好Father類後,接着加載Son類去了,步驟和Father類的加載順序是一樣的。
  4. 好了,現在完成了類的加載,下一步就是對象的創建了。Son s = new Son();開始創建s對象。
  5. 第一步就是爲s對象申請空間。注意了,分配空間是有依據的,這裏的便是實例變量(類變量已經在類的加載階段完成了),分配好空間的瞬間就會爲變量賦默認初始值(注意,不是賦值!!比如例子中的int i = 50;將50賦值給i還沒有完成)。(ATTENTION:首先是創建父類對象,即給父類的各個實例變量分配空間和賦默認初始值,這裏省略了。
  6. 第二步,進入到Son()構造方法體裏面;第一條語句一定是對父類的初始化super();有時候是省略了的。然後又調到Father類去執行Father類的構造方法中去了(在這裏我們姑且忽略Object類,將Father類當做頂級)。這時候,父類對各個實例變量完成賦值語句(比如例子中的protected int i = 10,將10賦值給i),然後執行方法體裏面的方法。
  7. Father類中的print()方法被子類覆蓋了,所以這條語句執行的是子類的,然而這時候子類的i只是完成了默認值的初始化,所以先輸出【Son 0】。執行下一條i = 20;Father類構造方法執行完了,又回到子類的構造方法了。
  8. 現在回到子類了。在public Son()中,super();語句執行完了,又跳出來爲各個成員變量賦值(private int i = 50,現在將50賦值給i了!!!)按順序完成了賦值後又回到Son的構造方法體中。執行print(),輸出【Son 50】。執行super.print();調用父類的print()方法,輸出【Father 20】。

總結一下過程:

  1. 類的加載:
    1. 如果類沒有被加載過,就先加載類(父類先加載);
    2. 在加載類的過程中,爲類變量分配空間,並且完成初始化;
  2. 創建對象:
    1. 爲子類對象分配空間(父類的成員變量先分配),並且在完成分配的瞬間對各個變量賦默認值(賦值語句不執行)。
    2. 執行子類的構造函數,第一句一定是super(…),所以會跳轉到父類中,這裏省略,只說子類的執行順序(父類的是一樣的);執行完super(…)後再完成各個實例變量的賦值語句;完成後依次序執行相應的語句。

需要注意的是:

  1. 如果子類對父類的實例方法進行了覆蓋(返回類型、實例方法名、參數相同),那麼在父類中執行這個方法時其實用的是子類的(比如上例中的print())。如果子類要訪問父類的這個方法,就用super.*來訪問(比如上例中super.print())。
  2. 覆蓋和隱藏:如果方法是類方法就是隱藏,實例方法就是覆蓋(或重寫)。可以看看這個
  3. 實例變量/方法類變量/方法的區別:實例變量/方法是在對象的創建後纔有的,而類變量/方法是在類的加載階段就開始存在。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章