More quiz questions available here
Given the following two classes
01: abstract class SupA {
02: SupA() { this(null); }
03: SupA(SubA s) {this.init();}
04: abstract void init();
05: }
06: class SubA extends SupA {
07: void init() {System.out.print("SubA");}
08: }
Which statement is correct if you try to compile the code and create an instance of SubA? Choose one.
A. | Compilation fails at line 02. | |
B. | Compilation fails at line 03. | |
C. | A runtime exception occurs at line 02. | |
D. | A runtime exception occurs at line 03. | |
E. | SubA is printed. |
|
F. | SubASubA is printed. |
Answer. This question investigates object initialization, overridden method invocation, and the difference between this()
and this
.
Consider the process of instantiating and initializing an object, and assume that the class and all its parent types are fully loaded and initialized at the point of instantiation.
new
causes the allocation of memory for the entire object, including all the parent elements of which it is made. That memory is also zeroed in this phase.In contemporary releases of Java, including Java 17 and later, all constructors start with one of three code elements. First, there is an implicit call to super()
with no arguments. This is followed by either an explicit call to super(...)
, which may take arguments, or by a call to this(...)
, which, again, may take arguments. Strictly, evaluation of any actual parameters to these calls executes before those delegating calls. (Note that there’s a proposal to allow explicit code to be placed before those calls, provided no reference is made to the uninitialized this
object, but that’s for the future.)
The class SupA has two constructors. The one on line 02 delegates to the one on line 03 using this(null)
, while the one on line 03 has an implicit call to super()
.
The class SubA has no explicit constructors; therefore, the compiler gives it an implicit constructor, which delegates using super()
.
From that outline, consider the flow of construction and initialization of an instance of SubA.
First, the invocation new SubA()
begins by allocating and zeroing memory for the entire object, including the storage necessary for the SubA, SupA, and Object parts. There are no instance fields in the code you see in this question, but the principle is the same.
Next, the newly allocated object is passed as the implicit this
argument into the implicit constructor for SubA. That constructor immediately delegates—using super()
—to the zero-argument constructor for SupA. That constructor in turn immediately delegates to the one-argument constructor for SupA on line 03 using the explicit call this(null)
.
The body of the constructor on line 03 begins with an implicit call to super()
that was generated by the compiler. That call passes control up to the zero-argument constructor of java.lang.Object
. When control returns from the constructor, any instance initialization on the SupA class would be executed, but of course there is none in this case. So, execution continues with the explicit body of the constructor. The constructor body calls this.init();
, which invokes the implementation on line 07 and prints the message SubA
. At that point, the constructor on line 03 is finished and control returns to the constructor on line 02, which also is finished since there’s no more code after this(null)
.
After all the constructors for SupA have finished, control returns to the implicit constructor of SubA. The implicit constructor would then perform any instance initialization called for by the SubA class, but there is none. At this point, the construction and initialization process has been completed.
Notice that in the description above, the message SubA
was printed exactly once, which makes option E the correct answer and options A, B, C, D, and F incorrect.
To dive deeper, here are some specific considerations for clarification and additional points.
A call of the form this()
(with parentheses) is a call to an overloaded constructor in the same class. Contrast this with the reference this
, which is an explicit reference to the current instance of the class. The second form (this
without parentheses) is the prefix used explicitly to invoke the init()
method. Also note that init()
is an ordinary instance method that implements the abstract method declared in SupA; Java does not attach any special meaning to the name init.
The constructor on line 02 passes null to the constructor on line 03. There’s nothing tricky here. If the constructor on line 03 attempted to refer to the object, it would cause a null pointer exception, but because no such reference is made, there is no problem.
The call to this.init()
on line 03 is entirely valid and safe. It’s not possible to enter a constructor except via a call to new
, and new
must be followed by a concrete class name. This in turn means that the object referred to by this
on line 03 must have a proper implementation of the init()
method.
Creating a new instance of SubA does not cause an instance of SupA to be created, so there is only one instance, and when you call this.<something>
, it will be tried on SubA. If this element is missing, it will be looked up for an inherited element in superclasses. In the case of the init()
method, it will be directly present in SubA, so it will be called when the this.init();
statement runs.
One more point regarding good coding practice: Although it’s done in this question, it’s a bad idea to call overridable methods during instance initialization, for two reasons.
Conclusion. The correct answer is option E.
Simon Roberts joined Sun Microsystems in time to teach Sun’s first Java classes in the UK. He created the Sun Certified Java Programmer and Sun Certified Java Developer exams. He wrote several Java certification guides and is currently a freelance educator who publishes recorded and live video training through Pearson InformIT (available direct and through the O’Reilly Safari Books Online service). He remains involved with Oracle’s Java certification projects.
Mikalai Zaikin is a lead Java developer at IBA Lithuania (part of worldwide IBA Group) and currently located in Vilnius. During his career, Zaikin has helped Oracle with development of Java certification exams, and he has been a technical reviewer of several Java certification books, including three editions of the famous Sun Certified Programmer for Java study guides by Kathy Sierra and Bert Bates.
Previous Post