development

finally 블록에서 반환 된 변수를 변경해도 반환 값이 변경되지 않는 이유는 무엇입니까?

big-blog 2020. 6. 15. 07:50
반응형

finally 블록에서 반환 된 변수를 변경해도 반환 값이 변경되지 않는 이유는 무엇입니까?


아래와 같이 간단한 Java 클래스가 있습니다.

public class Test {

    private String s;

    public String foo() {
        try {
            s = "dev";
            return s;
        } 
        finally {
            s = "override variable s";
            System.out.println("Entry in finally Block");  
        }
    }

    public static void main(String[] xyz) {
        Test obj = new Test();
        System.out.println(obj.foo());
    }
}

이 코드의 출력은 다음과 같습니다.

Entry in finally Block
dev  

블록 s에서 재정의되지 않고 finally인쇄 출력을 제어 하는 이유는 무엇 입니까?


try의 실행으로 완료 블록 return문과 값 s상기 시점에서 return문이 실행은 메소드에 의해 리턴 된 값이다. finally절이 나중에 s( return명령문이 완료된 후) 값을 변경 한다는 사실 은 그 시점에서 리턴 값을 변경하지 않습니다.

위의 내용 참조 하는 객체가 아니라 블록 s자체 의 값 변경을 처리합니다 . 경우 변경 가능한 객체에 대한 참조가 있었다 (이 아니다)하고 콘텐츠 오브젝트가 변경되었다 블록 다음 그 변경은 리턴 값을 보일 것이다.finallyssStringfinally

이 모든 작동 방식에 대한 자세한 규칙 은 Java 언어 사양의 14.20.2 섹션에 나와 있습니다. return명령문의 실행은 try블록 의 갑작스러운 종료로 계산됩니다 ( " try 블록의 실행이 다른 이유로 갑자기 완료 되는 경우 R .... "이 적용되는 섹션 ). 명령문이 갑작스러운 블록 종료 인 이유 는 JLS 14.17 섹션을 참조하십시오 return.

더 자세하게 말하면, 진술로 인해 문장 try블록과 finally블록이 try-finally갑자기 종료되면 return§14.20.2의 다음 규칙이 적용됩니다.

tryR이 [예외를 던지는 것 외에] 다른 이유로 갑자기 블록의 실행이 완료되면 finally블록이 실행 된 다음 선택 사항이 있습니다.

  • 은 if finally블록은 다음, 정상적으로 완료 try이유 R.위한 문이 완료 갑자기
  • 상기 중간 finally블록이 이유에 대해 S 급격 완료 후 try갑자기 이유 S에 대한 문이 완료 (이성 R은 폐기된다).

결과적으로 블록 return명령문 finally이 전체 try-finally명령문 의 리턴 값을 결정 하고 블록의 리턴 된 값 try이 삭제됩니다. 비슷한 일이 발생 try-catch-finally경우 문 try블록, 그것이 의해 잡힌 예외가 발생 catch블록을, 그리고 두 catch블록과 finally블록이 return문을.


마지막으로 호출하기 전에 반환 값이 스택에 배치되기 때문입니다.


바이트 코드를 살펴보면 JDK가 크게 최적화되었으며 foo () 메서드가 다음과 같이 나타납니다.

String tmp = null;
try {
    s = "dev"
    tmp = s;
    s = "override variable s";
    return tmp;
} catch (RuntimeException e){
    s = "override variable s";
    throw e;
}

그리고 바이트 코드 :

0:  ldc #7;         //loading String "dev"
2:  putstatic   #8; //storing it to a static variable
5:  getstatic   #8; //loading "dev" from a static variable
8:  astore_0        //storing "dev" to a temp variable
9:  ldc #9;         //loading String "override variable s"
11: putstatic   #8; //setting a static variable
14: aload_0         //loading a temp avariable
15: areturn         //returning it
16: astore_1
17: ldc #9;         //loading String "override variable s"
19: putstatic   #8; //setting a static variable
22: aload_1
23: athrow

Java는 리턴하기 전에 "dev"문자열이 변경되지 않도록 보존했습니다. 실제로 여기에는 마침내 막히지 않습니다.


여기에 주목할만한 두 가지가 있습니다.

  • Strings are immutable. When you set s to "override variable s", you set s to refer to the inlined String, not altering the inherent char buffer of the s object to change to "override variable s".
  • You put a reference to the s on the stack to return to the calling code. Afterwards (when the finally block runs), altering the reference should not do anything for the return value already on the stack.

I change your code a bit to prove the point of Ted.

As you can see in the output s is indeed changed but after the return.

public class Test {

public String s;

public String foo() {

    try {
        s = "dev";
        return s;
    } finally {
        s = "override variable s";
        System.out.println("Entry in finally Block");

    }
}

public static void main(String[] xyz) {
    Test obj = new Test();
    System.out.println(obj.foo());
    System.out.println(obj.s);
}
}

Output:

Entry in finally Block 
dev 
override variable s

Technically speaking, the return in the try block won't be ignored if a finally block is defined, only if that finally block also includes a return.

It's a dubious design decision that was probably a mistake in retrospect (much like references being nullable/mutable by default, and, according to some, checked exceptions). In many ways this behaviour is exactly consistent with the colloquial understanding of what finally means - "no matter what happens beforehand in the try block, always run this code." Hence if you return true from a finally block, the overall effect must always to be to return s, no?

In general, this is seldom a good idiom, and you should use finally blocks liberally for cleaning up/closing resources but rarely if ever return a value from them.


Try this: If you want to print the override value of s.

finally {
    s = "override variable s";    
    System.out.println("Entry in finally Block");
    return s;
}

참고URL : https://stackoverflow.com/questions/16030858/why-does-changing-the-returned-variable-in-a-finally-block-not-change-the-return

반응형