CodeGym/Java Blog/Inheritance in Java/Base class constructors
Author
Volodymyr Portianko
Java Engineer at Playtika

Base class constructors

Published in the Inheritance in Java group
members
Hi! Last time we talked about constructors and learned a lot about them. Now we're going to talk about base class constructors.
Base class constructors   - 1
What's a base class? It has to do with the fact that in Java several different classes can have a common origin.
Base class constructors   - 2
This is called inheritance. Several child classes can have one common ancestor. For example, imagine that we have an Animal class:
public class Animal {

   String name;
   int age;
}
We can declare 2 child classes: Cat and Dog. This is done using the keyword extends.
public class Cat extends Animal {

}

public class Dog extends Animal {

}
We may find this helpful in the future. For example, if there is a task to catch mice, we'll create a Cat object in our program. If the task is to chase after a stick, then we'll use a Dog object. And if we create a program that simulates a veterinary clinic, it will work with the Animal class (and thus be able to treat both cats and dogs). It is very important to remember that when an object is created, the constructor of its base class is first called. Only after that constructor is finished does the program execute the constructor of the class corresponding to the object we are creating. In other words, when creating a Cat object, the Animal constructor is run first, and only afterward is the Cat constructor executed. To see this, add some console output to the Cat and Animal constructors.
public class Animal {

   public Animal() {
       System.out.println("Animal constructor executed");
   }
}


public class Cat extends Animal {

   public Cat() {
       System.out.println("Cat constructor executed!");
   }

   public static void main(String[] args) {
       Cat cat = new Cat();
   }
}
Console output: Animal constructor executed Cat constructor executed! Indeed, it does work that way! Why? One reason is to avoid duplicating fields shared between the two classes. For example, every animal has a heart and brain, but not every animal has a tail. We could declare brain and heart fields, which are common to all animals, in the Animal parent class, and a tail field in the Cat subclass. . Now we'll declare a Cat class constructor that takes arguments for all 3 fields.
public class Cat extends Animal {

   String tail;

   public Cat(String brain, String heart, String tail) {
       this.brain = brain;
       this.heart = heart;
       this.tail = tail;
   }

   public static void main(String[] args) {
       Cat cat = new Cat("Brain", "Heart", "Tail");
   }
}
Note: The constructor works correctly even though the Cat class has no brain and heart fields. These fields are "inherited" from the Animal base class. The inheriting class has access to the fields of the base class, so they are visible in our Cat class. As a result, we don't need to duplicate these fields in the Cat class. We can take them from the Animal class. What's more, we can explicitly call the base class constructor in the child class constructor. A base class is also called a "superclass". That's why Java uses the keyword super to indicate the base class. In the previous example
public Cat(String brain, String heart, String tail) {
       this.brain = brain;
       this.heart = heart;
       this.tail = tail;
   }
We separately assigned each field in our parent class. We don't actually have to do this. It's enough to call the parent class constructor and pass the necessary arguments:
public class Animal {

   String brain;
   String heart;

   public Animal(String brain, String heart) {
       this.brain = brain;
       this.heart = heart;
   }

public class Cat extends Animal {

   String tail;

   public Cat(String brain, String heart, String tail) {
       super(brain, heart);
       this.tail = tail;
   }

   public static void main(String[] args) {
       Cat cat = new Cat("Brain", "Heart", "Tail");
   }
}
In the Cat constructor, we called the Animal constructor and passed two fields. We had only one field to explicitly initialize: tail, which is not in Animal. Remember we mentioned that the parent class constructor is called first when an object is created? That's why super() should always be first in a constructor! Otherwise, the constructor logic will be violated and the program will generate an error.
public class Cat extends Animal {

   String tail;

   public Cat(String brain, String heart, String tail) {
       this.tail = tail;
       super(brain, heart);// Error!
   }

   public static void main(String[] args) {
       Cat cat = new Cat("Brain", "Heart", "Tail");
   }
}
The compiler knows that when an object of a child class is created, the base class constructor is called first. And if you try to manually change this behavior, the compiler won't allow it.

How an object is created

We previously looked at an example with a base and parent class: Animal and Cat. Using these two classes as examples, we'll now look at the process of creating an object and initializing variables. We know that there are static and instance (non-static) variables. We also know that the Animal base class has variables, and the Cat child class has its own. For clarity, we'll add one static variable each to the Animal and Cat classes. The animalCount variable in the Animal class will represent the total number of animal species on Earth, and the catCount variable will signify the number of cat species. Additionally, we'll assign starting values to all non-static variables in both classes (which will then be changed in the constructor).
public class Animal {

