It seems intuitively clear that in Java, instance variable intitializers are executed in the order in which they appear in the class declaration.
This certainly appears to be the case in the JDK I am using. For example, the following:
public class Clazz {
int x = 42;
int y = this.z;
int z = this.x;
void print() {
System.out.printf("%d %d %d\n", x, y, z);
}
public static void main(String[] args) {
new Clazz().print();
}
}
prints 42 0 42 (in other words, y picks up the default value of z).
Is this ordering actually guaranteed? I've been looking through the JLS, and can't find any explicit confirmation.
Yes, it is.
The se7 JLS covers instance variable initialization order in the 12.5 Execution section:
...
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.
...
the JLS for Java 5 mentions in the "Classes" section:
The static initializers and class variable initializers are executed
in textual order.
yes the variables initialisation in the class are executed in the same order. So in your second line y takes the default value o of z since z is not initialised
Related
I was reading the question here : Java : in what order are static final fields initialized?
According to the answer
"except that final class variables and fields of interfaces whose
values are compile-time constants are initialized first ..."
I think this is not correct because the following will fail :
static {
String y = x;
}
public static final String x = "test";
In the static block, x is not recognized. Can anyone please comment if that answer is correct ?
The order of initialization doesn't change the fact that the JLS doesn't let you refer to variables before they're declared in various cases. This is described in JLS§8.3.3:
Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope (§6.3). Specifically, it is a compile-time error if all of the following are true:
The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
That's why your code gets this compilation erorr:
error: illegal forward reference
The statement that static fields that are constant variables are initialized first is indeed defined in JLS§12.4.2:
Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.
Then, initialize the static fields of C which are constant variables (§4.12.4, §8.3.2, §9.3.1).
...
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
As you can see, constant variables are initialized in step 6, but others in step 9.
This demonstrates the behavior:
public class Example {
static String y;
static {
y = foo();
}
static String foo() {
return x.toUpperCase();
}
public static final String x = "test";
public static void main(String[] args) throws Exception {
System.out.println(x);
System.out.println(y);
}
}
That compiles, and outputs:
test
TEST
In contast, if you change the x line so it's not constant anymore:
public static final String x = Math.random() < 0.5 ? "test" : "ing";
It compiles, but then fails because x is null as of y = foo();.
For the avoidance of doubt: I don't recommend using methods to initialize fields like that. :-)
As this answer states:
... they are initialized in the order in which they appear in the source.
You are absolutely right, your example fails, because you are trying to use a variable that is declared after the usage. Since static block are executed in order of the source code, you are absolutely correct and should suggest and edit for that answer, because this statement is invalid:
except that final class variables ... are initialized first.
public class Sequence {
Sequence() {
System.out.print("c ");
}
{
System.out.print("y ");
}
public static void main(String[] args) {
new Sequence().go();
}
void go() {
System.out.print("g ");
}
static {
System.out.print("x ");
}
}
What is the result?
A) c x y g
B) c g x y
C) x c y g
D) x y c g
E) y x c g
F) y c g x
This is Oracle certification question and answer is option D.I did not understand the answer.I thought option C is correct.Can anyone explain why the answer is option D and not option C?
Basically, it's just a matter of reading through JLS 12.5 (Creation of New Class Instances) carefully.
In particular, note the order of:
Execute the instance initializers and instance variable initializers for this class [...]
Execute the rest of the body of this constructor. [ ... ]
y is printed by the instance initializer; c is printed by the body of the constructor - therefore y is printed first.
The order of execution of java class goes as follows.
Static block initialization because it goes before object creation.
Initialization block of your object
Parent constructor (not listed in your code)
Constructor
Method called afterward
You have a static and an instance initializer in this code block.
In sequence, the static initializers are run first, then the instance initializers when an instance of the object is created. This is specified by JLS 12.4.2 and JLS 12.5:
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
A new class instance is explicitly created when evaluation of a class instance creation expression (§15.9) causes a class to be instantiated.
... 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.
To break it down a bit more:
By virtue of the class being loaded, "x" is printed first.
By initializing an instance of Sequence, "y" is printed next.
By calling the no-arg constructor of Sequence, "c" is printed third.
By invoking the go method, "g" is printed last.
1.static block is executed before main method at the time of classloading.
static {
System.out.print("x "); }
}
2.The instance initializer block is created when instance of the class is created.
{
System.out.print("y ");
}
3.The instance initializer block is invoked after the parent class constructor is invoked
Sequence(){
System.out.print("c ");
}
Before java 8 an inner class could access outer objects only if they were declared final.
However now when I run example code (from below) on javaSE 1.8 there is no compilation error and program runs fine.
Why did they change that and how does It work now?
Example code from java 7 tutorial:
public class MOuter {
private int m = (int) (Math.random() * 100);
public static void main(String[] args) {
MOuter that = new MOuter();
that.go((int) (Math.random() * 100), (int) (Math.random() * 100));
}
public void go(int x, final int y){
int a = x + y;
final int b = x - y;
class MInner{
public void method(){
System.out.println("m is "+m);
System.out.println("x is "+x); // supposedly illegal - 'x' not final
System.out.println("y is: "+y);
System.out.println("a is "+a); // supposedly illegal? - 'a' not final
}
}
MInner that = new MInner();
that.method();
}
}
In Java 7 the the concept of effectively final was introduced to support the "more precise rethrow" feature, and its scope was expanded in Java 8 to cover local variables that are assigned only once, but not actually declared final. These can be captured and used in lambda bodies or inner classes just as if they were declared final.
This is covered in section §4.12.4 of the Java Language Specification:
Certain variables that are not declared final may instead be
considered effectively final.
A local variable or a method, constructor, lambda, or exception
parameter is effectively final if it is not declared final but it
never occurs as the left hand operand of an assignment operator
(§15.26) or as the operand of a prefix or postfix increment or
decrement operator (§15.14, §15.15).
In addition, a local variable whose declaration lacks an initializer
is effectively final if all of the following are true:
It is not declared final.
Whenever it occurs as the left-hand operand of an assignment
operator, it is definitely unassigned and not definitely assigned
before the assignment; that is, it is definitely unassigned and not
definitely assigned after the right-hand operand of the assignment
(§16 (Definite Assignment)).
It never occurs as the operand of a prefix or postfix increment or
decrement operator.
If a variable is effectively final, adding the final modifier to its
declaration will not introduce any compile-time errors. Conversely, a
local variable or parameter that is declared final in a valid program
becomes effectively final if the final modifier is removed.
It's still the same rule, except the compiler doesn't force you to explicitely define the variable as final anymore. If it is effectively final, you can access it. If it's not (i.e. the compiler detects that the variable is reassigned), then it doesn't compile.
I have a program like this:
class Test {
final int x;
{
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??
"Here x is 0".
Will final variables have dafault values?
if I change my code like this,
class Test {
final int x;
{
printX();
x = 7;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
I am getting output as:
Here x is 0
Here x is 7
const called
Can anyone please explain this behavior..
http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html, chapter "Initializing Instance Members":
The Java compiler copies initializer blocks into every constructor.
That is to say:
{
printX();
}
Test() {
System.out.println("const called");
}
behaves exactly like:
Test() {
printX();
System.out.println("const called");
}
As you can thus see, once an instance has been created, the final field has not been definitely assigned, while (from http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):
A blank final instance variable must be definitely assigned at
the end of every constructor of the class in which it is
declared; otherwise a compile-time error occurs.
While it does not seem to be stated explitely in the docs (at least I have not been able to find it), a final field must temporary take its default value before the end of the constructor, so that it has a predictable value if you read it before its assignment.
Default values: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
On your second snippet, x is initialized on instance creation, so the compiler does not complain:
Test() {
printX();
x = 7;
printX();
System.out.println("const called");
}
Also note that the following approach doesn't work. Using default value of final variable is only allowed through a method.
Test() {
System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
x = 7;
System.out.println("Here x is " + x);
System.out.println("const called");
}
JLS is saying that you must assign the default value to blank final instance variable in constructor (or in initialization block which is pretty the same). That is why you get the error in the first case. However it doesn't say that you can not access it in constructor before. Looks weird a little bit, but you can access it before assignment and see default value for int - 0.
UPD. As mentioned by #I4mpi, JLS defines the rule that each value should be definitely assigned before any access:
Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.
However, it also has an interesting rule in regards to constructors and fields:
If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.
So in second case the value x is definitely assigned at the beginning of the constructor, because it contains the assignment at the end of it.
If you don't initialize x you'll get a compile-time error since x is never initialized.
Declaring x as final means that it can be initialized only in the constructor or in initializer-block (since this block will be copied by the compiler into every constructor).
The reason that you get 0 printed out before the variable is initialized is due to the behavior defined in the manual (see: "Default Values" section):
Default Values
It's not always necessary to assign a value when a field is declared.
Fields that are declared but not initialized will be set to a
reasonable default by the compiler. Generally speaking, this default
will be zero or null, depending on the data type. Relying on such
default values, however, is generally considered bad programming
style.
The following chart summarizes the default values for the above data
types.
Data Type Default Value (for fields)
--------------------------------------
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
String (or any object) null
boolean false
The first error is the compiler complaining that you have a final field, but no code to initialize it - simple enough.
In the second example, you have code to assign it a value, but the sequence of execution means you reference the field both before and after assigning it.
The pre-assigned value of any field is the default value.
All non-final fields of a class initialize to a default value (0 for nummeric data types, false for boolean and null for reference types, sometimes called complex objects). These fields initialize before a constructor (or instance initialization block) executes independent of whether or not the fields was declared before or after the constructor.
Final fields of a class has no default value and must be explicitly initialized just once before a class constructor has finished his job.
Local variables on the inside of an execution block (for example, a method) has no default value. These fields must be explicitly initialized before their first use and it doesn't matter whether or not the local variable is marked as final.
Let me put it in the simplest words I can.
final variables need to be initialized, this is mandated by the Language Specification.
Having said that, please note that it is not necessary to initialize it at the time of declaration.
It is required to initialize that before the object is initialized.
We can use initializer blocks to initialize the final variables. Now, initializer blocks are of two types
static and non-static
The block you used is a non-static initializer block. So, when you create an object, Runtime will invoke constructor and which in turn will invoke the constructor of the parent class.
After that, it will invoke all the initializers (in your case the non-static initializer).
In your question, case 1: Even after the completion of initializer block the final variable remains un-initialized, which is an error compiler will detect.
In case 2: The initializer will initialize the final variable, hence the compiler knows that before the object is initialized, the final is already initialized. Hence, it will not complain.
Now the question is, why does x takes a zero. The reason here is that compiler already knows that there is no error and so upon invocation of init method all the finals will be initialized to defaults, and a flag set that they can change upon actual assignment statement similar to x=7.
See the init invocation below:
As far as I'm aware, the compiler will always initialize class variables to default values (even final variables). For example, if you were to initialize an int to itself, the int would be set to its default of 0. See below:
class Test {
final int x;
{
printX();
x = this.x;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
The above would print the following:
Here x is 0
Here x is 0
const called
If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??
"Here x is 0".
No. You are not seeing that output because you are getting a compile-time error in the first place. Final variables do get a default value, but the Java Language Specification (JLS) requires you to initialize them by the end of the constructor (LE: I'm including initialization blocks here), otherwise you'll get a compile-time error which will prevent your code to be compiled and executed.
Your second example respects the requirement, that's why (1) your code compiles and (2) you get the expected behaviour.
In the future try to familiarize yourself with the JLS. There's no better source of information about the Java language.
I am getting confused with below code I expected that it will give an error or answer will be 10 but it is giving 20 how?
public class test {
public static void main(String[] args) {
System.out.println(x);
}
static{
x=10;
}
static int x=20;
}
It's specified in section 12.4.2 of the JLS, which gives details of class initialization:
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
The variable initializer (x = 20) occurs after the static initializer (the block containing x = 10) in the program text. The value at the end of initialization is therefore 20.
If you swap the order round so that the variable initializer comes first, you'll see 10 instead.
I'd strongly advise you to avoid writing code which relies on textual ordering if possible though.
EDIT: The variable can still be used in the static initializer because it's in scope - just like you can use an instance variable in a method declared earlier than the variable. However, section 8.3.2.3 gives some restrictions on this:
The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively static) field of a class or interface C and all of the following conditions hold:
The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
The usage is not on the left hand side of an assignment.
The usage is via a simple name.
C is the innermost class or interface enclosing the usage.
It is a compile-time error if any of the four requirements above are not met.
So if you change your static initializer to:
static {
System.out.println(x);
}
then you'll get an error.
Your existing static initializer uses x in a way which does comply with all of the restrictions, however.
in static if a value is changed once then it will be effected through out.
So you are getting 20.
if you write this way
public class test {
static int x=20;
public static void main(String[] args) {
System.out.println(x);
}
static{
x=10;
}
}
then it will print 10.