Will Java Final variables have default values? - java

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.

Related

final variable initialized first

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.

why static block cannot access the static variable defined after it

I've checked Forward References During Field Initialization and this the answer from #assylias, but still I got no answer to the why.
Why a static block can assign the static variable declared after it but can NOT access it?
class Parent {
static {
i = 2; // valid
// can only assign new value to it instead of accessing it?
// System.out.println(i); // invalid - compile-error
}
static int i = 0;
static {
i = 3; // valid
}
}
Is it due to the fact: the value is not initialized yet, so we just explicitly inhibit you from using it? or there are something related to security I don't know?
updated
this is not a duplicate of that problem which is about
Why this doesn't happen when accessing with class name?
This question is about why we have this design? for what purpose?
Static fields are initialized based on the order they appear in the code.
So, when you assign a value to i variable you just say to the compiler: "Hey, guy, when you get to initialize this variable, set its value to...". But you can not use it until it's initialized because it simply does not exist yet.
UPDATE:
As it says in the book "The Java Language Specification" by James Gosling, Bill Joy, Guy Steele and Gilad Bracha:
These restrictions are designed to catch, at compile time, circular or
otherwise malformed initializations.
Consider this:
static {
i = 2;
j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;
Should j variable be 7 or 15?
If it's 7, then we have initialized i variable twice, which is not possible, since the field is static. If it's 15 then what does i = 2; mean?
This code is ambiguous, so Java Specification does not allow to do that.
After some further reading, I think Pavel is not quite accurate in this point as #Holger pointed out in the comment.
we have initialized i variable twice, which is not possible, since the field is static.
As the 12.4.2. Detailed Initialization Procedure points out
For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation.
I suppose the initialization twice is okay to the class initializer itself as long as it's just once to the invoking clients.
But the demos Pavel provided still stands its position so I basically just reuse it here but with different explanation.
static {
i = 2;
j = i + 5;
// no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;
But when you use MyClass.i directly in j = MyClass.i + 5, the compiler would know it would then be okay as 8.3.3. Forward References During Field Initialization detailed with four conditions.
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.
And there is a detailed discussion in this answer already.
To wind it up, I think this would be for predictable behavior to add these restrictions. Once again, the other official purpose pointed out in 8.3.3. Forward References During Field Initialization.
These restrictions are designed to catch, at compile time, circular or otherwise malformed initializations.
The same is also applicable to the non-static members. You can assign values to them in instance initializer block but can not use them before initialization.
class Parent {
{
x = 3; // works fine
// System.out.println(x); // gives compilation error.
}
int x = 0;
public static void main(String[] args) {
}
}

When a static variable is in RIWO state, can it be accessed directly ?

If a static variable is in RIWO (Read Indirectly Write Only) state. the static variable can not be accessed directly.
here is the code
class Test {
static{
System.out.println(x);
}
static int x = 10;
public static void main(String[] args) {
}
}
in this case illegal forward reference compile time error is coming.
but if you are using the class name to access the static variable, it can be accessed.
here is the code example
class Test {
static{
System.out.println(Test.x);
}
static int x = 10;
public static void main(String[] args) {
}
}
answer is : 0
how is this possible ? isn't this a direct access ?
As per JLS 12.4.1. When Initialization Occurs and this answer:
a class's static initialization normally happens immediately before the first time one of the following events occur:
an instance of the class is created
a static method of the class is invoked
a static field of the class is assigned
a non-constant static field is used
In your case reading Test.x falls under point 4, a non-constant static field. Your code reads and output 0 which is the default int value, however you change it by marking the field final as
static {
System.out.println(Test.x);
}
final static int x = 10;
The code will now output 10. You can see it by comparing output of javap -c Test.class for both non-final and final field cases, the order of bytecode will be reversed around:
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: bipush 10
To me it looks like the Java compiler's forward reference error is a workaround for gotchas in static initialization in JVM.
This could be a java compiler bug.
In your first case, compiler successfully find out the bad usage.
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.
Use the variable before declare it of course should be forbidden.
In your second case, unfortunately, compiler don't find the bad usage.
The following things happens step by step:
JVM load the Test class, now Test.class object is accessable
Someone trigger the Test class static's initlization
JVM run in textual order, the most front static block will be invoked first
JVM try to get the Test.x. Since it's not constant, jvm go to METASPACE to get the value. int is 0 by default. So you get 0 printed.
JVM run the next line x = 10, set the data into METASPACE
By the way, Karol's answer has a little mistake. Only add final is not sufficient, the other necessary condition is it's really constant. The following code will still print 0.
static {
System.out.println(JustTest.x);
}
static final int x = Integer.valueOf(1);

Java Compile Time error for instance level Final variables and not giving for method level local variables [duplicate]

This question already has answers here:
Why Final variable doesn't require initialization in main method in java?
(4 answers)
Closed 5 years ago.
Why is the java compiler not giving any compile time error for method level local final variables when we not initialize them?
( but it is giving en error for instance level final variables when they are not initialized ).
public class FinalKeyword
{
final int j; // error: the blank final field j may not have been initialized.
public static void main(String[] args)
{
final int k; // not giving any Compile time error!
}
}
Thing is: as soon as you change your main() method to
final int k;//Not Giving any Compile time error
System.out.println(k); //different story now!
You get an error about k not being initialized, too!
The point is:
the compiler has to make sure that other source code that isn't visible right now ... is able to do a new FinalKeyword() without problems. Thus it can't allow you to keep j not initialized.
but that main() method ... even when another method invokes this main() method - that is fine. The method does nothing! In other words - it is not a problem to define a variable that doesn't get used within a method! Because there is no way how you could get to that variable!
The compiler has to prevent you from getting into situations where you "stumble" over an uninitialized variable. When you invoke a method that has such variables ... but never uses them - that is simply a "don't care".
And surprise: when we go with
public class FinalTest {
public static void main(String args[]) {
final int k;
}
}
and compile that; and then we use javap to get to de-compiled byte code:
public static void main(java.lang.String[]);
Code:
0: return
This is one of the rare occasions where javac does a bit of optimization - by simply throwing away unused variables. Bonus fun fact: even when changing my example to k=5; - you will find that the class file still only contains return!
j is a field of the class. k is not. You need to initialise final fields in the class's constructor. Your class is using your default, implicit constructor which does no initialisation of fields.
The reason it doesn't give you a warning for the local variable is because you don't use it. If you were to use the uninitialised local variable, you would get an error:
public static void main(String[] args)
{
final int k;
if (k < 5) {
// whatever
}
}
As to why the compiler's not clever enough to know that you're not using the field either, I'm not sure. It's presumably more complex because of inheritance, access levels and such.
In general, if the compiler comes across a situation where a final may be accessed while uninitialized, or set while initialized, there is an error.
A class's final fields must be initialized, because someone could access them at any time. If it is package-private, protected, or public, other code can directly access the field, and they expect it to be initialized. Even if the field is private, someone could reflect it and try to access it, so the compiler must not ignore it.
A method's final locals are internal to the method, and there is no way to reflect inside a method without modifying some bytecode, so the compiler does not need to be so strict. Instead, the compiler does control flow analysis to map out when the field will be assigned and won't be assigned, and it will throw an error if it is used while maybe unassigned, and will be content if it is never used or is used while definitely assigned.
void meth0() {
final String s;
// Compiler: s is never used; I don't care.
}
void meth1() {
final String s;
// s is definitely unassigned
if(something()) {
s = "something";
// s is definitely assigned
System.out.println(s);
} else {
// s is definitely unassigned
s = "other";
// s is definitely assigned
System.out.println(s);
}
// if-branch def. assigns s, else-branch def. assigns s
// Ergo: s is def. assigned
somethingElse(s);
// Compiler: control flow checks out; code is valid
}
void meth2() {
final String s;
// def. unass.
if(something()) {
s = "something";
// def ass.
}
// s is in undetermined state
// s = ""; // s may be assigned; error
// use(s); // s may not be assigned; error
// s is now totally unusable. Nice job breaking it, hero!
}

Ordering of instance variable initializers

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

Categories

Resources