繁体   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