Example 1:
public class ExampleWhile {
public static void main(String args[]) {
int a = 10, b = 20;
while (a < b)
{
System.out.println("hello");
}
System.out.println("hi");
}
}
Example 2:
public class ExampleWhile2 {
public static void main(String args[]) {
while (true)
{
System.out.println("hello");
}
System.out.println("hi"); // Compile time error saying unreachable statement
}
}
Why there is a compile time error in example 2 when example 1 runs without error?
Because the compiler is "clever" enough to know that while(true) is an infinite loop, hence System.out.println("hi"); would never be executed.
It is not clever enough to infer the same with variables, even when scoped to the method.
With compile-time constants (static final int makes the integer a compile-time constant. Compile time constants will be available as part of the byte-code itself), it's another story:
static final int A = 0;
static final int B = 1;
public static void main(String[] args) {
while (B > A) {
}
// won't compile!
System.out.println("Foo");
}
The compiler does not know about the values of a and b, so it doesn't see it as an infinite loop with an unreachable statement.
With while(true), on the other hand, the compiler knows there 'll be trouble.
The Java Language Specification gives specific rules, in 14.21. Unreachable Statements, for the compile time error.
The rule that applies to the sample code is:
A while statement can complete normally iff at least one of the
following is true:
The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
The first of the two conditions is true for Example 1, but false for Example 2. The second condition is false for both examples. As far as the language specification is concerned, there is a possibility that the first loop might complete normally, allowing control to reach the following statement. There is no such possibility for the second example.
The rules achieve two benefits. There is a single set of rules for whether a sequence of characters constitutes a compilable Java program, regardless of the choice of compiler. The rules can be implemented very simply.
Related
Why is this code a compilation error?
class Test1{
static {
System.out.println(x);
}
static int x = 10;
}
While the below code compiles and runs fine?
class Test2{
static int x = 10;
static {
m1();
System.out.println("base SB");
}
public static void main(String args[]){
m1();
System.out.println("base main method");
}
public static void m1(){
System.out.println(y);
}
static int y = 20;
}
If it declares y here, why not in the previous code.
The output of below code is:
0
base SB
20
base main method
I think Java does not support forward referencing.
It may be an unsatisfying answer, but this is just how the language was defined, specifically here.
The restrictions above are designed to catch, at compile time, circular or otherwise malformed initializations
But you can get around the restriction quite easily like in the question, or just by using Test21.x instead of just x, so it is not particularly robust:
class Test21{
static {
// This is legal, but it is uninitialized, and thus prints 0.
System.out.println(Test21.x);
}
static int x=10;
}
On balance, it is probably better to force you to jump through a small hoop, to make you stop and think about what you are doing, and only to proceed if you really intend to do that.
Ultimately, the language can't entirely stop you shooting yourself in the foot. It can only make it harder to do so.
Your second test case prints zero first which I would assume would not be idiomatic for most people.
This is simply a case of the compiler not being smart enough to catch a probable mistake which you have made.
It is able to detect the forward reference in the first case -- lucky! If it didn't, your program would have printed zero at runtime since the variable would not have been initialized yet. So the compiler saved you from a probable bug.
It is not able to detect it in the second case, since you have introduced a level of indirection with the additional method call to m1 -- unlucky!
In an ideal world, the second code sample wouldn't compile either.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Java static class initialization
in what order are static blocks and static variables in a class executed?
When I run this code the answer is 1, I thought it would be 2.
What is the order of initialization and the value of k in each step?
public class Test {
static {k = 2;}
static int k = 1;
public static void main(String[] args) {
System.out.println(k);
}
}
Edit 1: As a follow up to "k is set to default value" then why this next code doesn't compile? Theres an error "Cannot reference a field before it's defined".
public class Test {
static {System.out.println(k);}
static int k=1;
public static void main(String[] args) {
System.out.println(k);
}
}
Edit 2: For some unknow to me reason it^ works when instead of "k" its "Test.k".
Thanks for all the answers. this will sufice :D
They are executed in the order that you write them. If the code is:
public class Test {
static int k = 1;
static {k = 2;}
public static void main(String[] args) {
System.out.println(k);
}
}
then the output becomes 2.
The order of initialization is: ..the class variable initializers and static initializers of the class..., in textual order, as though they were a single block.
And the values (for your code) are: k = 0 (default), then it's set to 2, then it's set back to 1.
You can check that it's actually set to 2 by running the following code:
private static class Test {
static {
System.out.println(Test.k);
k = 2;
System.out.println(Test.k);
}
static int k = 1;
public static void main(String[] args) {
System.out.println(k);
}
}
Short answer
When the initialization of the class starts, k will have initial value of 0.
The static block (since it precedes the assignment in the declaration) is then executed, and k will be assigned 2.
Then the initializer in the declaration is executed, and k will be assigned 1.
Long explanation
Let us use this example, since your example is a bit simple:
class TestInitOrder {
static {
System.out.println(TestInitOrder.stat1);
System.out.println(TestInitOrder.stat2);
System.out.println(TestInitOrder.str);
System.out.println(TestInitOrder.str2);
str = "something";
System.out.println(TestInitOrder.str);
System.out.println(TestInitOrder.str2);
System.out.println(TestInitOrder.lazy);
System.out.println(TestInitOrder.second);
}
private static final int stat1 = 10;
static final String str2 = "sdfff";
static String str = "crap";
private static int stat2 = 19;
static final Second second = new Second();
static final int lazy;
static {
lazy = 20;
}
static {
System.out.println(TestInitOrder.str2);
System.out.println(TestInitOrder.stat2);
System.out.println(TestInitOrder.str);
System.out.println(TestInitOrder.lazy);
System.out.println(TestInitOrder.second);
}
public static void main(String args[]) {
}
}
class Second {
public Second() {
System.out.println(TestInitOrder.second);
}
}
According to Java Language Specification, from section 4.12.5:
Every variable in a program must have a value before its value is used:
Each class variable, instance variable, or array component is initialized with a default value when it is created
(The following lines from the specification specify the default value for all the types, basically some form of 0, such as 0, 0.0d, null, false, etc.)
So before the class is initialized (due to one of these reasons), the variables will hold an initial value.
According to the detailed initialization procedure (only the interesting steps are quoted here, and emphasis mine):
6.
[...] Then, initialize the final class variables and fields of interfaces whose values are compile-time constant expressions (§8.3.2.1, §9.3.1, §13.4.9, §15.28).
[...]
9.
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.
Let us look at step 6, with the 4 final class variables: stat1, str2, second, lazy.
Since 10 is constant expression, and so is "sdfff", and due to the order of execution, it is not possible to observe the initial value for str2 and stat1. In order to make an observation, the earliest you can do is in step 9.
The case of second demonstrate that when the right hand side is not a compile-time constant expression, so its initial value is visible.
The case of lazy is different, since the assignment is done in static block, and hence happen in step 9 - so it is possible to observe its initial value. (Well, the compiler checks carefully that lazy is assigned exactly once).
After the initialization of final class variables with compile-time constant expression comes the execution of static blocks and the rest of the initializers.
As you can see from the example, the static blocks and initialization happens according to textual order - demonstrated with the use of str variable - it is first printed out as null, then something, then crap.
Some may find it similar to the SO question Will Java Final variables have default values? but that answer doesn't completely solve this, as that question doesn't directly print the value of x within instance initializer block.
The problem arises when I try to print x directly inside the instance initializer block, while having assigned a value to x before the end of the block :
Case 1
class HelloWorld {
final int x;
{
System.out.println(x);
x = 7;
System.out.println(x);
}
HelloWorld() {
System.out.println("hi");
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
This gives a compile time error stating that variable x might not have been initialized.
$ javac HelloWorld.java
HelloWorld.java:6: error: variable x might not have been initialized
System.out.println(x);
^
1 error
Case 2
Instead of directly printing, I am calling a function to print:
class HelloWorld {
final int x;
{
printX();
x = 7;
printX();
}
HelloWorld() {
System.out.println("hi");
}
void printX() {
System.out.println(x);
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
This compiles correctly and gives output
0
7
hi
What is the conceptual difference between the two cases?
In the JLS, §8.3.3. Forward References During Field Initialization, its stated that there's a compile-time error when:
Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables
are in scope. Specifically, it is a compile-time error if all of the
following are true:
The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of C or an instance 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.
The following rules come with a few examples, of which the closest to yours is this one:
class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}
Accesses [to static or instance variables] by methods are not checked in this way, so the code above produces output 0, because the variable initializer for i uses the class method peek() to access the value of the variable j before j has been initialized by its variable initializer, at which point it still has its default value (§4.12.5 Initial Values of Variables).
So, to summarize, your second example compiles and executes fine, because the compiler does not check if the x variable was already initialized when you invoke printX() and when printX() actually takes place at Runtime, the x variable will be assigned with its default value (0).
Reading the JLS, the answer appears to be in section 16.2.2:
A blank final member field V is definitely assigned (and moreover is not definitely unassigned) before the block (§14.2) that is the body of any method in the scope of V and before the declaration of any class declared within the scope of V.
This means that when a method is called, the final field is assigned to its default value 0 before invoking it, so when you reference it inside the method, it compiles successfully and prints the value 0.
However, when you access the field outside of a method, it is considered unassigned, hence the compilation error. The following code will also not compile:
public class Main {
final int x;
{
method();
System.out.println(x);
x = 7;
}
void method() { }
public static void main(String[] args) { }
}
because:
V is [un]assigned before any other statement S of the block iff V is [un]assigned after the statement immediately preceding S in the block.
Since the final field x is unassigned before the method invocation, it is still unassigned after it.
This note in the JLS is also relevant:
Note that there are no rules that would allow us to conclude that V is definitely unassigned before the block that is the body of any constructor, method, instance initializer, or static initializer declared in C. We can informally conclude that V is not definitely unassigned before the block that is the body of any constructor, method, instance initializer, or static initializer declared in C, but there is no need for such a rule to be stated explicitly.
The difference is that in the first case you are calling System.out.println from initializer block so the block which is invoked before constructor. In the first line
System.out.println(x);
variable x is not yet initialized so that you get compilation error.
But in the second case you call instance method which doesn't know if variable has already been initialized so you don't have compilation error and you can see the default value for x
Ok, here is my 2 cents.
We all know that final variables can be initialized only While declaring or later on in constructors. Keeping that fact in mind, let see what happened here so far.
No errors Case:
So when you use inside a method, it have already a value.
1) If you initialize it, that value.
2) If not, the default value of data type.
Error case :
When you do that in an initialization block, which you are seeing errors.
If you look at the docs of initialization block
{
// whatever code is needed for initialization goes here
}
and
The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.
In compiler's eye, your code is literally equals to
class HelloWorld {
final int x;
HelloWorld() {
System.out.println(x); ------------ ERROR here obviously
x = 7;
System.out.println(x);
System.out.println("hi");
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
You are using it before even initializing it.
Case 1 :
Gives you a compile-error,
Because at System.out.println(x);
you are trying to print x which was never initialized.
Case 2:
Works because you are not directly using any literal values, instead you are calling some method, which is correct.
General Rule is,
If you are trying to access any variable which is never initialized
then it will give a compilation error.
We deal here with initializer block. The Java compiler copies initializer blocks into every constructor.
The compiler error don't occure in second example, because printing x is in another Frame, please refer to spec.
When I compile:
public static final boolean FOO = false;
public static final void fooTest() {
if (FOO) {
System.out.println("gg");
}
}
I get an empty method fooTest() {}. However when I compile:
static boolean isBar = false;
public static final boolean BAR = isBar;
public static final void fooTest() {
if (BAR) {
System.out.println("gg");
}
}
the if statement is included in the compiled class file. Does this mean there are two different "types" of static final in java, or is this just a compiler optimization?
In the first case, the compiler does an optimization. It knows Foo will always be false and kill the code than will never be reached.
In the second case, you are assigning the value of the non-final variable isBar to BAR. The compiler can't tell if the variable isBar has been modified somewhere else, especially if it is not private. Therefore it is not sure of the value of BAR. Therefore he can not do the optimization.
It is purely a case of compiler optimization where it ignores a direct assignment and does consider indirect assignment of literals.
In the first case static final fields are constants which are known at compile time. So the compiler optimizes and inlines the constant.
In the second case the field is non-final variable and cannot be inlined by the compiler as it can change.
class Test{
public static final boolean BOOL = true; //CONSTANT KNOWN AT COMPILE TIME
public void someMethod(){
while(BOOL){
//some code
}
}
//OPTIMIZED VERSION DONE BY COMPILER
public void someMethod(){
while(true){ //There is no need for accessing BOOL if it is not going to change and compiler understands that so inlines the value of constant
}
}
}
You can look at the compiled code using some de-compiler tools and you will find the optimized method that I have written in the class file.
There could be only one existing static value within the memory. In the first block, the static final variable FOO is set false value which in known to the compiler and hence it optimizes the if statement in it. But in second statement, the BAR final static variable is assigned isBar static variable value, which in turn makes the compiler not to optimize the if statement (since isBar could be any Boolean value and it cannot be determined because java supports polymorphism and values are assigned to variables dynamically during run time.), hence static variables memory position in the second block is important to the compiler to determine its value, where as in the first block the value false is known without any assignment between different variables and it is optimized.
I have problem understanding the order in which initialization happens. this is the order I assumed:
*Once per
1. Static variable declaration
2. Static block
*Once per object
3. variable declaration
4. initialization block
5. constructor
but according to this code I am obviously wrong:
class SomethingWrongWithMe
{
{
b=0; //no. no error here.
int a = b; //Error: Cannot reference a field before it is defined.
}
int b = 0;
}
And the error would disappear if I do this:
class SomethingWrongWithMe
{
int b = 0;
{
b=0;
int a = b; //The error is gone.
}
}
I can't figure out why isn't there an error on
b=0;
The Java Language Specification (section 8.3.2.3) says you can use a variable on the left hand side of an expression, i.e. assign to it, before it is declared, but you cannot use it on the right hand side.
All variables are initialized to their default values, then explicit initializers and anonymous blocks are run in the order they are found in the source file. Finally the constructor is called.
Statics are only run once on the first use of a class.
The compile error appears to be a rule of Java rather than something that necessarily makes sense in every case.
Variable definitions are not done "before" blocks. They are both done at the same time, in the order that they are defined
class SomethingWrongWithMe {
{
b = debug("block 1");
}
int b = debug("define");
{
b = debug("block 2");
}
private int debug(String str) {
System.out.println(str);
return 0;
}
}
Output
block 1
define
block 2
First of all, your assumptions are more or less correct, except for the fact that declarations (with initialization, such as int b = 0) and instance initializer blocks are executed in the order they are written.
int b = 0; // executed first
{
b = 1; // executed second
}
int a = b; // executed third
Also note that the declaration i.e. int b is not executed. The declaration just declares the existence of the variable.
As for the error you got (or, rather the error you didn't get) I agree that it looks strange. I assume that the compiler deals with referencing a variable in an expression and assigning a value to it in different ways. When writing to a variable in an instance initializer, it just checks that the variable is there, while when reading from it, it requires it to be declared above the instance initializer block. I'll see if I can find a reference for that in the JLS.