2013-05-26 2 views
4

나는 매우 비슷한 질문을하고 이미 대답했다는 것을 알고 있으며, 나는 찾을 수 있었지만 여전히 100 % 명확하지 않은 것을 읽었다.로컬 변수를 루프에서 final로 선언하기

public static void fooMethod { 

    while(<...>) { 
    .... 
    final int temp = <something>; 
    .... 
    } 
} 

없음 내부 클래스, 아무것도 특별하거나 특이한 :

이 코드를 고려. 나에게 반 직관적 인 것처럼 보입니다.

위의 샘플에서 지역 변수 final을 선언하는 것이 목적이 있습니까?

여기서 정확히 final 컴파일러의 유무에 관계없이 정확하게 동일한 바이트 코드가 생성된다는 것을 알고 있습니까?

여기에 뭔가가 있습니까? RTFM 사례 인 경우 올바른 방향으로 안내해주세요.

(내가 할 수있는 경우)

내가 무엇을 얻을 및/또는 재 작성 (temp가 원시적 될 필요가 없습니다 것을 이해와) 같은 잃을 않는 후속 질문?

public static void fooMethod2 { 

    int temp; 
    while(<...>) { 
    .... 
    temp = <something>; 
    .... 
    } 
} 
+1

아마 다른 곳과 같은 목적으로 사용됩니다 : 변수를 수정하려고하면 컴파일러가 멈 춥니 다. – Cairnarvon

+0

이 변수는 * 매우 * 로컬 변수입니다 (루프 본문에만 해당). 동일한 루프의 아무 곳에서도 참조되지 않습니다. 다른 목적으로 재사용을 허용하지 않음으로써 가독성을 보호합니까? –

답변

9

:final 키워드, 지역 변수에 사용 매개 변수 때 , 생성 된 바이트 코드 (.class 파일)로 만들지 않으며 예상대로 런타임 동안 사용하지 않습니다. (컴파일 시간, 그것은하지만, 아래에 확인하는 diference을 만들 수 있습니다.) 이러한 경우에

인해 익명의 내부 클래스에 적용되지 않습니다 때, 그것은 스타일 선택은 단지 의도 문서화에 유용하다 변수의 범위.

아래의 테스트에서 해당 정보를 확인합니다.



1 : 컴파일러가 final를 사용하여, 그것의 무언가를 할 수있는 경우가 있습니다 차이 :

이 조각에서

봐 :

boolean zZ = true; 
while (zZ) { 
    int xX = 1001;   // <------------- xX 
    int yY = 1002;   // <------------- yY 
    zZ = (xX == yY); 
} 

int 변수 xXyY. 처음으로 final으로 선언하고 두 번째로 final을 모두 삭제했습니다.

모두 final :

 0: iconst_1    // pushes int 1 (true) onto the stack 
    1: istore_1    // stores the int on top of the stack into var zZ 
    2: goto   15 
    5: sipush  1001 // pushes 1001 onto the operand stack 
    8: istore_2    // stores on xX 
    9: sipush  1002 // pushes 1002 onto the operand stack 
    12: istore_3    // stores on yY 
    13: iconst_0    // pushes 0 (false): does not compare!! <--------- 
    14: istore_1    // stores on zZ 
    15: iload_1    // loads zZ 
    16: ifne   5  // goes to 5 if top int (zZ) is not 0 
    19: return   

모두 비 final : 상기 경우

// 0: to 12: all the same 
    13: iload_2    // pushes xX onto the stack 
    14: iload_3    // pushes yY onto the stack 
    15: if_icmpne  22  // here it compares xX and yY! <------------ 
    18: iconst_1  
    19: goto   23 
    22: iconst_0  
    23: istore_1  
    24: iload_1  
    25: ifne   5 
    28: return   

그들이 final이다 컴파일러 여기 (javap -c 인쇄) 생성 된 바이트 코드는 그들이 동등하지 않으며 결코 그들을 비교하지 않는다는 것을 알고있다 (false는 바이트 코드에서 생성된다 xX == yY은이다).

final을 사용할 때 생성 된 코드에 대해에서 수행 할 수있는 컴파일러는 이며, 바이트 코드 방식으로 결론을 내릴 수 있습니다. (나는 그들이 의미가 있지만, 확실히 final 위해 여기뿐만 아니라 스타일의 선택을 말하는 게 아니에요.)


2 : 컴파일러가 어떤 결론을 내릴 수없는 경우, 현지 바르에 final를 사용하여 심지어 final, 컴파일러 C를 ​​사용하여,이 경우

boolean zZ = true; 
int aA = 1001; 
int bB = 1002; 
while (zZ) { 
    final int xX = aA; // <------- took away the "final" here, didnt matter 
    final int yY = bB; // <------- took away the "final" here, didnt matter 
    zZ = (xX == yY); 
} 

