简体   繁体   English

抽象类的Java构造函数

[英]Java constructor of an abstract class

As far as I know (please correct me if I'm wrong) a class that is abstract cannot be instantiated. 据我所知(如果我错了请纠正我)一个抽象的类无法实例化。 You can give it a constructor, but just can't call new on that class. 你可以给它一个构造函数,但不能在该类上调用new。 If you call super in a subclass, the superclass constructor will run (and thus create an object of that class?) then how come you can actually call super in a subclass of an abstract class? 如果在子类中调用super,超类构造函数将运行(从而创建该类的对象?)那么为什么你实际上可以在抽象类的子类中调用super? I'm sure it has something to do with my misunderstanding about a constructor making an object... 我确定这与我对构造函数制作对象的误解有关...

If you call super in a subclass, the superclass constructor will run (and thus create an object of that class??) then how come you can accually call super in a subclass of an abstract class? 如果在子类中调用super,超类构造函数将运行(从而创建该类的对象??)那么为什么你可以在抽象类的子类中精确地调用super?

This part is wrong. 这部分是错的。 When you call super in the subclass constructor, you're just telling to the subclass that it first has to execute the initialization code from the super class (abstract or not), then it will continue executing the code to initialize the current instance of the class being created. 当你在子类构造函数中调用super时,你只是告诉子类它首先必须从超类(抽象与否)执行初始化代码,然后它将继续执行代码来初始化当前的实例。正在创建的课程。 This doesn't mean that it will create an instance of the super class in the middle of the creation of the current instance. 这并不意味着它将在创建当前实例的过程中创建超类的实例。

Calling a constructor in an abstract class would only be used to setup attributes that are specific to that abstract class - which would otherwise be tedious to setup in each implementation of the abstract class. 在抽象类中调用构造函数只能用于设置特定于该抽象类的属性 - 否则在抽象类的每个实现中设置都会很繁琐。 This ability removes boiler plate code. 此功能可删除锅炉板代码。

In the example below, see how determining the lifetime of the car could be calculated based on other attributes of the car. 在下面的示例中,了解如何根据汽车的其他属性计算汽车的使用寿命。 This would be excessive to do it in each implementation of the Car subtype. 这在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
    }
}

Lets say for example we define the abstract class "car". 让我们说例如我们定义抽象类“汽车”。 Then we write a subclass, "honda" extending "car". 然后我们写了一个子类,“honda”扩展“car”。 In order to make a "honda", you must first make a "car". 为了制造“本田”,你必须先制作一辆“汽车”。 Regardless of whether or not "car" was abstract, in order to make any subclass object, you have to call super() to "make" the superclass object first. 无论“car”是否是抽象的,为了制作任何子类对象,你必须首先调用super()来“make”超类对象。

See my answer to a similar question here: What is (is there?) a purpose behind declaring a method twice when parent class appears to not change any properties? 在这里查看我对类似问题的回答: 当父类似乎没有改变任何属性时,是什么(有没有?)一个目的,然后两次声明一个方法? (Note that this question is a misnomer, and is actually talking about constructors) (请注意,这个问题用词不当,实际上是在讨论构造函数)

I'll try to explain it at byte code level and see if that helps. 我将尝试在字节代码级别解释它,看看是否有帮助。

AbstractService.java AbstractService.java

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

Service.java 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);
    }
}

If you look at the generated byte code for these classes 如果查看这些类的生成字节代码

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(); is translated to 被翻译成

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

As you can see above, first new byte code is executed which will create a new Object with only default values for instance variables (in this case id is integer so default 0) but are initialized, then dup which will create a copy of this new object reference and then invokespecial which will call Service() that will call in turn call AbstractService() which in turn calls Object() 如上所示,首先执行新的字节代码,它将创建一个新的Object,只有实例变量的默认值(在这种情况下,id是整数,所以默认为0)但是已经初始化,然后是dup,它将创建这个新的副本对象引用然后invokespecial将调用Service(),它将依次调用AbstractService()调用Object()

If you look at the byte codes in those methods, they don't create objects (no new byte code instruction) they merely initialize the objects like setting the value of variable id=10 which is the proper initialization to user-defined values. 如果查看这些方法中的字节代码,它们不会创建对象(没有新的字节代码指令),它们只是初始化对象,例如设置变量id = 10的值,这是对用户定义值的正确初始化。

So when you say new SomeClass(), it's not just about calling your Constructor, its about creating an object with default states and then calling special method called constructor (either the compiler generated one or the user-defined one) to initialize that object. 因此,当您说新的SomeClass()时,它不仅仅是调用您的构造函数,而是创建一个具有默认状态的对象,然后调用名为constructor的特殊方法(编译器生成一个或用户定义的一个)来初始化该对象。 In another words, constructors bring objects from default state(id=0) to user-defined state(id=10) so that the object is ready to use ie calling methods on it. 换句话说,构造函数将对象从默认状态(id = 0)带到用户定义的状态(id = 10),以便对象可以使用,即调用它的方法。

This is too much at the beginner level but if you pay attention to the byte code it will make sense :) 这在初学者级别太多但是如果你注意字节代码它会有意义:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM