programing

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

nasanasas 2020. 8. 27. 07:50
반응형

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


Java 설계자가 지역 변수에 기본값을 부여해서는 안된다고 생각한 이유가 있었습니까? 진지하게, 인스턴스 변수에 기본값이 주어질 수 있다면 왜 우리는 지역 변수에 대해 똑같이 할 수 없습니까?

또한 블로그 게시물에 대한이 댓글 에서 설명한대로 문제가 발생합니다 .

이 규칙은 finally 블록에서 리소스를 닫으려고 할 때 가장 실망 스럽습니다. try 내에서 리소스를 인스턴스화하지만 finally 내에서 닫으려고하면이 오류가 발생합니다. 인스턴스화를 시도 외부로 이동하면 시도 내에 있어야한다는 또 다른 오류가 발생합니다.

매우 실망 스럽습니다.


지역 변수는 대부분 일부 계산을 수행하기 위해 선언됩니다. 따라서 변수의 값을 설정하는 프로그래머의 결정이며 기본값을 사용해서는 안됩니다. 프로그래머가 실수로 로컬 변수를 초기화하지 않았고 기본값을 사용하면 출력이 예상치 못한 값이 될 수 있습니다. 따라서 지역 변수의 경우 컴파일러는 정의되지 않은 값의 사용을 피하기 위해 프로그래머가 변수에 액세스하기 전에 일부 값으로 초기화하도록 요청합니다.


링크 한 "문제" 가이 상황을 설명하는 것 같습니다.

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에 대한 호출은 NullPointerException을 발생시킵니다.

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
  }
}

Notice that the final instance/member variables don't get initialized by default. Because those are final and can't be changed in the program afterwards. That's the reason that Java doesn't give any default value for them and force the programmer to initialize it.

On the other hand, non-final member variables can be changed later. Hence the compiler doesn't let them remain uninitialised, precisely, because those can be changed later. Regarding local variables, the scope of local variables is much narrower. Compiler knows when its getting used. Hence, forcing programmer to initialize the variable makes sense.


The actual answer to your question is because method variables are instantiated by simply adding a number to the stack pointer. To zero them would be an extra step. For class variables they are put into initialized memory on the heap.

Why not take the extra step? Take a step back--Nobody mentioned that the "warning" in this case is a Very Good Thing.

You should never initialize your variable to zero or null on the first pass (when you are first coding it). Either assign it to the actual value or don't assign it at all because if you don't then java can tell you when you really screw up. Take Electric Monk's answer as a great example. In the first case, it's actually amazingly useful that it's telling you that if the try() fails because SomeObject's constructor threw an exception, then you would end up with an NPE in the finally. If the constructor can't throw an exception, it shouldn't be in the try.

This warning is an awesome multi-path bad programmer checker that has saved me from doing stupid stuff since it checks every path and makes sure that if you used the variable in some path then you had to initialize it in every path that lead up to it. I now never explicitly initialize variables until I determine that it is the correct thing to do.

On top of that, isn't it better to explicitly say "int size=0" rather than "int size" and make the next programmer go figure out that you intend it to be zero?

On the flip side I can't come up with a single valid reason to have the compiler initialize all uninitialized variables to 0.


I think the primary purpose was to maintain similarity with C/C++. However the compiler detects and warns you about using uninitialized variables which will reduce the problem to a minimal point. From a performance perspective, it's a little faster to let you declare uninitialized variables since the compiler will not have to write an assignment statement, even if you overwrite the value of the variable in the next statement.


It is more efficient not to initialize variables, and in the case of local variables it is safe to do so, because initialization can be tracked by the compiler.

In cases where you need a variable to be initialized you can always do it yourself, so it is not a problem.


(It may seem strange to post a new answer so long after the question, but a duplicate came up.)

For me, the reason comes down to this this: The purpose of local variables is different than the purpose of instance variables. Local variables are there to be used as part of a calculation; instance variables are there to contain state. If you use a local variable without assigning it a value, that's almost certainly a logic error.

