itsource

Java에서 로컬 변수가 초기화되지 않는 이유는 무엇입니까?

mycopycode 2023. 1. 28. 09:35
반응형

Java에서 로컬 변수가 초기화되지 않는 이유는 무엇입니까?

자바 설계자가 로컬 변수에 기본값을 부여해서는 안 된다고 느낀 이유가 있습니까?인스턴스 변수에 기본값을 지정할 수 있다면 로컬 변수에 대해서도 동일한 값을 지정할 수 없습니다.

, 블로그 투고에의 코멘트에 기재되어 있는 것처럼, 문제의 원인이 되기도 합니다.

이 규칙은 최종 블록에서 리소스를 닫으려고 할 때 가장 짜증납니다.시도 중에 리소스를 인스턴스화했지만 마지막 시도 내에 리소스를 닫으려고 하면 이 오류가 발생합니다.인스턴스화를 시행 범위 밖으로 이동하면 시행 범위 내에 있어야 한다는 오류가 다시 표시됩니다.

너무 답답해요.

로컬 변수는 대부분 계산을 수행하기 위해 선언됩니다.따라서 변수의 값을 설정하는 것은 프로그래머의 결정이며 기본값은 사용하지 않아야 합니다.

프로그래머가 실수로 로컬 변수를 초기화하지 않고 기본값으로 설정한 경우 출력은 예기치 않은 값이 될 수 있습니다.따라서 로컬 변수의 경우, 컴파일러는 정의되지 않은 값의 사용을 피하기 위해 변수에 액세스하기 전에 프로그래머에게 값을 사용하여 초기화를 요구합니다.

링크하고 있는 「문제」는, 이 상황을 나타내고 있는 것 같습니다.

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

코멘터의 불만은 컴파일러가 다음 라인에서 멈춘다는 것입니다.finally다음과 같이 주장합니다.so초기화되지 않았을 수 있습니다.또 언급을 하고 있는데, 으로 되어 있을 거예요.

// Do some work here ...
SomeObject so = new SomeObject();
try {
  so.DoUsefulThings();
} finally {
  so.CleanUp();
}

그러면 컴파일러는 코드가 "시행 범위 내에 있어야 한다"고 말하기 때문에 코멘터는 그 솔루션에 만족하지 않습니다.즉, 일부 코드가 더 이상 처리되지 않는 예외를 발생시킬 수 있습니다.잘 모르겠어요.어느 버전의 코드도 예외를 처리하지 않으므로 첫 번째 버전의 예외와 관련된 모든 것은 두 번째 버전에서도 동일하게 작동합니다.

어쨌든, 이 두 번째 버전의 코드가 올바른 작성 방법입니다.첫 번째 버전에서는 컴파일러 오류 메시지가 정확했습니다.so이치노 ' '의 경우, " 입니다.SomeObject실패하여 """가 실패함so되지 않기 에 에러로 콜을 호출하려고 .so.CleanUp 를 .try리소스 취득 후 섹션은 다음과 같습니다.finally섹션이 완성됩니다.

try-finally를 치다so초기화가 이루어지는 은, 단지,SomeObject예를 들어, 어떤 일이 일어나더라도 확실하게 정리할 수 있도록 합니다.실행할 필요가 있는 다른 것이 있지만 이러한 것들이 다음 중 하나인지 아닌지와 관련이 없는 경우SomeObject인스턴스는 속성이 할당되었으므로 다른 인스턴스로 이동해야 합니다. try-finally록, 、 마마 、 제제제린린블 。

사용하기 전에 변수를 수동으로 할당하도록 요구해도 실제 문제는 발생하지 않습니다.사소한 번거로움으로 이어지겠지만, 당신의 코드가 더 나을 거예요.좀 더 것이고, 더 제한적인 변수들이 있을 입니다.try-finally너무 많이 보호하려고 하지 않는 블록입니다.

