I have a class
public class MyMain{
public static void main(String... arg){
Temp t = new Temp(){
{
System.out.println(" instance initialize");
}
};
}
}
class Temp{
int i;
{
i=9;
System.out.println("Static"+i);
}
Temp(){
System.out.println("Temp const "+i);
}
}
When i execute the main method the output comes:
Static9
Temp const 9
instance initialize
Ideally, the blocks are executed before the constructor, but the inline initialization block is called after the Constructor. Why?
You're creating a subclass of Temp. For each class, any instance initializers are executed before the constructor body - but the superclass goes through initialization before the subclass initialization. So the execution flow is:
Initializers in Object
Constructor body in Object
Initializers in Temp
Constructor body in Temp
Initializers in anonymous class
Constructor body in anonymous class (none)
I would strongly advise you to refactor any code which looked like this anyway - aim for clarity rather than cleverness.
JLS 12.5 spells out the order in which things happen during construction (emphasis mine):
Just before a reference to the newly created object is returned as the
result, the indicated constructor is processed to initialize the new
object using the following procedure:
(3) This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If
this constructor is for a class other than Object, then this
constructor will begin with an explicit or implicit invocation of a
superclass constructor (using super). Evaluate the arguments and
process that superclass constructor invocation recursively using these
same five steps. If that constructor invocation completes abruptly,
then this procedure completes abruptly for the same reason. Otherwise,
continue with step 4.
(4) Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable
initializers to the corresponding instance variables, in the
left-to-right order in which they appear textually in the source code
for the class. If execution of any of these initializers results in an
exception, then no further initializers are processed and this
procedure completes abruptly with that same exception. Otherwise,
continue with step 5.
(5) Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the
same reason. Otherwise, this procedure completes normally.
To summarize, superclass constructors (step 3) are executed before instance initializers (step 4). Both are executed before "the rest of the body of this constructor" (which you don't have in your example).
The inline initialisation block is called after the constructor of the base class of the anonymous class you are instancing at the moment and before the empty implicit constructor of the anonymous class itself.
What you are actually creating is not a Temp class instance, but an instance of some anonimous class, that inherits from Temp.
Thus, at first, the Temp initializers are called (anonimous block inside Temp and its construstor) and initializers in anonimous class are called afterwards.
In your code
Temp t = new Temp(){
{
System.out.println(" instance initialize");
}
};
you are creating object of anonymous class which extends Temp class.
Creating object of Subclass:
initialize block from Superclass
constructor of Superclass
initialize block from Subclass
constructor of Subclass
Point 1: To be clear, you have two instance initializers: one in the Temp class and one in the anonymous inner class created in the main() method that is a subclass of Temp.
Point 2: Instance initializers aren't actually run before constructors. Per the JLS, they're run during a constructor, after delegating to the super constructor and before initializing instance fields and completing the constructor.
Point 3: In your code, each initializer is correctly executed at its appropriate time. I think you're expecting the second one to execute at the same time as the first one, but that would be incorrect because as pointed out in Point 1, they're initializers for two different classes.
Point 4: You may also be confused between static initializers and instance initializers. They're two distinct things.
The object must be in memory first before anything else can be done to it. The object is constructed in memory and then your console prints happen.
Related
I was learning about using this() to call an overloaded constructor and came across this restriction:
You can not use any instance variable of the constructor's class in a call
to this()
For example:
class Test{
int x;
public Test() {
this(x); //Does not compile
}
public Test(int y) {}
void method1() {
method2(x); //OK
}
void method2(int y) {}
}
I know that no need to pass an instance field to a constructor since it's visible by default. However, why is the same restriction not applied to instance methods?
There is one other requirement in Java: constructor calls (using this() must be performed first within any constructor. The constructors will initialize the object.
After that the instance fields are initialized after these initial calls. So as the field values are now well defined, you can use them for anything including calling other methods.
However, before the initial constructor calls, the fields are in an undefined state and cannot be used as argument for other constructor calls.
For these kind of things you need to look in the JLS:
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
So the instance variables are only initialized after the constructor calls. This makes sense, because it would be strange to first assign it the default value (zero or null) and then assign it another value from within a constructor.
The constructor constructs the instance. So we shouldn't expect the instance variable x to be initialized at the time the constructor starts.
On the other hand, an instance method can already access instance variables. There is no reason to forbid passing them as a parameter into another instance method.
However, when we start to think about it a bit further, that restriction on the constructor doesn't make that much sense anymore. We are able to access instance variables there as well, so why shouldn't we be able to pass it a parameter to another constructor?
So a better question to ask is: Why can't we pass an instance variable to call an overload of our constructor from within our constructor?
And this question has been beautifully answered. It could even be considered a duplicate (but since that pretext is neccessary to understand why, I wrote an answer instead of simply flagging).
The instance fields that are declared and initialized in the class body outside of the constructor, like your int x; are assigned after the call to the overloaded constructor.
You can compare that to the other restriction we have about calling constructor overloads: We can only do so in the first line of our constructor. Right at the start. And then, the variables are not yet initialized. But they are initialized right before the first non-constructor-call instruction.
Tangential information to why this other restriction is a thing can be found here and there:
Because the JLS says so. Could the JLS be changed in a compatible manner to allow it? Yup.
Historically, this() or super() must be first in a constructor. This
restriction was never popular, and perceived as arbitrary. There were
a number of subtle reasons, including the verification of
invokespecial, that contributed to this restriction. Over the years,
we've addressed these at the VM level, to the point where it becomes
practical to consider lifting this restriction, not just for records,
but for all constructors.
You should be educating the class order initialization f.e: https://www.baeldung.com/java-initialization
The int x field is not initialized when the class constructor is called. You can set (initialize) `int x 'in your constructor, but not call it.
I don't know why you have to follow this way, but you can use the static field:
class Test{
static int x;
public Test() {
this(x); //Does not compile
}
public Test(int y) {}
void method1() {
method2(x); //OK
}
void method2(int y) {}
}
You can also initialize a static field in the line where it is called,
static int x =4/2;
or in static block:
static int x;
static {
x = 4/2;
}
This question already has an answer here:
Don’t call subclass methods from a superclass constructor
(1 answer)
Closed 7 years ago.
class superclass {
superclass() {
show();
}
void show() {
System.out.println("three");
}
}
class subclass extends superclass {
int x=3;
void show() {
System.out.println(x);
}
public static void main(String ar[]) {
subclass s=new subclass();
}
}
Output: 0
According to me: first the subclass constructor gets executed, which then calls superclass constructor. This means that, first of all, the show() method of the superclass should execute and should print three, but that is not happening. Why?
The superclass constructor is executed before any of the subclass variables are initialized. So, the x in subclass is still 0 (the default value) while the superclass constructor is executing. Polymorphism still works when a method is called in a constructor, so subclass's show is called, printing x prints 0. This illustrates the danger in calling an overridable method in a constructor -- variables may not be initialized yet. In this case, x isn't 3 [yet.
The JLS, Section 12.5, covers the steps in executing a constructor.
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
(emphasis mine)
The variables are initialized after the call to the superclass constructor completes, whether that call is explicit or implicit.
subclass overrides superclass's show method. Therefore when you create an instance of subclass, subclass's show method is executed.
The reason it returns 0 and not 3 is that it is called from the constructor of superclass, which is executed before the instance variables of subclass are initialized. Therefore x still contains a default value of 0.
As i said last time you posted this question, when you make an object of subclass, it calls the superclass constructor which calls the child show() method (which overrides the super show() method) before x is initialized to 3.
If you had
subclass(){
show();
}
in your subclass, then it would print 3 because that would call the show() method after x has been initialized to that value.
If you wanted to print three then you should call super.show() in the subclass constructor.
I am trying to piece together the process of Initialization and Instantiation in the JVM but the JLS is a little obtuse on a few details, so if anyone would mind clearing up some details it would be appreciated. This is what I have been able to figure out so far.
Initialization
Recursively Initialize static final variables of the class and it's interfaces that are compile time constants.
Back out of the recursion processing static blocks and static fields in textual order.
Instantiation
Recursively Initialize final instance variables of the class that are compile time constants.
Back out of the recursion processing non-static blocks and instance fields in textual order prepending them to the constructors as it returns.
Okay, so now for the questions.
are interfaces processed in order of declaration?
are interfaces processed in a separate recursive stack?
a) if yes, do interfaces get processed before or after superclasses?
b) if yes, am I correct in deducing that one or the others (Interface or Superclass) gets its non-compile-time constant fields initialized before the others compile-time constants.
What role does calls to the nondefault super() constructor play in this process?
Am I mistaken in any of my conclusions?
Am I missing any other key details?
It is important to distinguish between the initialization of a class, and initialization of an object.
Class Initialization
A class or interface is initialized upon first access, by assigning the compile time constant fields, then recursively initializing the superclass (if not already initialized), then processing the static initializers (which include the initializers for for the static fields that are not compile time constants).
As you have noticed, initialization of a class does not, by itself, trigger initialization of the interfaces it implements. Interfaces are therefore initialized when they are first accessed, typically by reading a field that is not a compile time constant. This access may occur during evaluation of an initializer, causing a recursive initialization.
It is also worth noting that initialization is not triggered by accessing fields that are compile time constants, as these are evaluated at compile time:
A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.
If such a field is static, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V) must never be observed.
If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field's value to V during instance creation (§12.5).
Object Initialization
An object is initialized whenever a new object is created, typically by evaluation of a class instance creation expression. This proceeds as follows:
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
As we can see in step 3, the presence of an explicit call to the super constructor simply changes which super class constructor is invoked.
Following is an example that print the order of each step during object creation.
InstanceCreateStepTest.java:
import javax.annotation.PostConstruct;
/**
* Test steps of instance creation.
*
* #author eric
* #date Jan 7, 2018 3:31:12 AM
*/
public class InstanceCreateStepTest {
public static void main(String[] args) {
new Sub().hello();
System.out.printf("%s\n", "------------");
new Sub().hello();
}
}
class Base {
static {
System.out.printf("%s - %s - %s\n", "base", "static", "block");
}
{
System.out.printf("%s - %s - %s\n", "base", "instance", "block");
}
public Base() {
System.out.printf("%s - %s\n", "base", "constructor");
}
#PostConstruct
public void init() {
System.out.printf("%s - %s\n", "base", "PostConstruct");
}
public void hello() {
System.out.printf("%s - %s\n", "base", "method");
}
}
class Sub extends Base {
static {
System.out.printf("%s - %s - %s\n", "sub", "static", "block");
}
{
System.out.printf("%s - %s - %s\n", "sub", "instance", "block");
}
public Sub() {
System.out.printf("%s - %s\n", "sub", "constructor");
}
#PostConstruct
public void init() {
System.out.printf("%s - %s\n", "sub", "PostConstruct");
}
#Override
public void hello() {
// super.hello();
System.out.printf("%s - %s\n", "sub", "method");
}
}
Execution:
Just invoke the main method, and then check the output.
Tips:
The methods marked by #PostConstruct won't be invoked, unless you invoke it inside some container, like Spring-boot, since it depends on those containers to implement annotation like #PostConstruct.
I know static contents are loaded first in the memory, but why is 'IT' printed before 'CT' when I haven't mentioned it as static?
class Person
{
Person()
{
System.out.print(" CP");
}
static
{
System.out.print("SP");
}
}
class Teacher extends Person
{
Teacher()
{
System.out.print(" CT");
}
{
System.out.print(" IT");
}
}
public class StaticTest
{
public static void main(String[] args)
{
Person p = new Teacher();
}
}
Initializer blocks such as {System.out.print(" IT");} are executed before the constructor. Actually, they are copied to the beginning of each constructor.
Initializing Instance Members
Normally, you would put code to initialize an instance variable in a
constructor. There are two alternatives to using a constructor to
initialize instance variables: initializer blocks and final methods.
Initializer blocks for instance variables look just like static
initializer blocks, but without the static keyword:
{
// whatever code is needed for initialization goes here
}
The Java compiler copies initializer blocks into every constructor.
Therefore, this approach can be used to share a block of code between
multiple constructors.
(Source)
And to be more exact, here's the initialization order as described in the JLS :
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
Notice that the instance initializers are executed in step 4, prior to the body of the constructor (step 5).
public class SuperClass{
public SuperClass(){
System.out.println("Super class");
}
}
public class SubClass extends SuperClass{
int i;
{
i=10;
}
public SubClass(){
System.out.println("Subclass");
}
public static void main(String[] args){
SubClass sc = new SubClass();
}
}
I did a debug on all possible places and I see that first when the instance is created, it first goes to SuperClass constructor and prints Super Class and only then it comes to the instance variable and then the initialization block and then lastly SubClass constructor and prints SubClass.
But, I had read somewhere that inside the subclass constructor is where super() is called by default and that is when it goes to the superclass constructor!!!
SubClass() constructor implicitly looks like this :
public SubClass(){
super();
{
i=10;
}
System.our.println("Subclass");
}
As per the JLS section 12.5 :
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
Well, that's because your SubClass constructor actually looks like this:
public SubClass() {
super();
{
i=10;
}
System.out.println("Subclass");
}
If you don't call the super(); explicitly, compiler will do it for you implicitly. That means that the SuperClass constructor is called first and THEN the `System.out.println("Subclass");
Also, the super() must be the first statement in the constructor.
You can check all of this by making the constructor of the SuperClass private (and in separate file, too). The compiler will show you an error in the SubClass constructor saying that there is no default constructor in the SuperClass.
A constructor always calls super(). That is implicit.
Read the documentation.
If a constructor does not explicitly invoke a superclass constructor,
the Java compiler automatically inserts a call to the no-argument
constructor of the superclass. If the super class does not have a
no-argument constructor, you will get a compile-time error. Object
does have such a constructor, so if Object is the only superclass,
there is no problem.