   String brain = "Initial value of brain in the Animal class";
   String heart = "Initial value of heart in the Animal class";

   public static int animalCount = 7700000;

   public Animal(String brain, String heart) {
       System.out.println("Animal base class constructor is running");
       System.out.println("Have the variables of the Animal class already been initialized?");
       System.out.println("Current value of static variable animalCount = " + animalCount);
       System.out.println("Current value of brain in the Animal class = " + this.brain);
       System.out.println("Current value of heart in the Animal class = " + this.heart);
       System.out.println("Have the variables of the Cat class already been initialized?");
       System.out.println("Current value of static variable catCount = " + Cat.catCount);

       this.brain = brain;
       this.heart = heart;
       System.out.println("Animal base class constructor is done!");
       System.out.println("Current value of brain = " + this.brain);
       System.out.println("Current value of heart = " + this.heart);
   }
}

public class Cat extends Animal {

   String tail = "Initial value of tail in the Cat class";

   static int catCount = 37;

   public Cat(String brain, String heart, String tail) {
       super(brain, heart);
       System.out.println("The cat class constructor has started (The Animal constructor already finished)");
       System.out.println("Current value of static variable catCount = " + catCount);
       System.out.println("Current value of tail = " + this.tail);
       this.tail = tail;
       System.out.println("Current value of tail = " + this.tail);
   }

   public static void main(String[] args) {
       Cat cat = new Cat("Brain", "Heart", "Tail");
   }
}
So we're creating a new instance of the Cat class, which inherits Animal. We've added some detailed console output to see what's happening and in what order. This is what will be displayed when a Cat object is created: Animal base class constructor is running Have the variables of the Animal class already been initialized? Current value of static variable animalCount = 7700000 Current value of brain in the Animal class = Initial value of brain in the Animal class Current value of heart in the Animal class = Initial value of heart in the Animal class Have the variables of the Cat class already been initialized? Current value of static variable catCount = 37 Animal base class constructor is done! Current value of brain = Brain Current value heart = Heart The cat class constructor has started (The Animal constructor already finished) Current value of static variable catCount = 37 Current value of tail = Initial value of tail in the Cat class Current value of tail = Tail So, now we can clearly see the order of variable initialization and constructor calls when a new object is created:
  1. Static variables of the base class (Animal) are initialized. In our case, the Animal class's variable animalCount is set to 7700000.

  2. Static variables of the child class (Cat) are initialized.

    Note: we're still inside the Animal constructor and we've already displayed:

    Animal base class constructor is running
    Have the variables of the Animal class already been initialized?
    Current value of static variable animalCount = 7700000
    Current value of brain in the Animal class = Initial value of brain in the Animal class
    Current value of heart in the Animal class = Initial value of heart in the Animal class
    Have the variables of the Cat class already been initialized?
    Current value of static variable catCount = 37


  3. Then the non-static variables of the base class are initialized. We specifically assigned them initial values, which are then replaced in the constructor. The Animal constructor has not finished yet, but the initial values of brain and heart have already been assigned:

    Animal base class constructor is running
    Have the variables of the Animal class already been initialized?
    Current value of static variable animalCount = 7700000
    Current value of brain in the Animal class = Initial value of brain in the Animal class
    Current value of heart in the Animal class = Initial value of heart in the Animal class


  4. The base class constructor starts.
    We've already convinced ourselves that this step is fourth: in the first three steps at the beginning of the Animal constructor, many variables have already been assigned values.


  5. Non-static fields of the child class (Cat) are initialized.
    This happens before the Cat constructor starts running.
    When it starts running, the tail variable already has a value:

    The cat class constructor has started (The Animal constructor already finished) Current value of static variable catCount = 37 Current value of tail = Initial value of tail in the Cat class


  6. The constructor of the Cat child class is called

    And that's what creating an object looks like in Java!

    I must say that we are not big fans of rote-learning, but it's best to memorize the order of variable initialization and constructor calls.

    This will greatly increase your understanding of the flow of the program, and the state of your objects at any particular moment.