되어 있는 는, 「」를 해 주세요.so에서는 '''가 있을 것입니다.null에서 가 발생하는 것이 컴파일 시간 오류가 발생합니다.finally '블럭이 됩니다.NullPointerException코드의 '여기서 작업하기' 섹션에서 발생할 수 있는 다른 예외를 숨길 수 있는 경우(또는 예외 실행)finally섹션은 이전 예외에 자동으로 연결됩니까?기억이 안 나요.그렇다고 해도, 실제의 경우는 예외입니다.)

또한 다음 예에서는 SomeObject 구성 내에서 예외가 발생했을 수 있습니다.이 경우 'so' 변수는 null이 되고 CleanUp에 대한 호출은 NullPointer를 느려집니다.예외.

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

제가 하는 일은 다음과 같습니다.

SomeObject so = null;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  if (so != null) {
     so.CleanUp(); // safe
  }
}

최종 인스턴스/멤버 변수는 기본적으로 초기화되지 않습니다.왜냐하면 그것들은 최종적인 것이고 이후 프로그램에서 변경할 수 없기 때문이다.이것이 바로 자바가 디폴트값을 부여하지 않고 프로그래머에게 초기화를 강요하는 이유입니다.

한편, 비최종 멤버 변수는 나중에 변경할 수 있습니다.따라서 컴파일러는 나중에 변경할 수 있기 때문에 초기화되지 않은 상태로 두지 않습니다.로컬 변수에 대해서는 로컬 변수의 범위가 훨씬 좁고 컴파일러는 언제 사용되는지 알고 있습니다.따라서 프로그래머가 변수를 강제로 초기화하는 것은 의미가 있습니다.

이 질문에 대한 실제 답변은 스택 포인터에 숫자를 추가하는 것만으로 메서드 변수가 인스턴스화되기 때문입니다.0으로 만드는 것은 추가 단계일 것이다.클래스 변수의 경우 힙의 초기화 메모리에 저장됩니다.

한 걸음 더 나아가는 게 어때요?한 발짝 물러서세요.이 경우의 '경고'가 '매우 좋은 것'이라고 말하는 사람은 아무도 없습니다.

첫 번째 패스(처음 코드화할 때)에서는 변수를 0 또는 null로 초기화하지 마십시오.실제 값에 할당하거나 아예 할당하지 마십시오. 할당하지 않으면 Java가 실제로 언제 실패했는지 알려줄 수 있습니다.전기승려의 대답을 좋은 예로 들어보자.첫 번째 경우, SomeObject의 컨스트럭터가 예외를 발생시켰기 때문에 try()가 실패하면 최종적으로 NPE가 발생한다는 것을 알려주는 것은 실제로 매우 유용합니다.컨스트럭터가 예외를 설정할 수 없는 경우는 시행하지 않는 것이 좋습니다.

이 경고는 모든 경로를 체크하고 변수를 어떤 경로에서 사용했을 경우 해당 경로로 연결되는 모든 경로에서 초기화할 수 있도록 하기 때문에 바보 같은 짓으로부터 저를 구해준 훌륭한 다중 경로 불량 프로그래머 검사입니다.이제 올바른 작업이라고 판단할 때까지 변수를 명시적으로 초기화하지 않습니다.

또, 「int size」보다 「int size=0」이라고 명시적으로 말하고, 다음의 프로그래머에게 「0」이라고 하는 것이 좋지 않은가.

반대로 컴파일러가 초기화되지 않은 모든 변수를 0으로 초기화해야 하는 타당한 이유를 찾을 수 없습니다.

C/C++와의 유사성을 유지하는 것이 주된 목적이라고 생각합니다.그러나 컴파일러는 초기화되지 않은 변수를 사용하여 문제를 최소로 줄일 수 있음을 감지하고 경고합니다.퍼포먼스의 관점에서는 컴파일러가 다음 스테이트먼트에서 변수 값을 덮어쓰더라도 할당 스테이트먼트를 쓸 필요가 없기 때문에 초기화되지 않은 변수를 선언하는 것이 조금 더 빠릅니다.

변수를 초기화하지 않는 것이 더 효율적이며 로컬 변수의 경우 초기화를 컴파일러에 의해 추적할 수 있기 때문에 그렇게 하는 것이 안전합니다.

초기화할 변수가 필요한 경우 언제든지 사용자가 직접 할 수 있으므로 문제가 없습니다.

저에게 있어서, 그 이유는 다음과 같습니다.로컬 변수의 용도가 인스턴스 변수의 용도와 다릅니다.로컬 변수는 계산의 일부로 사용되며 인스턴스 변수는 상태를 포함합니다.값을 할당하지 않고 로컬 변수를 사용하는 경우 이는 거의 논리 오류입니다.

즉, 인스턴스 변수를 항상 명시적으로 초기화하도록 요구하는 것은 완전히 늦어질 수 있습니다.이 오류는 초기화되지 않은 인스턴스 변수를 허용하는 모든 컨스트럭터에서 발생합니다(예를 들어 선언 시 초기화되지 않고 컨스트럭터 내에서 초기화되지 않음).하지만 그것은 고슬링 등이 90년대 초에 내린 결정이 아니다.그래서 이렇게 된 것이다.(그리고 나는 그들이 잘못된 결정을 내렸다고 말하는 것이 아니다.)

단, 로컬 변수의 디폴트를 되돌릴 수 없었습니다.네, 컴파일러에 의존하여 논리를 재확인할 필요는 없습니다.하지만 컴파일러가 논리를 검출할 때 편리합니다. :- )

로컬 변수의 배후에 있는 개념은 로컬 변수가 필요한 제한된 범위 내에서만 존재한다는 것입니다.따라서 가치 또는 적어도 그 가치가 어디서 나오는지에 대해서는 불확실성의 이유가 거의 없어야 한다.로컬 변수의 디폴트값을 가지는 것으로부터, 많은 에러가 발생하는 것을 상상할 수 있었습니다.

예를 들어 다음과 같은 간단한 코드를 고려합니다. (N.B. 로컬 변수가 명시적으로 초기화되지 않은 경우 지정된 대로 기본값이 할당된다고 가정합니다.)

System.out.println("Enter grade");
int grade = new Scanner(System.in).nextInt(); // I won't bother with exception handling here, to cut down on lines.
char letterGrade; // Let us assume the default value for a char is '\0'
if (grade >= 90)
    letterGrade = 'A';
else if (grade >= 80)
    letterGrade = 'B';
else if (grade >= 70)
    letterGrade = 'C';
else if (grade >= 60)
    letterGrade = 'D';
else
    letterGrade = 'F';
System.out.println("Your grade is " + letterGrade);

컴파일러가 기본값인 '0'을 letterGrade할당했다고 가정하면 이 코드는 정상적으로 동작합니다.하지만, 만약 우리가 다른 문장을 잊어버렸다면?

코드를 테스트하면 다음과 같은 결과가 발생할 수 있습니다.

Enter grade
43
Your grade is

이 결과는 예상된 것이지만, 코드 작성자의 의도는 분명 아니었다.실제로, 아마도 대부분의 경우(또는 적어도 상당수의 경우)에서, 디폴트 은 원하는 값이 아닐 것이기 때문에, 대부분의 경우 디폴트 값은 오류를 초래할 것이다.디버깅이 실패하면 디버깅에 문제가 생기기 때문에 코더가 로컬 변수를 사용하기 전에 초기값을 강제로 할당하도록 하는 것이 더 합리적입니다.= 1for(int i = 1; i < 10; i++) to = 0for(int i; i < 10; i++).

try-catch-finally 블록은 예를 들어 오브젝트가 컨스트럭터 내에서 체크된 예외를 발생시킬 때 약간 혼란스러울 수 있습니다(그러나 인용문처럼 실제로는 catch-22가 아닙니다).다만, 어떤 이유로든 최종적으로 블록의 끝에 있는 이 오브젝트에 대해 조치를 취해야 합니다.이를 위한 완벽한 예는 리소스를 처리할 때이며, 이 리소스는 닫아야 합니다.

과거에 이런 걸 해결할 수 있는 방법 중 하나는...

Scanner s = null; // Declared and initialized to null outside the block. This gives us the needed scope, and an initial value.
try {
    s = new Scanner(new FileInputStream(new File("filename.txt")));
    int someInt = s.nextInt();
} catch (InputMismatchException e) {
    System.out.println("Some error message");
} catch (IOException e) {
    System.out.println("different error message");
} finally {
    if (s != null) // In case exception during initialization prevents assignment of new non-null value to s.
        s.close();
}

그러나 Java 7 이후로는 이 블록은 더 이상 리소스 사용 시 필요하지 않습니다.

try (Scanner s = new Scanner(new FileInputStream(new File("filename.txt")))) {
    ...
    ...
} catch(IOException e) {
    System.out.println("different error message");
}

즉, (이름에서 알 수 있듯이) 이것은 리소스로만 작동합니다.

그리고 앞의 예는 조금 불쾌하지만, 이는 로컬 변수와 그 구현 방법에 대한 설명보다 최종 테스트 캐치 또는 이러한 클래스가 구현되는 방법에 대해 더 많이 설명합니다.

필드가 기본값으로 초기화되는 것은 사실이지만, 이것은 조금 다릅니다. 예를 들어, '아까', '아까', '아까', 이렇게 int[] arr = new int[10];이 어레이를 초기화하는 즉시 오브젝트는 메모리 내의 지정된 위치에 존재합니다.잠시 디폴트값이 없다고 가정해 보겠습니다.초기값은 그 메모리 위치에 있는1과 0의 시리즈가 됩니다.이것은 많은 경우에 비결정적인 행동을 일으킬 수 있다.

만약 우리가...

int[] arr = new int[10];
if(arr[0] == 0)
    System.out.println("Same.");
else
    System.out.println("Not same.");

이 있을 이다.Same.할 수 .Not same.을 사용하다참조 변수에 대해 말하기 시작하면 이 문제는 더욱 심각해질 수 있습니다.

String[] s = new String[5];

정의에 따르면 의 각 요소는 String(또는 늘)을 가리켜야 합니다.그러나 초기값이 이 메모리 위치에서 발생한 일련의 0과 1의 경우 매번 같은 결과를 얻을 수 있을 뿐만 아니라 오브젝트 s[0]가 가리키는 것(의미 있는 것을 가리킨다고 가정)이 문자열(아마도 Rabbit,:p)이라는 보증도 없습니다.이러한 타입에 대한 관심 부족은 Java를 만드는 거의 모든 것에 직면할 것이다.따라서 로컬 변수에 대한 기본값을 갖는 것은 기껏해야 선택사항으로 간주할 수 있지만 인스턴스 변수에 대한 기본값을 갖는 것은 필요성에 가깝습니다.

이것을 뒤집고, 다음과 같이 질문합니다.필드가 기본값으로 초기화되는 이유는 무엇입니까?Java 컴파일러가 필드를 디폴트값 대신 직접 초기화하도록 요구한다면 사용하기 전에 메모리를 비워둘 필요가 없기 때문에 더 효율적입니다.따라서 모든 변수를 지역 변수처럼 취급하는 이 현명한 언어 설계입니다.

필드 체크가 로컬 변수 체크보다 어렵기 때문이 아닙니다.하는 방법을 알고 .Java 는 이 를 Java에 대해 해야 하기 입니다.왜냐하면 Java 컴파일러는 다음을 위해 체크해야 하기 때문입니다.final필드[ ]를 클릭합니다.따라서 컴파일러의 다른 필드에 대해 확실히 할당되는 다른 필드를 적용할 수 있는 다른 필드에 적용할 수 있습니다.따라서 컴파일러가 같은 논리를 다른 필드에 적용하여 확실하게 컨스트럭터에 할당하는 것은 거의 추가 작업이 아닙니다.

The reason is that, even for 그 이유는 심지어final컴파일러가 올바르게 할당되었는지 증명하기 전에 해당 필드를 다른 코드에서 볼 수 있다는 것을 증명하기 전에 값을 확인할 수 있습니다.컴파일러가 필드가 컨스트럭터에서 확실하게 할당되었음을 증명하는 필드, 할당 전 값은 다른 코드에서 볼 수 있습니다.

class A {
    final int x;
    A() {
        this.x = calculate();
    }
    int calculate() {
        System.out.println(this.x);
        return 1;
    }
}

이 코드는 드 에 스 서 실 트 음 령 당할 in this다or,니명합 code다를itely to the assigns construct확 defin어컨게하는터럭코이this.x인 ,, 이 value 음 음 、 음 음 음 음 음 음 음 음 음 음 음 , , , 。0에 표시됩니다.calculate method this.x인쇄되어 있습니다.되지 않은 은 0으로 됩니다.calculate방법은 초기화되지 않은 메모리의 내용을 관찰할 수 있으며, 이는 비논리적인 동작이며 잠재적인 보안 문제가 있을 수 있습니다.

다른 은 '부르다'라는 방식을 입니다.calculate()이 시점에서 아직 필드가 확실하게 할당되지 않은 코드입니다.할 수 합니다.이 작업을 수행할 수 있는 편리성은 컨스트럭터를 호출하기 전에 필드의 메모리를 제로화하는 작은 성능 비용보다 더 가치가 있습니다.

방법의 초기화되지 않은 로컬 변수는 로컬이기 때문에 다른 방법에서 볼 수 없기 때문에 이 추론은 로컬 변수에는 적용되지 않습니다.

Eclipse는 초기화되지 않은 변수에 대한 경고까지 제공하므로 어쨌든 매우 명확합니다.개인적으로는 이것이 디폴트 동작인 것은 좋은 일이라고 생각합니다.그렇지 않으면 어플리케이션이 예기치 않은 값을 사용할 수 있습니다.또한 컴파일러가 에러를 발생시키는 대신 (아마도 경고를 주는 대신) 아무것도 하지 않고 왜 어떤 것이 정상적으로 동작하지 않는지 머리를 긁적거릴 것입니다.

인스턴스 변수는 기본값을 가지지만 로컬 변수는 기본값을 가질 수 없습니다.로컬 변수는 기본적으로 메서드/동작에 있기 때문에 주요 목적은 일부 연산 또는 계산을 수행하는 것입니다.따라서 로컬 변수에 기본값을 설정하는 것은 좋지 않습니다.그렇지 않으면 예상치 못한 답변의 이유를 확인하는 것이 매우 어렵고 시간이 많이 소요됩니다.

로컬 변수는 스택에 저장되지만 인스턴스 변수는 힙에 저장되므로 힙에서 발생하는 기본값 대신 스택의 이전 값을 읽을 수 있습니다.

이러한 이유로 JVM은 로컬 변수를 초기화하지 않고 사용할 수 없습니다.

메서드의 메모리 스택은 실행 시 생성됩니다.메서드 스택 순서는 실행 시 결정됩니다.

전혀 호출되지 않는 함수가 있을 수 있습니다.따라서 객체 인스턴스화 시 로컬 변수를 인스턴스화하면 메모리가 완전히 낭비됩니다.또한 오브젝트 변수는 클래스의 완전한 오브젝트 라이프 사이클 동안 메모리에 남아 있는 반면 로컬 변수와 그 값은 메모리 스택에서 팝업되는 순간 가비지 수집의 대상이 됩니다.

따라서 호출되지 않거나 호출되어도 메모리 내에 남아 있지 않은 메서드의 변수에 메모리를 할당하는 것은 완전히 비논리적이고 메모리 낭비가 될 수 있습니다.

정답은 인스턴스 변수를 클래스 생성자 또는 모든 클래스 메서드에서 초기화할 수 있다는 것입니다.단, 로컬 변수의 경우 메서드에 정의된 것은 클래스에 영원히 남아 있습니다.

나는 다음의 두 가지 이유를 생각할 수 있었다.

  1. 대부분의 답변이 말한 것처럼 로컬 변수를 초기화해야 한다는 제약을 가함으로써 로컬 변수가 프로그래머가 원하는 대로 값을 할당받고 기대한 결과가 계산되는 것을 보증한다.
  2. 인스턴스 변수는 로컬 변수(같은 이름)를 선언함으로써 숨길 수 있습니다. 예상 동작을 보장하기 위해 로컬 변수를 강제로 값으로 초기화합니다(단, 이는 완전히 피합니다).

언급URL : https://stackoverflow.com/questions/415687/why-are-local-variables-not-initialized-in-java

반응형