That said, I could totally get behind requiring that instance variables were always explicitly initialized; the error would occur on any constructor where the result allows an uninitialized instance variable (e.g., not initialized at declaration and not in the constructor). But that's not the decision Gosling, et. al., took in the early 90's, so here we are. (And I'm not saying they made the wrong call.)

I could not get behind defaulting local variables, though. Yes, we shouldn't rely on compilers to double-check our logic, and one doesn't, but it's still handy when the compiler catches one out. :-)


The idea behind local variables is they only exist inside the limited scope for which they are needed. As such, there should be little reason for uncertainty as to the value, or at least, where that value is coming from. I could imagine many errors arising from having a default value for local variables.

For example, consider the following simple code... (N.B. let us assume for demonstration purposes that local variables are assigned a default value, as specified, if not explicitly initialized)

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);

When all is said and done, assuming the compiler assigned a default value of '\0' to letterGrade, this code as written would work properly. However, what if we forgot the else statement?

A test run of our code might result in the following

Enter grade
43
Your grade is

This outcome, while to be expected, surely was not the coder's intent. Indeed, probably in a vast majority of cases (or at least, a significant number, thereof), the default value wouldn't be the desired value, so in the vast majority of cases the default value would result in error. It makes more sense to force the coder to assign an initial value to a local variable before using it, since the debugging grief caused by forgetting the = 1 in for(int i = 1; i < 10; i++) far outweighs the convenience in not having to include the = 0 in for(int i; i < 10; i++).

It is true that try-catch-finally blocks could get a little messy (but it isn't actually a catch-22 as the quote seems to suggest), when for example an object throws a checked exception in its constructor, yet for one reason or another, something must be done to this object at the end of the block in finally. A perfect example of this is when dealing with resources, which must be closed.

One way to handle this in the past might be like so...

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();
}

However, as of Java 7, this finally block is no longer necessary using try-with-resources, like so.

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

That said, (as the name suggests) this only works with resources.

And while the former example is a bit yucky, this perhaps speaks more to the way try-catch-finally or these classes are implemented than it speaks about local variables and how they are implemented.

It is true that fields are initialized to a default value, but this is a bit different. When you say, for example, int[] arr = new int[10];, as soon as you've initialized this array, the object exists in memory at a given location. Let's assume for a moment that there is no default values, but instead the initial value is whatever series of 1s and 0s happens to be in that memory location at the moment. This could lead to non-deterministic behavior in a number of cases.

Suppose we have...

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

It would be perfectly possible that Same. might be displayed in one run and Not same. might be displayed in another. The problem could become even more grievous once you start talking reference variables.

String[] s = new String[5];

According to definition, each element of s should point to a String (or is null). However, if the initial value is whatever series of 0s and 1s happens to occur at this memory location, not only is there no guarantee you'll get the same results each time, but there's also no guarantee that the object s[0] points to (assuming it points to anything meaningful) even is a String (perhaps it's a Rabbit, :p)! This lack of concern for type would fly in the face of pretty much everything that makes Java Java. So while having default values for local variables could be seen as optional at best, having default values for instance variables is closer to a necessity.


Eclipse even gives you warnings of uninitialized variables, so it becomes quite obvious anyway. Personally I think it's a good thing that this is the default behaviour, otherwise your application may use unexpected values, and instead of the compiler throwing an error it won't do anything (but perhaps give a warning) and then you'll be scratching your head as to why certain things don't quite behave the way they should.


The local variables are stored on a stack, but instance variables are stored on the heap, so there are some chances that a previous value on the stack will be read instead of a default value as happens in the heap. For that reason the jvm doesn't allow to use a local variable without initialize it.


Instance variable will have default values but the local variables could not have default values. Since local variables basically are in methods/behavior, its main aim is to do some operations or calculations. Therefore, it is not a good idea to set default values for local variables. Otherwise, it is very hard and time-consuming to check the reasons of unexpected answers.


The answer is instance variables can be initialized in class constructor or any class method, But in case of local variables, once you defined whatever in the method that remains forever in the class.


I could think of following 2 reasons

  1. As most of the answers said by putting the constraint of initialising the local variable, it is ensured that the local variable gets assigned a value as programmer wants and ensures expected results are computed.
  2. Instance variables can be hidden by declaring local variables (same name) - to ensure expected behaviour, local variables are forced to be initailised a value. (Would totally avoid this though)

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

반응형