    Moreover, many classes don't use inheritance. In this case, the steps related to the base class don't apply.

Comments (56)
  • Popular
  • New
  • Old
You must be signed in to leave a comment
冲击
Level 18 , Seattle, China
20 December 2023, 06:06
我是这样理解的 ,首先是所有的静态变量初始化(因为静态变量被类所属),而一个类中并不止静态变量,构造函数需要启动的话还要初始化基类里面的非静态变量(如果有的话)这是基类的构造函数才会启动。接下来是子类的构造函数,因为所有的静态变量在基类构造函数启动时已经静初始化,所以只需要初始化子类当中的非静态变量就可以启动子类的构造函数了。(个人见解,若有不对之处请指正)
Henry Lee System Engineer
9 December 2023, 06:37
1. 基类的静态变量被初始化。 2. 子类的静态变量被初始化。 3. 初始化基类的非静态变量。 4. 基类构造函数启动。 5. 初始化子类的非静态变量。 6. 子类的构造函数启动。
Melody Ye
Level 29 , France, France
18 February 2023, 11:33
Goooood!
0wis
Level 6 , Paris, France
13 December 2021, 19:44
Ok, this is a great article to understand how inheritance practically works. And in fact the order of variable initialization makes sense : Static variables are independent of any created object by definition (that is what they are used for). Therefore, they must be initialized before any object creation, as soon as the class is called. Otherwise it could create a conflict if there is any non-static variable that has "taken" the place of a static one. And it seems logical to initialize the parent class static variables before the child ones. Then, when every necessary static variable are initialized, it is time to initialize the non-static variables of the created object before using the constructor to create the object. And as a class is a "blueprint" of each object, you need to build the fondations (the parent class) before building the last bits (in the child class). Hence the order : 1.Base class static variable initialization 2.Child class static variable initialization 3.Base class non-static variable initialization 4.Base class constructor 5.Child class non-static variable initialization 6.Child class constructor I home my "mnemotechnic" explanation will be usefull and not too confusing. Thanks professor !
Nathan Guidry
Level 6 , Lake Charles, United States
16 May 2021, 23:34
okay but shouldn't we avoid logic and functionality within constructors and leave that to methods??:<
Jonaskinny Java Developer at Sandmedia
11 February 2022, 18:17
Well yes, although no logic error would exist in this case but yes the inconsistency abounds. Side note, this is the case in the industry where you have to decide to either ... a-modify code to comply with 'best practices' yet have it be different than the rest of the code in the app b-just make the changes you need and do not perfect the legacy base code so future maintainers don't have 2 standards to follow. So its actually good to get used to this seeming dichotomy and its not easy over years of maintaining various systems from a single organization (where much of the code is shared/copied from one to the others) and also maintain your recollection of how to do things 'correctly'
cheese
Level 8 , Guangzhou
20 March 2021, 07:47
父类的初始化先于子类的初始化,你爸爸肯定比你先出生呀!反正出场顺序就是父类静态>子类静态>父类的非静态>子类的非静态
TaoLu
Level 20 , 泾县, China
11 March 2021, 12:04
类的初始化 先于实例,基类(父类)的初始化先于派生类(子类), 类属性随类初始化而初始化(包含赋值初始化,static 初始化块,类初始化代码执行失败将会导致类无法被加载) 构造器--父类的构造器先于子类执行,(注意,子类的构造器第一行都是显示或隐式的调用父类构造器) 实例变量--父类的实例变量(继承到的)先于子类完成初始化,(执行顺序 遵循,构造器第一行代码完成后,即父类构造器调用返回,1,默认初始化成员(基本类型初始化为0,boolean-false,引用类型-null)2,执行声明变量时等于号后面的代码,还有初始化块的代码,顺序由代码编写自上而下执行,4,执行构造器剩余代码。) --final 实例变量,必须在实例初始化时赋值(即,1,声明时赋值,2,初始化块中赋值,3,构造器中赋值) --constant常量(psf),由编译器宏替换,字节码中不存在? --私有常量(private static final)和常量一个定位吗?? 目前个人的理解只能到这里了,如果有错误欢迎指正,欢迎大家补充,一起学习!
sreedhar s
Level 9 , Wellington, New Zealand
10 March 2021, 22:07
Took time to understand, very good explanation, thank you!
Andy Lee
Level 10
24 February 2021, 11:17
所以是初始化先于构造,静态先于非静态(实例)?
Sinisa
Level 11 , Banja Luka, Bosnia and Herzegovina
22 February 2021, 14:01
Confusion of the highest orda!