This question already has answers here:
Uninitialized class members in Java do not issue any compiler errors. local variables however do. Why?
(5 answers)
Closed 6 years ago.
Why do variables declared in a class have default values, but the variables declared inside methods, said to be "local variables", don't have default values in Java?
For example
class abc
{
int a;
public static void main(String ss[])
{
int b;
abc aa=new abc();
System.out.println(aa.a);
System.out.println(b);
}
}
In this above example variable a has default value of 0 but variable b gives error that it might not have been initialized.
All member variable have to load into heap so they have to initialized with default values when an instance of class is created. In case of local variables, they don't get loaded into heap they are stored in stack until they are being used before java 7, so we need to explicitly initialize them.
Now the "Java Hotspot Server Compiler" performs "escape analysis" and decides to allocate some variables on the stack instead of the heap.
Local variables Initialization
Variables declared in methods and in blocks are called local variables. Local variable are not initialized when they are created at method invocation. Therefore, a local variable must be initialized explicitly before being used. Otherwise the compiler will flag it as error when the containing method or block is executed.
Example:
public class SomeClassName{
public static void main(String args[]){
int total;
System.out.println("The incremented total is " + total + 3); //(1)
}
}
The compiler complains that the local variable total used in println statement at (1) may not be initialized.
Initializing the local variable total before usage solves the problem:
public class SomeClassName{
public static void main(String args[]){
int total = 45; //Local variable initialized with value 45 System.out.println("The incremented total is " + total+ 3); //(1)
}
}
Fields initialization
If no initialization is provided for an instance or static variable, either when declared or in an initializer block, then it is implicitly initialized with the default value of its type.
An instance variable is initialized with the default value of its type each time the class is instantiated, that is for every object created from the class.
A static variable is initialized with the default value of its type when the class is first loaded.
As local variables are allocated on stack, memory chunk for a local variable is allocated when it is assigned with a value.
Take simple example
class Abc {
int i = -111;
int e;
int doSomething() {
int a = 10;
int b = a + i;
int c = b + 100;
Abc d = new Abc();
e = b + c + d.a;
return e + 1000;
}
}
and the bytecode from javap -c Abc
Compiled from "Abc.java"
class Abc {
int i;
int e;
Abc();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush -111
7: putfield #2 // Field i:I
10: return
int doSomething();
Code:
0: bipush 10
2: istore_1
3: iload_1
4: aload_0
5: getfield #2 // Field i:I
8: iadd
9: istore_2
10: iload_2
11: bipush 100
13: iadd
14: istore_3
15: new #3 // class Abc
18: dup
19: invokespecial #4 // Method "<init>":()V
22: astore 4
24: aload_0
25: iload_2
26: iload_3
27: iadd
28: aload 4
30: getfield #2 // Field i:I
33: iadd
34: putfield #5 // Field e:I
37: aload_0
38: getfield #5 // Field e:I
41: sipush 1000
44: iadd
45: ireturn
}
When a method is inovked a memory space in the stack called current frame is allocated
If you look carefully even int a=-111; assignment happens in an implicit init function Abc() !
int a = -111;
5: bipush -111
7: putfield #2 // Field a:I
As field variable e is not assigned any value it will be 0 if primitive or null if a Object reference
And if you look at doSomething()
int a = 10;
0: bipush 10
for a local to be used the initial value needs to be pushed into stack in this case 10 . without this 'push' [initialization] a's value is not accessible to subsequent statements (as the value is not on the stack). once the value is pushed to stack other operations like iadd istore etc are carried out on the stack
below statement actually creates an object on the heap space and invokes init method. This is where un initialized variables like 'e' gets default values
15: new #3 // class Abc
18: dup
I leave further bytecode comparison upto you ;) but I hope it is clear
tl;dr: It was more or less an arbitrary choice
If you ask me, it was a mistake that Java has default values for instance variables. The compiler should have forced the programmer to initialize it before like it is the case for local variables.
The rationale behind the default values is safety. When an object is instantiated, a chunk of memory will be allocated for the object which contains where the instance variables are pointing to etc. The Java designers decided it would be a good idea to wipe this part of memory with zeros and nulls. This way you will never read garbage that happened to be there before the object was allocated. They could have forced initialization; there is nothing fundamental about the choice. It probably made things easy to implement and made enough sense to the designers of Java.
In case of local variables, the designers chose to force initialization (or perhaps it's more accurate to say they chose to not do any kind of initialization when a local variable is only declared, and thus the most logical behavior of the compiler was to force initialization of the variable before use).
Related
This question already has answers here:
Java "for" statement implementation prevents garbage collecting
(6 answers)
Closed 4 years ago.
The following example describes the generation of the following lines of code until Java 9.
List data = new ArrayList<>();for (String b : data);
public class Test
{
public Test() {}
public static void main(String[] paramArrayOfString) throws IOException {
ArrayList localArrayList = new ArrayList();
String str;
for (Iterator localIterator = localArrayList.iterator(); localIterator.hasNext(); str = (String)localIterator.next()) {}
}
In Java 10, iterator variables are declared outside for loops and initialized to the null value immediately once the operation is over, so GC can get rid of unused memory.
{
Iterator iterator = data.iterator();
for (; iterator.hasNext();)
{
String b = (String)iterator.next();
}
b = null;
iterator = null;
}
How is setting reference null explicitly better than reference going out of scope by the end of the for loop.
Source: https://dzone.com/articles/features-in-java-10
Also, adding link from the comments : https://bugs.openjdk.java.net/browse/JDK-8192858
Edit: There already exists a related question: Java "for" statement implementation prevents garbage collecting that provides more information.
After reading through the bug-reports (https://bugs.openjdk.java.net/browse/JDK-8192858 and https://bugs.openjdk.java.net/browse/JDK-8175883) the reason for this change can be summarised as the following:
There was an issue in the bytecode produced by javac which resulted in a reference to an array / iterable being kept after the completion of the loop.
As a result, even if an array / iterable is explicitly nullified, there is still a reference to the array / iterable which means that the array / iterable was not eligible for garbage collection until leaving the scope of the method.
With large arrays / iterables (as per the example below) this could result in an OutOfMemoryError.
This is demonstrated by this use case here (taken from the bug report):
public class IteratorInOneScope {
private static final int HALF_OF_MEMORY = (int) (Runtime.getRuntime().maxMemory() * 0.5);
public static void main(String[] args) {
byte[] data = new byte[HALF_OF_MEMORY];
for (byte b : data); // <-- if you comment this line - the application finished successfully
data = null; // this expects to discard reference -> allow to release the memory
byte[] data2 = new byte[HALF_OF_MEMORY]; // the memory can't be allocated second time, if the "for" loop statement above is used
System.out.println("Success");
}
}
Which compiled to the following bytecode:
0: getstatic #2 // Field HALF_OF_MEMORY:I
3: newarray byte
5: astore_0 <==== array ref in slot #0
6: aload_0
7: astore_1 <==== array ref in slot #1
8: aload_1
9: arraylength
10: istore_2
11: iconst_0
12: istore_3
13: iload_3
14: iload_2
15: if_icmpge 29
18: aload_1
19: iload_3
20: baload
21: istore 4
23: iinc 3, 1
26: goto 13
29: aconst_null
30: astore_0 <== nulls slot #0
31: getstatic #2 // Field HALF_OF_MEMORY:I
34: newarray byte
36: astore_1
37: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
40: ldc #4 // String Success
42: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return
There is no update to JLS 14.14.2, for this reason:
JLS 14.14.2 is only concerned with semantics of the statement, not
garbage collection behavior. Compilers are free to generate whatever
bytecode they want that produces the specified behavior. So, if javac
wants to set some unused locals to null, it's free to do so. No spec
change necessary, and it would be a mistake to include it in the spec,
because it doesn't impact the statement's semantics.
I've asked several questions regarding the subject, but it seems like every time I get an answer, I have more questions.
This question is continuation of my other question: Initialization in polymorphism of variables
Anyways, consider the following example.
class A{ //1
int a = 1; //2
}
i heard this conceptually looks like,
class A { //1
int a = 0; //2
A() { //3
super(); //4
a = 1; //5
}
From what I understand, this is because every time an object is created, instance objects are initialized to its default values.
If I put initialization block say,
System.out.print(i);
right below line 2 for both examples, top will print 1 and bottom will print 0. As far as I know, initialization block is executed before constructor. So is this only conceptual representation of constructors only? Or does the code actually change as such when the default constructor is called? Can someone clarify this for me?
Why does it behave this way? In my other question, it seemed to only cause confusion as to which variable is called. Can't instance variable just be declared a=1 and gets used throughout the class? Shouldn't that make it simpler?
As you said, the equivalence between the two classes in your question is only conceptual.
In fact, if an non-static data field has an initialization value, it is initialized before calling the constructor. The initialization block is copied by the compiler to the beginning of every constructor (after the super line), so it is executed after the initialization of the field and before the constructor code itself.
Your description of how int a = 1 gets converted to a constructor is correct, but it is not the whole story.
If, in addition to a, there are other instance fields with initializers, all of their initializers are collected into a single block that runs as part of constructors
If, in addition to field initialization you have general-purpose initializer blocks, their content gets collected into that same block, along with field initializers.
For example, if you have
class A {
{
System.out.println(a);
}
int a = 1;
{
System.out.println(a);
System.out.println(b);
}
int b = 2;
{
System.out.println(b);
}
public A() {
// Code of A
}
}
then the code block prior to Code of A looks like this:
System.out.println(a);
a = 1;
System.out.println(a);
System.out.println(b);
b = 2;
System.out.println(b);
// Code of A
It should be clear now why zero is printed in the initialization block prior to int a = 1 in the block preceding the initializer: initialization blocks are not treated separately from field initializers, their code gets mixed together in the same order that they appear in the source code.
The difference between your example is the order of operations. In your first example, with the initializer block where you said, the order is:
Assign 1 to a (in the declaration)
Output a (in the initializer block)
...but in your example example, it's
Assign 0 (the default value) to a (effectively in the declaration)
Output a (in the initialization block)
Assign 1 to a (in the constructor)
The key to understanding instance initialization for me is this: Instance initialization code is literally copied into the constructors — all of them, including the default one — by the compiler. It's copied in source code order, and it's before anything in the constructor (including super).
Here's a more complete example. Consider this class:
class Example {
// Instance field with initializer
private int i = 5;
// Instance initialization block
{
System.out.println(this.i);
}
// constructor 1
Example() {
System.out.println(this.i * 2);
}
// constructor 2
Example(int _i) {
this.i = _i;
System.out.println(this.i * 3);
}
}
That's compiled into bytecode exactly as though it were this:
class Example {
// Instance field
private int i;
// constructor 1
Example() {
// begin copied code
this.i = 5;
System.out.println(this.i);
// end copied code
System.out.println(i * 2);
}
// constructor 2
Example(int _i) {
// begin copied code
this.i = 5;
System.out.println(this.i);
// end copied code
this.i = _i;
System.out.println(this.i * 3);
}
}
In both cases above, Oracle's Java 8 outputs the exact same bytecode (as viewed by using javap -c Example after compiling):
Compiled from "Example.java"
class Example {
Example();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0
5: iconst_5
6: putfield #2 // Field i:I
9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_0
13: getfield #2 // Field i:I
16: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
19: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_0
23: getfield #2 // Field i:I
26: iconst_2
27: imul
28: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
31: return
Example(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0
5: iconst_5
6: putfield #2 // Field i:I
9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_0
13: getfield #2 // Field i:I
16: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
19: aload_0
20: iload_1
21: putfield #2 // Field i:I
24: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
27: aload_0
28: getfield #2 // Field i:I
31: iconst_3
32: imul
33: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
36: return
}
Instance variables are immediately available with the default variable if not otherwise set: Objects are set to null and primitive types to 0, false etc.
You have 3 options to set the value of an instance variable in Java:
1) Declare and instantiate immediately
class A {
int i = 1;
}
2) Instantiate it in a instance initializer block
class A {
int a; // it is default value 0 at this point
{ a = 1; } //instance initializer block
}
3) Instantiate it in the constructor
class A{
int a; // it is default value 0 at this point
A() {
a = 1;
}
}
During the instantiation of the A object, Java will
first instantiate the variable a to its default if not done by the user,
then it will go through any instance initializer block in the order they appear, and lastly
it will enter the constructor.
The below text is from jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
Even then, there are a number of complications. If a final field is
initialized to a compile-time constant expression (§15.28) in the
field declaration, changes to the final field may not be observed,
since uses of that final field are replaced at compile time with the
value of the constant expression.
Can anyone please give me better explanation for the above. I couldn't understand the statement "changes to the final field may not be observed". May with the help of example.
I couldn't understand the statement changes to the final field may not be observed
It tells that , if a final variable is declared as compile time constant then any change made in the final variable using reflection API further in program will not be visible to the program during execution.
For example consider the code given below:
import java.lang.reflect.*;
class ChangeFinal
{
private final int x = 20;//compile time constant
public static void change(ChangeFinal cf)
{
try
{
Class clazz = ChangeFinal.class;
Field field = clazz.getDeclaredField("x");
field.setAccessible(true);
field.set(cf , 190);//changed x to 190 for object cf
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
public static void main(String[] args)
{
ChangeFinal cf = new ChangeFinal();
System.out.println(cf.x);//prints 20
change(cf);
System.out.println(cf.x);//prints 20
}
}
The Output of the above code is:
20
20
WHY?
The answer lies in the output provided by javap -c command for public static void main:
public static void main(java.lang.String[]);
Code:
0: new #3; //class ChangeFinal
3: dup
4: invokespecial #11; //Method "<init>":()V
7: astore_1
8: getstatic #12; //Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_1
12: invokevirtual #13; //Method java/lang/Object.getClass:()Ljava/lang/Cla
ss;
15: pop
16: bipush 20
18: invokevirtual #14; //Method java/io/PrintStream.println:(I)V
21: aload_1
22: invokestatic #15; //Method change:(LChangeFinal;)V
25: getstatic #12; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_1
29: invokevirtual #13; //Method java/lang/Object.getClass:()Ljava/lang/Cla
ss;
32: pop
33: bipush 20
35: invokevirtual #14; //Method java/io/PrintStream.println:(I)V
38: return
}
At line 16 (before changeFinal method is called)the value of cf.x is hardcoded to 20 . And at line 33 (after changeFinal method is called) the value of cf.x is again hardcoded to 20. Therefore , Although the change in the value of final variable x is done successfully by reflection API during execution, but because of x being a compile time constant it is showing its constant value 20.
It means if in a class you have this:
public class Foo {
public final boolean fooBoolean = true; // true is a constant expression
public final int fooInt = 5; // 5 is a constant expression
}
At compile time any reference to Foo.fooBoolean may be replaced with true, and references to Foo.fooInt may be replaced by 5. If at runtime you later change either of those final fields via reflection, the code referencing it (as it was written) may never see it.
It is quite possible for a Java program to observe a final field having two different values at different times, even without reflection, without recompiling multiple versions of the class, and without anything along those lines. Consider the class below:
class X {
static final int x = getX();
static int getX() {
System.out.println("X.x is now " + X.x);
return 1;
}
public static void main(String[] args) {
System.out.println("X.x is now " + X.x);
}
}
Output:
X.x is now 0
X.x is now 1
This happens because some of the code (the first println) is executed before the field's value is assigned, so that code observes the field's default initial value of 0. The field has a default initial value before it is assigned, even though it is final, because it is not a constant field. The text you quoted from the JLS says this kind of thing cannot happen if the field is declared as a constant.
I know the Operator Precedence list, but I just cannot figure out what is the execution precedence in this code in "LINE 1". What Object is created before? For example: the My String or the new Precedence()? How can we apply the Operator Precedence rule in this example?
public class Precedence {
public String s;
public static void main (String ... args){
String a = new Precedence().s="My String"; // LINE 1
System.out.println(a);
}
}
OUTPUT:
My String
This
String a = new Precedence().s="My String"; // LINE 1
is a local variable declaration statement with an initialization expression.
Every time it is executed, the declarators are processed in order from
left to right. If a declarator has an initializer, the initializer is
evaluated and its value is assigned to the variable.
a is the declarator. It's evaluated to produce a variable (itself). Then the initialization expression is evaluated.
This
new Precedence().s = "My String";
is an assignment expression. The left hand side of the operator is evaluated first to produce a variable, so new Precedence() is evaluated first, instantiates the class Precedence, producing a reference to an object. Then the right hand side of the assignment is evaluated, the String literal "My String", so a reference to a String object is produced. Then the assignment happens assigning the reference to the String object to the variable s of the object referenced by the value returned by the new instance creation expression.
Finally, since
At run time, the result of the assignment expression is the value of
the variable after the assignment has occurred.
The value that was assigned to the field s of the Precedence object is also assigned to the variable a.
Here's the bytecode:
public static transient varargs main([Ljava/lang/String;)V
L0
LINENUMBER 8 L0
NEW Precedence
DUP
INVOKESPECIAL Precedence.<init> ()V
LDC "My String"
DUP_X1
PUTFIELD Precedence.s : Ljava/lang/String;
ASTORE 1
This shows the following execution order:
Create Precedence object.
Assign My String constant to Precedence.s.
Assign it also to a.
There is tool javap, dissasembler, which will show you bytecode and from there you can conclude what is order of execution. It will output comments.
$ /usr/lib/jvm/java-7-oracle/bin/javap -c Precedence.class
Compiled from "Precedence.java"
public class Precedence {
public java.lang.String s;
public Precedence();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String...);
Code:
0: new #2 // class Precedence
3: dup
4: invokespecial #3 // Method "<init>":()V
7: ldc #4 // String My String
9: dup_x1
10: putfield #5 // Field s:Ljava/lang/String;
13: astore_1
14: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_1
18: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
21: return
}
javap is part of JDK, path is on Linux, switch -c is Disassemble the code. Here is definition of instruction ldc, it is not obvious what it does
push a constant #index from a constant pool (String, int or float) onto the stack
The below text is from jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
Even then, there are a number of complications. If a final field is
initialized to a compile-time constant expression (§15.28) in the
field declaration, changes to the final field may not be observed,
since uses of that final field are replaced at compile time with the
value of the constant expression.
Can anyone please give me better explanation for the above. I couldn't understand the statement "changes to the final field may not be observed". May with the help of example.
I couldn't understand the statement changes to the final field may not be observed
It tells that , if a final variable is declared as compile time constant then any change made in the final variable using reflection API further in program will not be visible to the program during execution.
For example consider the code given below:
import java.lang.reflect.*;
class ChangeFinal
{
private final int x = 20;//compile time constant
public static void change(ChangeFinal cf)
{
try
{
Class clazz = ChangeFinal.class;
Field field = clazz.getDeclaredField("x");
field.setAccessible(true);
field.set(cf , 190);//changed x to 190 for object cf
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
public static void main(String[] args)
{
ChangeFinal cf = new ChangeFinal();
System.out.println(cf.x);//prints 20
change(cf);
System.out.println(cf.x);//prints 20
}
}
The Output of the above code is:
20
20
WHY?
The answer lies in the output provided by javap -c command for public static void main:
public static void main(java.lang.String[]);
Code:
0: new #3; //class ChangeFinal
3: dup
4: invokespecial #11; //Method "<init>":()V
7: astore_1
8: getstatic #12; //Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_1
12: invokevirtual #13; //Method java/lang/Object.getClass:()Ljava/lang/Cla
ss;
15: pop
16: bipush 20
18: invokevirtual #14; //Method java/io/PrintStream.println:(I)V
21: aload_1
22: invokestatic #15; //Method change:(LChangeFinal;)V
25: getstatic #12; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_1
29: invokevirtual #13; //Method java/lang/Object.getClass:()Ljava/lang/Cla
ss;
32: pop
33: bipush 20
35: invokevirtual #14; //Method java/io/PrintStream.println:(I)V
38: return
}
At line 16 (before changeFinal method is called)the value of cf.x is hardcoded to 20 . And at line 33 (after changeFinal method is called) the value of cf.x is again hardcoded to 20. Therefore , Although the change in the value of final variable x is done successfully by reflection API during execution, but because of x being a compile time constant it is showing its constant value 20.
It means if in a class you have this:
public class Foo {
public final boolean fooBoolean = true; // true is a constant expression
public final int fooInt = 5; // 5 is a constant expression
}
At compile time any reference to Foo.fooBoolean may be replaced with true, and references to Foo.fooInt may be replaced by 5. If at runtime you later change either of those final fields via reflection, the code referencing it (as it was written) may never see it.
It is quite possible for a Java program to observe a final field having two different values at different times, even without reflection, without recompiling multiple versions of the class, and without anything along those lines. Consider the class below:
class X {
static final int x = getX();
static int getX() {
System.out.println("X.x is now " + X.x);
return 1;
}
public static void main(String[] args) {
System.out.println("X.x is now " + X.x);
}
}
Output:
X.x is now 0
X.x is now 1
This happens because some of the code (the first println) is executed before the field's value is assigned, so that code observes the field's default initial value of 0. The field has a default initial value before it is assigned, even though it is final, because it is not a constant field. The text you quoted from the JLS says this kind of thing cannot happen if the field is declared as a constant.