java基礎:繼承關係類加載順序問題

/**
 * Created by fei on 2017/5/31.
 */
public class SonClass extends ParentClass{
    public SonClass(){
        System.out.println("SonClass's constructor");
    }
    { System.out.println("SonClass's block");}
    static {
        System.out.println("SonClass's static block ");
    }

    public static void main(String[] args) {
        System.out.println("------ main start ------ ");
        new SonClass();
        System.out.println("------ main  end  ------ ");
    }
}

class ParentClass{
    public ParentClass(){
        System.out.println("ParentClass's constructor");
    }
    { System.out.println("ParentClass's block");}
    static {
        System.out.println("ParentClass's static block ");
    }
}

運行結果:

ParentClass's static block 
SonClass's static block 
------ main start ------ 
ParentClass's block
ParentClass's constructor
SonClass's block
SonClass's constructor
------ main  end  ------ 

根據運行結果,一目瞭然,在執行 main 方法中 new SonClass() 之前,就在類加載之後執行了類中 static 代碼塊。然後再進入main方法,執行new操作,當然顯而易見,在執行new子類操作的時候,是要先進行其父類的構造,即先執行父類的構造代碼塊(代碼中只用大括號包裹的那段代碼)以及構造函數 ,然後再執行子類的構造代碼塊以及構造函數。

修改一下代碼,再來看看運行的結果:

/**
 * Created by fei on 2017/5/31.
 */
public class SonClass extends ParentClass{
    ParentClass parentClass;
    public SonClass(){
        System.out.println("1");
    }
    public SonClass(String name){
        System.out.println("2");
        this.name = name;
        parentClass = new ParentClass("FEI");
    }

    public static void main(String[] args) {
        System.out.println("------ main start ------ ");
        new SonClass("fei");
        System.out.println("------ main  end  ------ ");
    }
}

class ParentClass{
    String name ;
    public ParentClass(){
        System.out.println("3");
    }
    public ParentClass(String name){
        System.out.println("4");
        this.name = name ;
    }
}

運行的順序是:

------ main start ------ 
3
2
4
------ main  end  ------ 
  • 第一個規則:子類的構造過程中,必須調用其父類的構造方法。一個類,如果我們不寫構造方法,那麼編譯器會幫我們加上一個默認的構造方法(就是沒有參數的構造方法),但是如果你自己寫了構造方法,那麼編譯器就不會給你添加了,所以有時候當你new一個子類對象的時候,肯定調用了子類的構造方法,但是如果在子類構造方法中我們並沒有顯示的調用基類的構造方法,如:super(); 這樣就會調用父類沒有參數的構造方法。

  • 第二個規則:如果子類的構造方法中既沒有顯示的調用基類構造方法,而基類中又沒有無參的構造方法,則編譯出錯,所以,通常我們需要顯示的:super(參數列表),來調用父類有參數的構造函數,此時無參的構造函數就不會被調用。

總之,一句話:子類沒有顯示調用父類構造函數,不管子類構造函數是否帶參數都默認調用父類無參的構造函數,若父類沒有則編譯出錯。

還是兩個類,我們再更改一下。

/**
 * Created by fei on 2017/5/31.
 */
public class SonClass extends ParentClass{
    private String name = "SonClass";

    public SonClass() {
        printName();
    }
    public void printName() {
        System.out.println("SonClass print name: " + name);
    }
    public static void main(String[] args){
        new SonClass();
    }
}

class ParentClass{
    private String name = "ParentClass";

    public ParentClass() {
        //System.out.println(this.getClass());
        printName();
    }
    public void printName() {
        System.out.println("ParentClass print name: " + name);
    }
}

看了上面的兩個例子,最後這個例子就很容易被迷惑,可能有人會覺得運行結果是類似這樣的:

ParentClass print name: ParentClass 
SonClass print name: SonClass

或者是:

ParentClass print name: SonClass
SonClass print name: SonClass

但真正的結果是這樣的:

SonClass print name: null
SonClass print name: SonClass

爲什麼會這樣,其實只要打開代碼中父類構造器中的這句註釋,就很容易理解了:System.out.println(this.getClass())
結果是:

class SonClass

沒錯,父類中的this引用是子類實例對象,所以在父類構造函數裏調用的還是子類的printName()方法。具體原因也並我能十分肯定,我個人淺見,是因爲雖然我們調用了父類的構造方法,但是我們並沒有實例化出父類的實例對象,所以this還是指向的是子類的引用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章