아래와 같이 간단한 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
자체 의 값 변경을 처리합니다 . 경우 변경 가능한 객체에 대한 참조가 있었다 (이 아니다)하고 콘텐츠 오브젝트가 변경되었다 블록 다음 그 변경은 리턴 값을 보일 것이다.finally
s
s
String
finally
이 모든 작동 방식에 대한 자세한 규칙 은 Java 언어 사양의 14.20.2 섹션에 나와 있습니다. return
명령문의 실행은 try
블록 의 갑작스러운 종료로 계산됩니다 ( ” 다른 이유로 인해 try 블록의 실행이 갑자기 완료되면 R …. “이 적용되는 섹션 ). 명령문이 갑작스러운 블록 종료 인 이유 는 JLS 14.17 섹션을 참조하십시오 return
.
더 자세하게 말하면, 진술로 인해 문장 의 try
블록과 finally
블록이 try-finally
갑자기 종료되면 return
§14.20.2의 다음 규칙이 적용됩니다.
try
다른 이유로 R [예외 발생] 외에 블록의 실행이 갑자기 완료되면finally
블록이 실행 된 다음 선택 사항이 있습니다.
- 상기 중간
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”문자열이 변경되지 않도록 보존했습니다. 실제로 여기에는 마침내 막히지 않습니다.
답변
여기에 주목할만한 두 가지가 있습니다.
- 문자열은 변경할 수 없습니다. s를 “override variable s”로 설정하면 s가 s 객체의 고유 char 버퍼를 “override variable s”로 변경하지 않고 인라인 된 String을 참조하도록 설정합니다.
- 호출 코드로 돌아 가기 위해 스택에 s에 대한 참조를 넣습니다. 그런 다음 (finally 블록이 실행될 때) 참조를 변경하면 스택에 이미있는 반환 값에 대해 아무 것도 수행하지 않아야합니다.
답변
테드의 요점을 증명하기 위해 코드를 약간 변경합니다.
보시다시피 출력 s
은 실제로 변경되었지만 반환 후에는 변경됩니다.
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);
}
}
산출:
Entry in finally Block
dev
override variable s
답변
기술적으로 말하면, return
try 블록 의 in은 finally
블록이 정의 된 경우 무시되지 않으며 , 마지막으로 블록에 포함 된 경우에만 무시됩니다 return
.
의심스러운 디자인 결정은 아마도 회고에서 실수 일 것입니다. 여러면에서이 동작은 무엇을 finally
의미 하는지에 대한 구어체 이해와 정확히 일치 try
합니다. “블록 에서 사전에 어떤 일이 발생하더라도 항상이 코드를 실행하십시오.” 따라서 finally
블록 에서 true를 반환 하면 전체 효과는 항상return s
‘아니요’ .
일반적으로 이는 좋은 관용구가 finally
아니며 자원을 정리 / 닫기 위해 블록을 자유롭게 사용해야 하지만 값을 반환하지 않는 경우는 거의 없습니다.
답변
이것을 시도하십시오 : s의 대체 값을 인쇄하려는 경우.
finally {
s = "override variable s";
System.out.println("Entry in finally Block");
return s;
}