为什么在 Java 中没有初始化局部变量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/415687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why are local variables not initialized in Java?
提问by Shivasubramanian A
Was there any reason why the designers of Java felt that local variables should not be given a default value? Seriously, if instance variables can be given a default value, then why can't we do the same for local variables?
Java 的设计者有什么理由认为局部变量不应该被赋予默认值?说真的,如果可以给实例变量一个默认值,那为什么我们不能对局部变量做同样的事情呢?
And it also leads to problems as explained in this comment to a blog post:
并且它还会导致在博客文章的评论中解释的问题:
Well this rule is most frustrating when trying to close a resource in a finally block. If I instantiate the resource inside a try, but try to close it within the finally, I get this error. If I move the instantiation outside the try, I get another error stating that a it must be within a try.
Very frustrating.
好吧,当试图关闭 finally 块中的资源时,这条规则最令人沮丧。如果我在 try 中实例化资源,但尝试在 finally 中关闭它,则会出现此错误。如果我将实例化移到 try 之外,我会收到另一个错误,指出它必须在 try 内。
非常令人沮丧。
回答by Mehrdad Afshari
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.
我认为主要目的是保持与 C/C++ 的相似性。但是,编译器会检测并警告您使用未初始化的变量,这会将问题减少到最小程度。从性能的角度来看,让您声明未初始化的变量要快一些,因为编译器不必编写赋值语句,即使您在下一个语句中覆盖了变量的值。
回答by Warrior
Local variables are declared mostly to do some calculation. So its the programmer's decision to set the value of the variable and it should not take a default value. If the programmer, by mistake, did not initialize a local variable and it take default value, then the output could be some unexpected value. So in case of local variables, the compiler will ask the programmer to initialize with some value before they access the variable to avoid the usage of undefined values.
声明局部变量主要是为了做一些计算。所以它的程序员决定设置变量的值,它不应该采用默认值。如果程序员错误地没有初始化局部变量并且它采用默认值,那么输出可能是一些意想不到的值。因此,对于局部变量,编译器会要求程序员在访问变量之前使用某个值进行初始化,以避免使用未定义的值。
回答by Adeel Ansari
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.
请注意,默认情况下不会初始化最终实例/成员变量。因为这些是最终的,之后不能在程序中更改。这就是 Java 没有为它们提供任何默认值并强制程序员对其进行初始化的原因。
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.
另一方面,非最终成员变量可以稍后更改。因此,编译器不会让它们保持未初始化状态,准确地说,因为它们可以在以后更改。关于局部变量,局部变量的范围要窄得多。编译器知道它何时被使用。因此,强制程序员初始化变量是有道理的。
回答by Rob Kennedy
The "problem" you link toseems to be describing this situation:
您链接到的“问题”似乎描述了这种情况:
SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}
The commenter's complaint is that the compiler balks at the line in the finally
section, claiming that so
might be uninitialized. The comment then mentions another way of writing the code, probably something like this:
评论者的抱怨是编译器在该finally
部分中的行中犹豫不决,声称该行so
可能未初始化。然后评论提到了另一种编写代码的方式,可能是这样的:
// Do some work here ...
SomeObject so = new SomeObject();
try {
so.DoUsefulThings();
} finally {
so.CleanUp();
}
The commenter is unhappy with that solution because the compiler then says that the code "must be within a try." I guess that means some of the code may raise an exception that isn't handled anymore. I'm not sure. Neither version of my code handles any exceptions, so anything exception-related in the first version should work the same in the second.
评论者对该解决方案不满意,因为编译器随后说该代码“必须在尝试中”。我想这意味着某些代码可能会引发不再处理的异常。我不知道。我的代码的两个版本都没有处理任何异常,因此第一个版本中与异常相关的任何内容在第二个版本中都应该相同。
Anyway, this second version of code is the correctway to write it. In the first version, the compiler's error message was correct. The so
variable might be uninitialized. In particular, if the SomeObject
constructor fails, so
will not be initialized, and so it will be an error to attempt to call so.CleanUp
. Always enter the try
section afteryou have acquired the resource that the finally
section finalizes.
无论如何,这第二个版本的代码是编写它的正确方法。在第一个版本中,编译器的错误信息是正确的。该so
变量可能会被初始化。特别是,如果SomeObject
构造函数失败,so
将不会被初始化,因此尝试调用so.CleanUp
. 在您获得该部分最终确定的资源后,请始终进入该try
部分。finally
The try
-finally
block after the so
initialization is there onlyto protect the SomeObject
instance, to make sure it gets cleaned up no matter what else happens. If there are otherthings that need to run, but they aren't related to whether the SomeObject
instance was property allocated, then they should go in anothertry
-finally
block, probably one that wraps the one I've shown.
本try
-finally
后块so
初始化有只保护SomeObject
实例,以确保它得到清理无论什么事情发生。如果还有其他事情需要运行,但它们与SomeObject
实例是否已分配属性无关,那么它们应该进入另一个try
-finally
块,可能是包含我所展示的块的块。
Requiring variables to be assigned manually before use does not lead to real problems. It only leads to minor hassles, but your code will be better for it. You'll have variables with more limited scope, and try
-finally
blocks that don't try to protect too much.
要求在使用前手动分配变量不会导致实际问题。它只会带来一些小麻烦,但你的代码会更好。您将拥有范围更有限的变量,并且try
-finally
不会尝试保护太多的块。
If local variables had default values, then so
in the first example would have been null
. That wouldn't really have solved anything. Instead of getting a compile-time error in the finally
block, you'd have a NullPointerException
lurking there that might hidewhatever other exception could occur in the "Do some work here" section of the code. (Or do exceptions in finally
sections automatically chain to the previous exception? I don't remember. Even so, you'd have an extra exception in the way of the real one.)
如果局部变量有默认值,那么so
在第一个例子中应该是null
. 那真的不能解决任何问题。不是在finally
块中获得编译时错误,而是在那里NullPointerException
潜伏着一个可能隐藏代码的“在这里做一些工作”部分中可能发生的任何其他异常。(或者finally
部分中的异常会自动链接到前一个异常吗?我不记得了。即便如此,您还是会有一个真正的异常。)
回答by Kezzer
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.
Eclipse 甚至会警告您未初始化的变量,因此无论如何它变得非常明显。我个人认为这是默认行为是一件好事,否则您的应用程序可能会使用意外的值,而不是编译器抛出错误,它不会做任何事情(但可能会发出警告)然后你会抓挠您对为什么某些事情不完全按照应有的方式行事的想法。
回答by starblue
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.
在需要初始化变量的情况下,您总是可以自己完成,所以这不是问题。
回答by Electric Monk
Moreover, in the example below, an exception may have been thrown inside the SomeObject construction, in which case the 'so' variable would be null and the call to CleanUp will throw a NullPointerException
此外,在下面的示例中,可能在 SomeObject 构造中引发了异常,在这种情况下,“so”变量将为 null,并且对 CleanUp 的调用将引发 NullPointerException
SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}
What I tend to do is this:
我倾向于做的是:
SomeObject so = null;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
if (so != null) {
so.CleanUp(); // safe
}
}
回答by David Santamaria
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.
局部变量存储在堆栈中,但实例变量存储在堆中,因此有可能读取堆栈上的先前值而不是堆中发生的默认值。因此,jvm 不允许在未初始化的情况下使用局部变量。
回答by T.J. Crowder
(It may seem strange to post a new answer so long after the question, but a duplicatecame up.)
(在提出问题后这么长时间发布新答案似乎很奇怪,但出现了重复的答案。)
For me, the reasoncomes 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.)
也就是说,我完全可以不要求实例变量总是显式初始化;如果结果允许未初始化的实例变量(例如,未在声明时初始化且未在构造函数中初始化),则该错误将发生在任何构造函数上。但这不是 Gosling 等人的决定。al.,在 90 年代初,所以我们在这里。(我并不是说他们打错了电话。)
I could notget 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. :-)
我能不落后违约局部变量,虽然。是的,我们不应该依赖编译器来仔细检查我们的逻辑,一个不会,但是当编译器发现一个时它仍然很方便。:-)
回答by Bill K
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.
永远不要在第一遍(当您第一次编码时)将变量初始化为零或空值。要么将其分配给实际值,要么根本不分配它,因为如果您不这样做,那么 java 会告诉您何时真正搞砸了。以 Electric Monk 的回答为例。在第一种情况下,它实际上非常有用,它告诉您如果 try() 因 SomeObject 的构造函数抛出异常而失败,那么您将在 finally 中得到 NPE。如果构造函数不能抛出异常,则不应在 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?
最重要的是,明确地说“int size=0”而不是“int size”并让下一个程序员弄清楚你打算它为零不是更好吗?
On the flip side I can't come up with a single valid reason to have the compiler initialize all uninitialized variables to 0.
另一方面,我想不出一个有效的理由让编译器将所有未初始化的变量初始化为 0。