簡體   English   中英

抽象類的Java構造函數

[英]Java constructor of an abstract class

據我所知(如果我錯了請糾正我)一個抽象的類無法實例化。 你可以給它一個構造函數,但不能在該類上調用new。 如果在子類中調用super,超類構造函數將運行(從而創建該類的對象?)那么為什么你實際上可以在抽象類的子類中調用super? 我確定這與我對構造函數制作對象的誤解有關...

如果在子類中調用super,超類構造函數將運行(從而創建該類的對象??)那么為什么你可以在抽象類的子類中精確地調用super?

這部分是錯的。 當你在子類構造函數中調用super時,你只是告訴子類它首先必須從超類(抽象與否)執行初始化代碼,然后它將繼續執行代碼來初始化當前的實例。正在創建的課程。 這並不意味着它將在創建當前實例的過程中創建超類的實例。

在抽象類中調用構造函數只能用於設置特定於該抽象類的屬性 - 否則在抽象類的每個實現中設置都會很繁瑣。 此功能可刪除鍋爐板代碼。

在下面的示例中,了解如何根據汽車的其他屬性計算汽車的使用壽命。 這在Car子類型的每個實現中都是過多的。

abstract class Car {
    // Determine how many years a car will last based on other components
    int lifeTimeInYears;

    float price;

    public Car(float price) {
        // Assuming you could calculate the longevity based on price;
        if (price > 50000) {
            lifeTimeInYears = 15;
        }
        else {
            lifeTimeInYears = 10;
        }
    }

    public int getLifeTimeInYears() {
        return lifeTimeInYears;
    }
}

class SportsCar extends Car {

    public SportsCar(float price) {
        super(price);
    }
}

class CommuterCar extends Car {

    public CommuterCar(float price) {
        super(price);
    }
}

public class Test {
    public static void main(String[] args) {
        SportsCar sportsCar = new SportsCar(150000);
        sportsCar.getLifeTimeInYears(); // Value is 15

        CommuterCar commuterCar = new CommuterCar(15000);
        commuterCar.getLifeTimeInYears(); // Value is 10
    }
}

讓我們說例如我們定義抽象類“汽車”。 然后我們寫了一個子類,“honda”擴展“car”。 為了制造“本田”,你必須先制作一輛“汽車”。 無論“car”是否是抽象的,為了制作任何子類對象,你必須首先調用super()來“make”超類對象。

在這里查看我對類似問題的回答: 當父類似乎沒有改變任何屬性時,是什么(有沒有?)一個目的,然后兩次聲明一個方法? (請注意,這個問題用詞不當,實際上是在討論構造函數)

我將嘗試在字節代碼級別解釋它,看看是否有幫助。

AbstractService.java

public abstract class AbstractService {
    protected int id = 10;
    public abstract void verify();
}

Service.java

public class Service extends AbstractService{
    public static void main(String[] args) {
        Service service = new Service();
        service.verify();
    }

    public void verify() {
        System.out.println("printing id = "+id);
    }
}

如果查看這些類的生成字節代碼

public abstract class AbstractService {
    protected int id;

    public AbstractService() {
        /* L4 */
        0 aload_0;                /* this */
        1 invokespecial 1;        /* java.lang.Object() */
        /* L6 */
        4 aload_0;                /* this */
        5 bipush 10;
        7 putfield 2;             /* .id */
        10 return;
    }

    public abstract void verify();
}




public class Service extends com.sample.service.AbstractService {

    public Service() {
        /* L3 */
        0 aload_0;                /* this */
        1 invokespecial 1;        /* com.sample.service.AbstractService() */
        4 return;
    }

    public static void main(java.lang.String[] args) {
        /* L6 */
        0 new 2;
        3 dup;
        4 invokespecial 3;        /* com.sample.service.Service() */
        7 astore_1;               /* service */
        /* L7 */
        8 aload_1;                /* service */
        9 invokevirtual 4;        /* void verify() */
        /* L8 */
        12 return;
    }

    public void verify() {
        /* Skipping this as it's not needed */
    }
}

Service service = new Service(); 被翻譯成

        0 new 2;
        3 dup;
        4 invokespecial 3;        /* com.sample.service.Service() */

如上所示,首先執行新的字節代碼,它將創建一個新的Object,只有實例變量的默認值(在這種情況下,id是整數,所以默認為0)但是已經初始化,然后是dup,它將創建這個新的副本對象引用然后invokespecial將調用Service(),它將依次調用AbstractService()調用Object()

如果查看這些方法中的字節代碼,它們不會創建對象(沒有新的字節代碼指令),它們只是初始化對象,例如設置變量id = 10的值,這是對用戶定義值的正確初始化。

因此,當您說新的SomeClass()時,它不僅僅是調用您的構造函數,而是創建一個具有默認狀態的對象,然后調用名為constructor的特殊方法(編譯器生成一個或用戶定義的一個)來初始化該對象。 換句話說,構造函數將對象從默認狀態(id = 0)帶到用戶定義的狀態(id = 10),以便對象可以使用,即調用它的方法。

這在初學者級別太多但是如果你注意字節代碼它會有意義:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM