Infinite loops in Java

2019-01-31 22:31发布

Look at the following infinite while loop in Java. It causes a compile-time error for the statement below it.

while(true) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //Unreachable statement - compiler-error.

The following same infinite while loop, however works fine and doesn't issue any errors in which I just replaced the condition with a boolean variable.

boolean b=true;

while(b) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.

In the second case also, the statement after the loop is obviously unreachable because the boolean variable b is true still the compiler doesn't complain at all. Why?


Edit : The following version of while gets stuck into an infinite loop as obvious but issues no compiler errors for the statement below it even though the if condition within the loop is always false and consequently, the loop can never return and can be determined by the compiler at the compile-time itself.

while(true) {

    if(false) {
        break;
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.

while(true) {

    if(false)  { //if true then also
        return;  //Replacing return with break fixes the following error.
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.

while(true) {

    if(true) {
        System.out.println("inside if");
        return;
    }

    System.out.println("inside while"); //No error here.
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.

Edit : Same thing with if and while.

if(false) {
    System.out.println("inside if"); //No error here.
}

while(false) {
    System.out.println("inside while");
    // Compiler's complain - unreachable statement.
}

while(true) {

    if(true) {
        System.out.println("inside if");
        break;
    }

    System.out.println("inside while"); //No error here.
}      

The following version of while also gets stuck into an infinite loop.

while(true) {

    try {
        System.out.println("inside while");
        return;   //Replacing return with break makes no difference here.
    } finally {
        continue;
    }
}

This is because the finally block is always executed even though the return statement encounters before it within the try block itself.

15条回答
Emotional °昔
2楼-- · 2019-01-31 22:59

I'm surprised your compiler refused to compile the first case. That seems strange to me.

But the second case isn't optimized to the first case because (a) another thread might update the value of b (b) the called function might modify the value of b as a side effect.

查看更多
Anthone
3楼-- · 2019-01-31 23:04

Compilers aren't perfect - nor should they be

The responsibility of the compiler is to confirm syntax - not to confirm execution. Compilers can ultimately catch and prevent many run time problems in a strongly typed language - but they cannot catch all such errors.

The practical solution is to have batteries of unit tests to complement your compilers checks OR use object oriented components for implementing logic that are known to be robust, rather then relying on primitive variables and stop conditions.

Strong Typing and OO : increasing compiler's efficacy

Some errors are syntactical in nature - and in Java, the strong typing makes a lot of run time exceptions catchable. But, by using better types, you can help your compiler to enforce better logic.

If you want the compiler to enforce logic more effectively, in Java, the solution is to build robust, required objects that can enforce such logic, and using those objects to build up your application, rather than primitives.

A classic example of this is the use of the iterator pattern, combined with Java's foreach loop this construct is less vulnerable to the type of bug you illustrate than a simplistic while loop.

查看更多
小情绪 Triste *
4楼-- · 2019-01-31 23:04

Actually I don't think anyone got it QUITE right (at least not in the original questioner's sense). The OQ keeps mentioning:

Correct, but irrelevant, as b is NOT being changed in the loop

But it doesn't matter because the last line IS reachable. If you took that code, compiled it into a class file and handed the class file to someone else (say as a library), they could link the compiled class with code that modifies "b" through reflection, exiting the loop and causing the last line to execute.

This is true of any variable that isn't a constant (or final which compiles to a constant in the location where it's used--sometimes causing bizarre errors if you recompile the class with the final and not a class that references it, the referencing class will still hold the old value without any errors whatsoever)

I've used the ability of reflection to modify non-final private variables of another class to monkey-patch a class in a purchased library--fixing a bug so we could continue developing while we waited for official patches from the vendor.

By the way, this may not actually work these days--although I've done it before, there is a chance that such a small loop will be cached in the CPU cache and since the variable is not marked volatile the cached code may never pick up the new value. I've never seen this in action but I believe it's theoretically true.

查看更多
登录 后发表回答