:

이제 다음 코드를 가지고 : 단지 디자인 선택 주석은 xXyY이 같다면 컴파일러 시간을 알려주지, 그렇지? 이 때문에

, 우리는 볼 수 있습니다 생성 된 바이트 코드 정확히 같은 우리가 또는 final없이 클래스 을 생성 할 때 (같은 MD5!).

동안은 일반적인 경우, some say 지역 블록에 final, 을 사용하는 성능상의 이점이 있다는 것을 others disagree에, final 확실히 만 스타일 선택입니다.


3 : 내부의 지역 변수 또는 외부 루프 - 전혀 차이 :

이 조각의 생성 된 바이트 코드 ...

boolean zZ = true; 
int aA = 1001, bB = 1002; 
while (zZ) { 
    int xX = aA;      // <--- declaration is inside WHILE 
    int yY = bB; 
    zZ = (xX == yY); 
} 

...이 조각에 대해 생성 된 바이트 코드 ...
boolean zZ = true; 
int aA = 1001, bB = 1002; 
int xX, yY;       // <--- declaration is outside WHILE 
while (zZ) { 
    xX = aA; 
    yY = bB; 
    zZ = (xX == yY); 
} 

이 ... 정확히 같은 (만 줄 번호는 물론, 변경)된다.

다른 유형의 객체 (원시 유형 변수 만이 아님)를 사용한 다른 테스트에서도 같은 결과가 나타났습니다.

안에 지역 변수를 선언하거나 외부없이 바이트 코드 효과,이 꽤 많이 디자인 선택입니다 루프, 다른 곳에서 사용하지 않을 경우, 다음, 결론을하는 것이 안전합니다.

참고 : 모든 테스트는 Oracle JRE 버전 1.7.0_13에서 수행되었습니다.

+0

1) 바이트 코드 파일을보고 코드를 최적화하는 자바의 능력에 대한 결론을 내리는 것은 현명하지 못합니다. 심각한 최적화 작업은 런타임시 JIT 컴파일러에 의해 수행됩니다. 2) 사실 JIT 컴파일러는 변수가 특정 시점을 넘어서도 변경되지 않으면 완벽하게 해결할 수 있습니다. '최종'을 추가하면 도움이 될 것 같지 않습니다. –

+0

@StephenC 입력 해 주셔서 감사합니다. 정말로 바이트 코드 파일에서 결론을 이끌어내는 것은 현명하지 못한 것처럼 보입니다. 그러나 당신은 생성 된 바이트 코드가 동일하다고 결론을 내렸다. JIT가 가져가는 순간, 그는 거기에 '최종'키워드가 있는지 여부를 알 수 없습니다. 그래서 변화를 가져올 수 없습니다. 어떻게 생각해? – acdcjunior

+1

정확합니다. 차이점이 있다면 이론적으로 다른 디스크립터로 인코딩 될 수 있지만 JVM 스펙에 대한 간략한 설명은 그렇지 않다 ... –

4

final은 상수 변수에 대한 키워드입니다. final로 선언하면 나중에 루프 내부에서 재 할당 할 수 없습니다.

temp은 최종 여부에 관계없이 모든 반복마다 다시 선언됩니다.

예를 들어

:

while (...) 
{ 
    final int temp = ...; 

    temp = 5; // compiler error 
} 

그러나 경우에 그 (최종) 일정하지 : 몇 즉

while (...) 
{ 
    int temp = ...; 

    temp = 5; // fine 
} 
+0

그래서이 메모는 "이 변수를 수정할 의사가 없습니다"라는 메모에 불과합니다. 그리고 런타임에 영향을 미치지 않습니까? –

+0

@ PM77-1 그 질문에 대한 좋은 대답은 다음과 같습니다. http://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance – Supericy

+0

감사합니다. 아직 보지 못한이 하나. –

1

는 completly 다른 관점에서 이것을 고려 : 기능 프로그래밍 언어에서, 거의 모든 과제는 최종 결과이며 일반적인 경우이고, 클래스는 불변있다. 즉, 비 최종 과제 및/또는 변경 가능한 클래스는 예외입니다.

코드가 스칼라로 작성된 경우 IntelliJ IDE에 "이 할당을 최종으로 변경할 수 있음"이라는 힌트가 표시됩니다.

"결승"정말 고맙습니다. 왜냐하면 나중에 코드를 읽으면이 할당으로 인해 일부 줄이 더 아래로 변경되지 않기 때문입니다. 인스턴스가 변경되지 않는다는 것을 안다면, 이것은 도움이 될 것입니다.

"결승전"을 일관되게 사용하면 마지막이 아닌 결승전이 가시성을 얻게되며 일반적으로 이러한 변수가 관찰에 가장 중요합니다.