[java] 자바의 무한 루프

whileJava에서 다음 무한 루프를 살펴보십시오 . 아래 명령문에 대해 컴파일 타임 오류가 발생합니다.

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

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

그러나 다음과 같은 무한 while루프는 잘 작동하며 방금 조건을 부울 변수로 바꾼 오류가 발생하지 않습니다.

boolean b=true;

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

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

두 번째 경우에도 부울 변수 b가 true 이므로 컴파일러가 전혀 불평하지 않기 때문에 루프 뒤의 명령문에 분명히 도달 할 수 없습니다 . 왜?


편집 : 다음 버전은 while명백한 것처럼 무한 루프에 갇히지 만 if루프 내의 조건이 항상 false이며 결과적으로 루프가 반환되지 않고 컴파일러에 의해 결정될 수 있음에도 불구하고 그 아래의 명령문에 대해 컴파일러 오류가 발생 하지 않습니다. 컴파일 타임 자체.

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.

편집 : 와 같은 것 if하고 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.
}

다음 버전 while도 무한 루프에 빠집니다.

while(true) {

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

이는 블록 자체 내에서 명령문이 먼저 만나 finally더라도 블록이 항상 실행 return되기 때문 try입니다.



답변

컴파일러는 첫 번째 표현식이 항상 무한 루프를 생성 한다는 것을 쉽고 분명하게 증명할 수 있지만 두 번째 표현식 은 쉽지 않습니다. 장난감 예제에서는 간단하지만 다음과 같은 경우에는 어떻게됩니까?

  • 변수의 내용을 파일에서 읽었습니까?
  • 변수가 로컬이 아니고 다른 스레드에서 수정할 수 있습니까?
  • 변수가 일부 사용자 입력에 의존합니까?

컴파일러는 그 길을 완전히 포기하기 때문에 더 간단한 경우를 확인하지 않습니다. 왜? 사양에서 금지하는 것이 훨씬 더 어렵 기 때문 입니다. 섹션 14.21 참조 :

(그런데 내 컴파일러 변수가 선언되면 불평합니다 final.)


답변

사양 에 따르면 while 문에 대해 다음과 같이 말합니다.

while 문은 다음 중 하나 이상이 참인 경우 정상적으로 완료 될 수 있습니다.

  • while 문에 도달 할 수 있고 조건식이 true 값을 갖는 상수식이 아닙니다.
  • while 문을 종료하는 도달 가능한 break 문이 있습니다. \

따라서 컴파일러는 while 조건이 참 값을 갖는 상수이거나 while 내에 break 문이있는 경우에만 while 문 다음에 오는 코드에 도달 할 수 없다고 말합니다. 두 번째 경우 b의 값이 상수가 아니기 때문에 그 뒤에 오는 코드에 도달 할 수 없다고 간주하지 않습니다. 해당 링크 뒤에는 도달 할 수없는 것으로 간주되는 항목과 그렇지 않은 항목에 대한 자세한 정보를 제공하는 훨씬 더 많은 정보가 있습니다.


답변

true는 상수이고 b는 루프에서 변경 될 수 있기 때문입니다.


답변

변수 상태를 분석하는 것은 어렵 기 때문에 컴파일러는 거의 포기하고 원하는 것을 할 수 있습니다. 또한 Java 언어 사양에는 컴파일러가 도달 할 수없는 코드를 감지하는 방법에 대한 명확한 규칙 이 있습니다 .

컴파일러를 속이는 방법에는 여러 가지가 있습니다. 또 다른 일반적인 예는

public void test()
{
    return;
    System.out.println("Hello");
}

컴파일러는 해당 영역이 반응 할 수 없다는 것을 인식하므로 작동하지 않습니다. 대신 할 수 있습니다.

public void test()
{
    if (2 > 1) return;
    System.out.println("Hello");
}

이것은 컴파일러가 표현식이 결코 거짓이 아니라는 것을 인식 할 수 없기 때문에 작동합니다.


답변

후자는 도달 할 수 없습니다. 부울 b는 루프 내부 어딘가에서 종료 조건을 유발하는 거짓으로 변경 될 가능성이 있습니다.


답변

내 생각에 변수 “b”는 그 값을 변경할 가능성이 있으므로 컴파일러 System.out.println("while terminated");
가 도달 할 수 있다고 생각 합니다.


답변

컴파일러는 완벽하지 않습니다.

컴파일러의 책임은 실행을 확인하는 것이 아니라 구문을 확인하는 것입니다. 컴파일러는 강력한 형식의 언어에서 많은 런타임 문제를 궁극적으로 포착하고 방지 할 수 있지만 이러한 오류를 모두 포착 할 수는 없습니다.

실용적인 솔루션은 컴파일러 검사를 보완하기 위해 단위 테스트 배터리를 사용하거나 기본 변수 및 중지 조건에 의존하는 대신 강력한 것으로 알려진 논리를 구현하기 위해 객체 지향 구성 요소를 사용하는 것입니다.

강력한 타이핑 및 OO : 컴파일러의 효율성 향상

일부 오류는 본질적으로 구문 적이며 Java에서는 강력한 유형 지정으로 인해 많은 런타임 예외를 포착 할 수 있습니다. 그러나 더 나은 유형을 사용하면 컴파일러가 더 나은 논리를 적용하도록 도울 수 있습니다.

컴파일러가 로직을보다 효과적으로 적용하기를 원하는 경우 Java에서 솔루션은 이러한 로직을 적용 할 수있는 강력하고 필수 객체를 빌드하고 이러한 객체를 사용하여 기본 요소가 아닌 애플리케이션을 빌드하는 것입니다.

이에 대한 전형적인 예는 반복자 패턴을 사용하는 것입니다. Java의 foreach 루프와 결합 된이 구조는 단순한 while 루프보다 설명하는 버그 유형에 덜 취약합니다.