Two Laptop Bag*
The moose likes Programmer Certification (SCJP/OCPJP) and the fly likes initialization of final instance fields Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


JavaRanch » Java Forums » Certification » Programmer Certification (SCJP/OCPJP)
Bookmark "initialization of final instance fields" Watch "initialization of final instance fields" New topic
Author

initialization of final instance fields

Xiaolong KONG
Greenhorn

Joined: Mar 15, 2004
Posts: 4
I'm quite surprised by the result of the following program:


-------------
Result:
A.newInstance() : i=1 j=0
un()
B.newInstance() : i=1 j=1


Can anyone explain that ? And if possible, the reference to the JLS please.

Thanks in advance.
[ February 23, 2005: Message edited by: Mark Spritzler ]
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17250
    
    6

First, let's put some CODE tags around your code so that we can read it with correct indentation.

the CODE tags are created when you click the CODE button which is among the buttons under the Add Reply button

Mark


Perfect World Programming, LLC - Two Laptop Bag - Tube Organizer
How to Ask Questions the Smart Way FAQ
Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
Hello.
Very good example.

First, here is your link: http://java.sun.com/docs/books/jls/index.html

Second, about the program.
There is exact explanation for instantiation in Java Language Spec.
(paragraph 12.5 (page 242 in PDF))

1. Memory is allocated.
2a. If explicit constructor of superclass presents (super()) then it is called
2b. If there is no explicit constructor then implicit constructor is called
(default constructor: super())
3. All instance variables initializers are called
(note, that static initializers are called during class loading)
4. Rest of the body of the constructor is executed.

note that UN is known at compile time
and un() is not (it is method !)
so during memory allocation i is initialized with UN=1
and j is initialized with default =0

when you call B() in main()
you execute following
1. execute A()
2. initialize i and j
3. execute B()

and the output confirms that
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
You have made an observation that yields a result due to the invocation of an overrideable method on a 'this' reference.
Doing so is incredibly poor form, albeit common.

Here's some more:
http://www.xdweb.net/~dibblego/java/trivia/answers.html#q4


Tony Morris
Java Q&A (FAQ, Trivia)
Xiaolong KONG
Greenhorn

Joined: Mar 15, 2004
Posts: 4
Thanks for the code formating and the replies.

