抽象类的Java构造函数

时间:2015-01-21 21:22:51

标签: java inheritance constructor abstract-class

据我所知(如果我错了,请纠正我)一个抽象的类无法实例化。你可以给它一个构造函数,但是不能在该类上调用new。如果在子类中调用super,超类构造函数将运行(从而创建该类的对象?)那么为什么你实际上可以在抽象类的子类中调用super? 我确定这与我对构造对象的误解有关...

4 个答案:

答案 0 :(得分:6)

  

如果在子类中调用super,超类构造函数将运行(从而创建该类的对象??)那么为什么你可以在抽象类的子类中精确地调用super?

这部分是错误的。当您在子类构造函数中调用super时,您只是告诉子类它首先必须从超类(抽象与否)执行初始化代码,然后它将继续执行代码到初始化正在创建的类的当前实例。这并不意味着它将在创建当前实例的过程中创建超类的实例。

答案 1 :(得分:2)

在抽象类中调用构造函数只能用于设置特定于该抽象类的属性 - 否则在抽象类的每个实现中设置都会很繁琐。此功能会删除锅炉板代码。

在下面的示例中,了解如何根据汽车的其他属性计算汽车的使用寿命。在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
    }
}

答案 2 :(得分:1)

让我们说例如我们定义抽象类" car"。然后我们编写一个子类," honda"延伸" car"。为了制造一辆" honda",你必须先制作一辆"汽车"。无论是否" car"是抽象的,为了制作任何子类对象,你必须调用super()to" make"首先是超类对象。

在这里查看我对类似问题的回答:What is (is there?) a purpose behind declaring a method twice when parent class appears to not change any properties?(请注意,这个问题用词不当,实际上是在讨论构造函数)

答案 3 :(得分:1)

我会尝试在字节码级解释它,看看是否有帮助。

<强> 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),以便对象可以使用,即在其上调用方法。

这在初学者级别太多了,但是如果你注意字节代码就会有意义:)