Default values
And it is said that these constucors are used to initialize default values to the class attributes.
That is incorrect. The constructors (including the default no-arg constructor) does not initialize the fields to their default values. This is done implicitly beforehand by the language already (see the JLS definition).
The default constructor is identical to a completely empty constructor:
Foo() {}
Technically, like other constructors, this implicitly still contains the call to the parent class constructor:
Foo() {
super();
}
Also have a look at the bytecode of public class Foo {}
, which is:
public class Foo {
public Foo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
You can clearly see the default constructor with code to invoke Object
s constructor.
Why add it in bytecode?
Why does the compiler add a default constructor in the case when the user fails to write a constructor?
In theory it would not have to do that. However, language-design wise it is much easier to just add it to simplify the rest of the language.
For example, then you do not need any magic to make new Foo();
work, since the constructor just actually exists in the code that the JVM executes.
Same holds for more advanced topics such as the reflection API, which has methods like
Object foo = Foo.class.getConstructor().newInstance();
So if the constructor just actually exists in the bytecode, again, you do not need any magic in the JVM to make this work. It just works out of the box.
At the end of the day it was a design decision by the developers to create it in the way they did. They could have realized it differently as well.
That way however, you have a much clearer split between Java and JVM bytecode as languages. And technically you can also create classes in bytecode that do not even have constructors at all (which you can not create from within Java), which is interesting to special tools and other languages that compile to JVM bytecode (Kotlin, Groovy, Scala, Clojure, ...).