I'm indebted to this answer for the idea.
The following code compiles, but certainly shouldn't. It uses the value of x before it's been initialised. If you remove the StaticAssign. qualifier then it no longer compiles.
public class StaticAssign {
public static final int x;
static {
System.out.println(StaticAssign.x);
x = 5;
}
public static void main(String[] args) {}
}
On my machine, this consistently prints 0. If I change the type of x to String, it consistently prints null.
The question is: will it definitely print 0 or null, or might it access uninitialised memory and print out something else?
And even if this code gets through the compiler, why isn't it picked up by the JVM?
Is there a way to do nasty things based on this?
It actually has been initialized. Variables in the global scope are initialized automatically. Variables of Object type will be initialized to null primitive like int will be initialized to 0. A variable declared not in the global scope must be initialized ie. declared in a method. Another problem is declaring it as final this is telling the compiler it must be explicitly initialized. So by adding the x=5 you are bypassing the compiler error saying it must be explicitly initialized. When you access it before this line at run-time it is initialized to 0 like any other primitive int type variable in the global scope.
This is due to the way in which the classes are loaded.
First the class StaticAssign definition is loaded and the fields are initialized to default values:
StaticAssign.x = 0;
Then the initalization block is executed.
System.out.println(StaticAssign.x);
x = 5;
There is a reference to StaticAssign.x, the current class. As recursive initialization attempts are simply ignored, value of x is still 0.
This means that:
System.out.println(StaticAssign.x)
is valid because StaticAssign.x is a reference to field of an allready loaded class.
But if yo do:
System.out.println(x)
then x is a reference to a final uninitalized field.
On the other hand, you never will access uninitialised memory. When the class definition is loaded variables are initialized to default values, before the initalization block is executed.
EDIT:
There is a nice video "Elvis Lives Again" from Java Puzzlers that shows this much more better than I can explain
Related
Consider the following code snippet in Java. It won't compile.
package temppkg;
final public class Main
{
private String x;
private int y;
private void show()
{
String z;
int a;
System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
System.out.println(y); // Works fine displaying its default value which is zero.
System.out.println(z.toString()); // Causes a compile-time error - variable z might not have been initialized.
System.out.println(a); // Causes a compile-time error - variable a might not have been initialized.
}
public static void main(String []args)
{
new Main().show();
}
}
Why do the class members (x and y) declared in the above code snippet not issue any compile-time error even though they are not explicitly initialized and only local variables are required to be initialized?
When in doubt, check the Java Language Specification (JLS).
In the introduction you'll find:
Chapter 16 describes the precise way in which the language ensures
that local variables are definitely set before use. While all other
variables are automatically initialized to a default value, the Java
programming language does not automatically initialize local variables
in order to avoid masking programming errors.
The first paragraph of chapter 16 states,
Each local variable and every blank final field 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.
The default values themselves are in section 4.12.5. The section opens with:
Each class variable, instance variable, or array component is
initialized with a default value when it is created.
...and then goes on to list all the default values.
The JLS is really not that hard to understand and I've found myself using it more and more to understand why Java does what it does...after all, it's the Java bible!
Why would they issue a compile warning?, as instance variables String will get a default value of null, and int will get a default value of 0.
The compiler has no way to know that x.toString(), will cause a runtime exception, because the value of null is not actually set till after runtime.
In general the compiler couldn't know for sure if a class member has or has not been initialized before. For example, you could have a setter method that sets the value for a class member, and another method which accesses that member. The compiler can't issue a warning when accessing that variable because it can't know whether the setter has been called before or not.
I agree that in this case (the member is private and there is no single method that writes the variable) it seems it could raise a warning from the compiler. Well, in fact you are still not sure that the variable has not been initialized since it could have been accessed via reflexion.
Thins are much easier with local variables, since they can't be accessed from outside the method, nor even via reflexion (afaik, please correct me if wrong), so the compiler can be a bit more helpful and warn us of uninitialized variables.
I hope this answer helps you :)
Class members could have been initialized elsewhere in your code, so the compiler can't see if they were initialized at compilation time.
Member variables are automatically initialized to their default values when you construct (instantiate) an object. That holds true even when you have manually initialized them, they will be initialized to default values first and then to the values you supplied.
This is a little little lengthy article but it explains it: Object initialization in Java
Whereas for the local variables (ones that are declared inside a method) are not initialized automatically, which means you have to do it manually, even if you want them to have their default values.
You can see what the default values are for variables with different data types here.
The default value for the reference type variables is null. That's why it's throwing NullPointerException on the following:
System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
In the following case, the compiler is smart enough to know that the variable is not initialized yet (because it's local and you haven't initialized it), that's why compilation issue:
System.out.println(z.toString()); // "Cuases a compile-time error - variable z might not have been initialized.
I was preparing for Java OCA certification where I came across a similar question like that of the following one:
public class TestClass {
final int i;
public static void main(String[] args) {
TestClass t = new TestClass();
System.out.println(t.i);
}
}
As per Java, the instance and static variables are given default values. Surprisingly, this was error that was shown in my console:
error: variable i not initialized in the default constructor final int i;
Why wasn't i assigned a default value above?
Assigning default value to the final variable would defeat the entire purpose of making a variable final in the first place.
final would mean that you can't change the value once assigned.
If a default value is given to final variable then you would never be able to set the value of the variable to something else (even for the first time).
It's defined this way in the Java Language Specification:
Chapter 16. Definite Assignment
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.
[...]
For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.
Where the term "blank final field" refers to a final field with no value or initializer, and "definitely assigned" means that the field will be assigned to no matter what:
The idea behind definite assignment is that an assignment to the local variable or blank final field must occur on every possible execution path to the access. Similarly, the idea behind definite unassignment is that no other assignment to the blank final variable is permitted to occur on any possible execution path to an assignment.
Aside from throwing an error by specification, there's logical reasoning behind the decision. There's no point in having a default value for a blank final field. In your case, the blank final is an integer, and it would just be given 0 and you wouldn't be able to change it. What would the use of the variable be?
Also, if a final variable is not explicitly given a default value, why not initialize it in the first place? You can't reassign to it later since a default value would already be given, so why not initialize it now?
Error message is misleading since it can be initialized with default value:
class FinalTest{
final int x = printXAndInitializeIt();
private int printXAndInitializeIt(){
System.out.println("x before initialization = "+x);
return 1;
}
public static void main(String[] args) {
FinalTest ft = new FinalTest();
System.out.println("x after initialization = "+ft.x);
}
}
Output:
x before initialization = 0
x after initialization = 1
But even if there is default value final variable expects explicit initialization, since in most cases lack of initialization is caused by mistake. This error could be changed to warning, but I am guessing that Java designers though that "better safe than sorry".
It's not that final fields aren't initialized to the default value — in fact they sort of are, though you'll only observe the fact if you're doing some silly stuff — but that you're required to explicitly initialize them exactly once (either as part of the declaration, or in an initializer block, or in every single constructor).
To understand the motivation for this, consider the following code:
public class Foo {
private final int mValue;
public Foo(final boolean shouldSet) {
if (shouldSet) {
mValue = 1;
}
}
// ...
}
If the field weren't final, then the compiler would infer an implicit = 0 at the end of its declaration; but with a final field that's not always valid . . . and in the above example, the compiler can't even tell beforehand whether it will be valid or not. So the compiler sidesteps the issue by never inferring an implicit = 0, and requiring explicit initialization.
Why didn't it assign a default value in the above case ? Can anyone
explain!
if a default value were to be provided then you wouldn't be able to change the value of it. when the final keyword is used with variables it means once the variable is assigned, it cannot be re-assigned.
Wikipedia:
A final variable can only be initialized once, either via an
initializer or an assignment statement. It does not need to be
initialized at the point of declaration: this is called a "blank
final" variable. A blank final instance variable of a class must be
definitely assigned in every constructor of the class in which it is
declared; similarly, a blank final static variable must be definitely
assigned in a static initializer of the class in which it is declared;
otherwise, a compile-time error occurs in both cases. (Note: If the
variable is a reference, this means that the variable cannot be
re-bound to reference another object. But the object that it
references is still mutable, if it was originally mutable.)
further reading:
Definite Assignment
Why can't i be initialized before declaring it in code 1 but it can be in code 2 :
class Code1 {
void methodOfCode1() {
// System.out.println(i); can't use a local field before it is defined
// i = 10; can't initialize a local field before it is defined
int i;
}
}
class Code2{
static
{
i = 10; //A field can be initialized before it is defined.
}
static int i;
}
The answer is order of execution.
Code executes sequentially, you must define what something is before you use it. Therefore in a method you must declare a variable before you use it.
In Java the compiler initializes your class in multiple passes. It first runs through and declares all of the static members, then it sets the default values for all static members. This all happens before any methods (static or not) are executed.
It's the scope of the variable & the lack of type prior to its use: you have yet to define i. You must define a variable before it can be used:
int i = 0;
System.out.println(i); // works
System.out.println(j); // will not work
byte j;
Here's the Java Language Specification giving you the detail.
During initialise a variable, some amount of memory is allocated based on datatype.
Unless you allocate memory, you can't initialize value.
Because compiler must know where this variable is stored and how many bytes it have to use. If I tell computer var=97 before initializing, it cannot know where this "var" variable is stored. Also if "var" is char , computer needs one byte to store in RAM. However it also can be an integer which is 4 bytes(depends on different systems). Therefore, there would be an ambiguous situation for the system.Compiler does not allow that , reasonably.
Java is strongly-typed, you can't use something that you don't know what it is. For instance, a method is different if it receives a String or if it receives an int, so the compiler must be able to know which is the proper method to be invoked
For instance, this is valid:
public void dealWithIt(int val){
val++
System.out.println(val);
}
public void dealWithIt(String val){
System.out.println(val);
}
The compiler can't assign a value to a variable before know what type of variable it is.
In Java, a variable's type must be declared before it can be used.
For example:
System.out.println(i); // for this statement i is unknown/undefined...
i = 10; // same for above...
EDITED :
First inside class Code1,
you haven't declare variable before use it, which is against of Java Standard API so obviously compiler will complain.
while into class Code2 case,
you have declare 'static' both instance as well as block.
so it does not required any instance to load it.
(review static concept in depth will let you to the correct direction)
Java doesn't allow you to use an variable that may not have been initialized within a method scope. An uninitialized variable within a class scope may still be returned by a class method, and the value defaults to null.
Why the different treatment of the two different scopes?
public class TestClass {
Integer i;
Double d;
public TestClass() {
d = 1d;
}
public Double getD() {
return d;
}
public Integer getI() {
return i;
}
// public Integer getSomeInt() {
// Integer i;
// return i;
// }
public static void main(String[] args) {
TestClass myClass = new TestClass();
System.out.println(myClass.getI().getClass());
}
}
This results in a NullPointerException, but returning i within getSomeInt() is a compiler error because "the variable may not have been initialized".
The reason behind this are the limits of Java's static code analysis. The compiler is able to prove beyond doubt that you will not read a stack-allocated local var before initializing it. This is impossible to do for heap-allocated memory and therefore Java mandates that all heap-allocated storage be zeroed out before exposing a pointer to it.
The consequence of this rule is that everything heap-allocated has a default value of zero (false, null, whatever the binary zero amounts to for the type).
Because member variables have default value (if not initialized) and so the I has null and if you invoke method on null it will result on NullPointerException
and for local variables, they must be initialized before used otherwise it will turn into compile time error
Local variables are slightly different; the compiler never assigns a default value to an uninitialized local variable. If you cannot initialize your local variable where it is declared, make sure to assign it a value before you attempt to use it. Accessing an uninitialized local variable will result in a compile-time error. [....]
It's quite simple really. Member variables get automatically initialized to their default values, while local variables does not.
When you do
public Integer getSomeInt() {
Integer i;
return i;
}
you hide this.i and in return i you refer to an (uninitialized) local variable.
So why are member variables initialized automatically while local variables are not?
Ultimately this is a question that only the designers of the language can answer, but if I had to guess I'd say it is due to the performance issue of having to zero out all memory being allocated. When it comes to objects, it would however be a pain to force the programmer to initialize all fields explicitly.
From the JLS (4.12.3 Kinds of Variables):
A class variable is created when its class or interface is prepared (§12.3.2) and
is initialized to a default value (§4.12.5).
[...]
A local variable declaration statement may contain an expression which
initializes the variable. The local variable with an initializing expression is
not initialized, however, until the local variable declaration statement that
declares it is executed. (The rules of definite assignment (Chapter 16, Definite
Assignment) prevent the value of a local variable from being used before it has
been initialized or otherwise assigned a value.)
All fields are implicitly initialized in the constructor after the call to super but before anything else. Object references are set to null, primitive values are set to 0, false etc.
This implicit initialization isn't done in methods.
Why must a final variable be initialized before constructor completes?
public class Ex
{
final int q;
}
When I compile this code I get error like this
err:variable q might not have been initialized
The official reason is that it is defined by the Java Language Specification 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.
A blank final is a final variable whose declaration lacks an initializer (i.e. what you describe).
The value of a final variable can only be set once. The constructor is the only place in the code for a class that you can guarantee this will hold true; the constructor is only ever called once for an object but other methods can be called any number of times.
Because final prevents you from modifying variables, but it has to be initialized at some point, and the constructors is the right place to do so.
In your case, it would be called a blank final because it is not initialized when declared.
A final variable must be initialized at the declaration or in a constructor.
If it has not been initialized when the constructor returns, it may never be initialized, and may remain an uninitialized variable. The compiler cannot prove it will be initialized, and thus throws an error.
This Wikipedia excerpt explains it well:
A final variable can only be initialized once, either via an initializer or an assignment statement. It does not need to be initialized at the point of declaration: this is called a "blank final" variable. A blank final instance variable of a class must be definitely assigned at the end of every constructor of the class in which it is declared; similarly, a blank final static variable must be definitely assigned in a static initializer of the class in which it is declared: otherwise, a compile-time error occurs in both cases. (Note: If the variable is a reference, this means that the variable cannot be re-bound to reference another object. But the object that it references is still mutable, if it was originally mutable.)
The final keyword applied to a field has one of two effects:
on a primitive, it prevents the value of the primitive from being changed (an int can't change value)
on an object, it prevents the "value of the variable", that is, the reference to the object, from being changed. That is to say that, if you have a final HashMap<String,String> a, you will only be able to set it once, and you won't be able to do this.a=new HashMap<String,String>(); again, but nothing keeps you from doing this.a.put("a","b"),s since that doesn't modify the reference, only the content of the object.
The final modifier prevents your from changeing the variables value, hence you have to initialize it where you declare it.
The language specification contains specific guarantees about the properties of final variables and fields, and one of them is that a properly constructed object (i.e. one whose constructor finished successfully) must have all its final instance fields initialized and visible to all threads. Thus, the compiler analyzes code paths and requires you to initialize those fields.
If an instance variable is declared with final keyword, it means it can not be modified later, which makes it a constant. That is why we have to initialize the variable with final keyword.Initialization must be done explicitly because JVM doesnt provide default value to final instance variable.Final instance variable must be initialized either at the time of declaration like:
class Test{
final int num = 10;
}
or it must be declared inside an instance block like:
class Test{
final int x;
{
x=10;
}
}
or
it must be declared BEFORE constructor COMPLETION like:
class Test{
final int x;
Test(){
x=10;
}
}
Keep in mind that we can initialize it inside a consructor block because initialization must be done before constructor completion.
Final modifier does not allow to change your variable value. So you need to assign a value to it at some place and constructor is the place you have to do this in this case.
The moment the constructor completes execution, the object is 'open for business' - it can be used, its variables can be accessed.
The final keyword on a variable guarantees that its value will never change. This means, if the value is ever read, you can be sure that the variable will always have that value.
Since the variable can be accessed (read) at anytime after the execution of the constructor, it means it must never change after the constructor has executed - just it case it has been read.
Thus, it must, by then, have been set.