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.
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;
}
So i was playing around in java once again and while doing so, i ran into an interesting problem.
I was trying to write my self a little registration service. If an object of a certain type is instantiated via its constructor it would retrieve an id from my registration service and is added to the services list of objects.
Here is an example object
public class Test {
private final Long id;
public Test() {
this.id = TestRegistration.register(this);
throw new IllegalAccessError();
}
public Long getId() {
return this.id;
}
}
And here is an example service
public class TestRegistration {
private final static Map<Long, Test> registration = new HashMap<>();
protected final static long register(final Test pTest) {
if (pTest.getId() != null) {
throw new IllegalStateException();
}
long freeId = 0;
while (registration.containsKey(freeId)) {
freeId = freeId + 1;
}
registration.put(freeId, pTest);
return freeId;
}
protected final static Test get(final long pId) {
return registration.get(pId);
}
}
Now as you can see, the constructor of my class Test can't successfully execute at all since it always throws an IllegalAccessError. But the method register(...) of my TestRegistration class is called in the constructor before this error is thrown. And inside this method it is added to the registrations Map. So if i would run for example this code
public static void main(final String[] args) {
try {
final Test t = new Test();
} catch (final IllegalAccessError e) {
}
final Test t2 = TestRegistration.get(0);
System.out.println(t2);
}
My TestRegistration does in fact contain the object created when the constructor of my Test class was called and i can (here using variable t2) access it, even tho it wasn't successfully created in the first place.
My question is, can i somehow detect from within my TestRegistration class if the constructor of Test was executed successfully without any exceptions or other interruptions?
And before you ask why i throw this exception in the first place here is my answer. Test may have potential subclasses i don't know of yet but still will be registered in my TestRegistration class. But since i don't have influence on the structure of those subclasses i can't tell if their constructor will throw an exception or not.
It does not appear to be possible.
I've included the best I can gather from the JLS below.
As can be seen from that, the execution of the body of the constructor is the very last step in the creation of an object.
Thus I don't see any evidence to suggest that raising an exception would result in anything not happening which you could've checked to see whether the execution was successful (apart from the rest of the body of the constructor, obviously).
This may not seem particularly convincing, although I'm not convinced that you'd find anything more concrete than this (it is, after all, notoriously difficult to prove that something doesn't exist).
I would recommend putting the logic that modifies external data somewhere else.
It wouldn't be entirely unreasonable to require that a register method (either in the TestRegistration class, or in the object itself) be explicitly called after construction of an object.
It could also work to, instead of calling the constructors directly, have a class containing methods to return objects (which sounds a lot like the Factory Design Pattern), where you can then put this external modification logic.
From the JLS:
15.9.4. Run-Time Evaluation of Class Instance Creation Expressions
At run time, evaluation of a class instance creation expression is as follows.
First, if the class instance creation expression is a qualified class instance creation expression, the qualifying primary expression is evaluated. If the qualifying expression evaluates to null, a NullPointerException is raised, and the class instance creation expression completes abruptly. If the qualifying expression completes abruptly, the class instance creation expression completes abruptly for the same reason.
Next, space is allocated for the new class instance. If there is insufficient space to allocate the object, evaluation of the class instance creation expression completes abruptly by throwing an OutOfMemoryError.
The new object contains new instances of all the fields declared in the specified class type and all its superclasses. As each new field instance is created, it is initialized to its default value (§4.12.5).
Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.
Next, the selected constructor of the specified class type is invoked. This results in invoking at least one constructor for each superclass of the class type. This process can be directed by explicit constructor invocation statements (§8.8) and is specified in detail in §12.5.
The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created.
12.5. Creation of New Class Instances
...
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.
Weather or not the constructor comples does not influence whether or not the object is created. All instance variables will be created the super() will still be called and memory will still be allocated.
In reality the exception will always be thrown, every object in your registration service will have thrown an exception. As children will have to call super() in order to be registered and following registration an exception is thrown.
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.
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.