As you cited, according to the JLS, "All instance variables initializers are called" after the call super(). As instance field initializers, both UN (while runtime, it's effectively 1) and un() should be assigned to i or j AFTER the call to A(). However it's not what the program does.

In fact, if we remove the modifier final before the instance field i, we will get 0, 0, 1, 1. So does it mean that the modifier final modifies the initialization ordre of the instance fields ? If so, why it doesn't call un() for the field j before super() in our case?

It's surely not a good example for coding practice, but to better understand the JLS.
Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
When I said good example, I didn't mean coding practice.
It is good example of question to prepair for Programmer Exam.

May be my explanation of why i=1 was too short.
I wrote:

note that UN is known at compile time
and un() is not (it is method !)
so during memory allocation i is initialized with UN=1
and j is initialized with default =0

You should have good understanding of what is "instance variable initializer" and what is not.
In our case:

is initializer, but:

is not initializer, because i is final and UN is compile time constant.
You can find exect definition of instance (and static) variable initializers in JLS (probably somewhere in "Statements" or "Expressions" parts).
Xiaolong KONG
Greenhorn

Joined: Mar 15, 2004
Posts: 4
Hi, Evgeni,

I agree with you that it's a good example for the SCJP, it's also why I post it in this forum.

In you last response, you missed the point of "instance variable initializer" yourself.


and


are both instance variable initializers, as indicated in the �8.6 of JLS.

In fact, the key point in this example is the keyword final. If we remove it, even

is not invoked before the call to super() and the instance variable i will has its default value: 0.

Does anyone have an idea about the initialization of final instance fields?
Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
Hello,
I made a mistake, it's so sad
Thank you Xiaolong for noticing it.
is initializer.
And it must be called after A().

May be it is some kind of optimization
UN is compile time constant, and i is final.
May be compiler hints to JVM to initialize i with 1 and not with default 0.
It makes sense in every case except our example.
And test shows that it happens on practice.

Do one more test with the program.
replace line with
then add to be first line of constructor B().
Class B now looks like this:

Compile it and run.
It prints i=0, j=0 for A and i=1, j=1 for B.

Note that i is still final here!
Interesting, we use final variable i even before it is assigned!
(check chapter 16 in JLS)

Where did you get this example? Is there anything else as good as this one?
[ February 24, 2005: Message edited by: Evgeni Shuster ]
Animesh Shrivastava
Ranch Hand

Joined: Jul 19, 2004
Posts: 298
Go thru the link in JLS
here

It says that "static variables that are final and that are initialized with compile-time constant values are initialized first".

But when u change the value not to be a final. It doesnt get initialized during the Class initialization and so the value is not set even though it points to a compile time constant.
Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
to Animesh
Nobody has problem with STATIC and final variables.
UN is static and final and it is initialized with compile time constant 1.
But i is NOT static, so why i is initialized before constructor A() ?
And how it is possible to use final i in second test even BEFORE the value is assigned to it?
Mike Gershman
Ranch Hand

Joined: Mar 13, 2004
Posts: 1272
why i is initialized before constructor A()

A class's initializers are executed before its constructors (not counting the first lines of the constructors).


Mike Gershman
SCJP 1.4, SCWCD in process
Alton Hernandez
Ranch Hand

Joined: May 30, 2003
Posts: 443
Originally posted by Mike Gershman:

A class's initializers are executed before its constructors (not counting the first lines of the constructors).


Yes, but i is not a class variable.

A blank final instance variable is allowed as long as it is assigned at the end of the constructor. And in the modified version of program B, the variable i, which is an instance variable of class B (NOT A), is assigned.
[ February 25, 2005: Message edited by: Alton Hernandez ]
Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
to Mike

i is not class (=static) variable, it is an instance variable.

to Alton

According to the chapter 16 in JLS:
blank final field must have definitly assigned value when any access of it's value occurs
in modified version we access value of i ( in constructor A() ) before we assign value to it ( in constructor B() )

I still think, the reason for wrong behaviour is somewhere in implementation of JVM (some kind of optimization or just bug
[ February 25, 2005: Message edited by: Evgeni Shuster ]
Mike Gershman
Ranch Hand

Joined: Mar 13, 2004
Posts: 1272
I apologize for my ambiguous language. What I should have said is:

When an object is being instantiated, the order of execution is:
1. All its instance variables are assigned their default values (0, false, or null).

2. instance variable initialization expressions and instance initialization blocks, in the order they appear in the source code.

3. the constructors, starting after the first (super() or this()) line, in the reverse order of call.
[ February 25, 2005: Message edited by: Mike Gershman ]
Alton Hernandez
Ranch Hand

Joined: May 30, 2003
Posts: 443
Originally posted by Evgeni Shuster:

to Alton
According to the chapter 16 in JLS:
blank final field must have definitly assigned value when any access of it's value occurs
in modified version we access value of i ( in constructor A() ) before we assign value to it ( in constructor B() )

I still think, the reason for wrong behaviour is somewhere in implementation of JVM (some kind of optimization or just bug
[ February 25, 2005: Message edited by: Evgeni Shuster ]


The specification is not violated if you see i as part of an instance of B(), and not of A().

You must also differentiate between a default initial value from a proper initial value. A blank final variable will be initialized to a default initial value during the initialization phase. But it needs a proper initial value before its used because it is deemed as constant

Here, try this modified A class:

Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
to Alton
According to JLS here
I will copy it for convenience:

Each local variable (�14.4) and every blank final (�4.5.4) field (�8.3.1.2) must have a definitely assigned value when any access of its value occurs. A Java compiler must carry out a specific conservative flow analysis to make sure that, for every access of a local variable or blank final field f, f is definitely assigned before the access; otherwise a compile-time error must occur.


I understand your point.
But I have few more questions.

i is variable of B and is accessed in A, so you allow this
(it's ok, couse JVM allows this too )
then, with some use of polymorphism, we have execution flow that accesses i
A.main() --> B() --> A() --> B.getI()
I think this must be forbidden (as I understand JLS)

lets do one more test
1. add following method to A

2. add following method to B

3. add following line to be first line in constructor B()


Now the program looks like:


It prints:
A.newInstance() : i=0 j=0
un()
i = 0
B.newInstance() : i=1 j=1


here I access i in B before assigning a value to it,
and this looks even more forbidden than accessing it in A

How does this happen?

And one more question: Why in the original example
(posted by Xiaolong KONG) i gets value before call to constructor A() ?
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
The important point here is that calling an overrideable method on a 'this' reference from a constructor is pure evil.

Bloch also makes mention of this in "Effective Java" iirc.
Animesh Shrivastava
Ranch Hand

Joined: Jul 19, 2004
Posts: 298
I agree with what Alton says

You must also differentiate between a default initial value from a proper initial value. A blank final variable will be initialized to a default initial value during the initialization phase. But it needs a proper initial value before its used because it is deemed as constant


By Evgeni

here I access i in B before assigning a value to it,
and this looks even more forbidden than accessing it in A


The above statement by Alton makes it clear that a blank final variable will be initialized with a defualt value(0 here). But it needs a proper initial value. So whatever way u access it, "i" has the defualt value 0 before its explicit initialization.
Evgeni Shuster
Greenhorn

Joined: Feb 13, 2005
Posts: 15
I perfectly understand what Alton says.
I just say that it should not work this way.
In first example instance variable initializer executed before super constructor (must be after, according to JLS).
In second and third examples final field accessed before a value is assigned to it. (final field must have PROPER value before first access to it)
It is impossible to do thease things without using super constructor.

Note that Alton says:... it needs a proper initial value before its used ...
He just allows use of i in A, because i is not part of A.
But this allows access to initial (not proper) value of i from B.
But really there is nothing to do here.
Just to learn this problem and take care in our own programs.

to Tommy
All thease is not more evil than your changing of immutable Strings inside JVM. Dangers of such tricks were clear from the begginning.
But thank you for mentioning "Effective Java".
Alton Hernandez
Ranch Hand

Joined: May 30, 2003
Posts: 443
Originally posted by Evgeni Shuster:
I perfectly understand what Alton says.
I just say that it should not work this way.
In first example instance variable initializer executed before super constructor (must be after, according to JLS).
In second and third examples final field accessed before a value is assigned to it. (final field must have PROPER value before first access to it)
It is impossible to do thease things without using super constructor.


Hi Evgeni,

To further understand this, let us simplify the program. First, create two files, A.java amd B.java:


If you run and compile all these program, you will get an output of:
A() getI():0
B() getI():0

If you remove line 2 in B, the program will not compile simply because i must be definitely assigned before the end of every constructor.

Now, modify A and change any reference to getI() to getII(), like this:
A.java
class A {
int i=3;
int getII() {return i;}
A() {
System.out.println("A() getII():" + getII());
}
}
[/code]


Now compile A.java only and then run B. Your output will come out like this:
A() getI():3
B() getI():0

The point here is that the call by the A class to the overridden getI() function is done on runtime. So when you compile B(), the compiler has no knowledge that a reference to a blank final variable is being called from A.
 
GeeCON Prague 2014
 
subject: initialization of final instance fields