有没有理由为什么Java的设计者觉得局部变量不应该被赋予默认值?说真的,如果实例变量可以给出一个默认值,为什么我们不能对局部变量做同样的事情呢?
它也会导致问题,如本评论中对博客文章的解释:
这个规则在尝试关闭finally块中的资源时最令人沮丧.如果我在try中实例化资源,但尝试在finally中关闭它,我会收到此错误.如果我在try之外移动实例化,我会收到另一个错误,指出它必须在try中.
很沮丧.
Warrior.. 57
声明局部变量主要是为了进行一些计算.因此,程序员决定设置变量的值,它不应该采用默认值.如果程序员错误地没有初始化局部变量并且它采用默认值,那么输出可能是一些意外的值.因此,在局部变量的情况下,编译器将要求程序员在访问变量之前使用某个值进行初始化,以避免使用未定义的值.
声明局部变量主要是为了进行一些计算.因此,程序员决定设置变量的值,它不应该采用默认值.如果程序员错误地没有初始化局部变量并且它采用默认值,那么输出可能是一些意外的值.因此,在局部变量的情况下,编译器将要求程序员在访问变量之前使用某个值进行初始化,以避免使用未定义的值.
您链接的"问题"似乎描述了这种情况:
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 } }
请注意,默认情况下不会初始化最终的实例/成员变量.因为那些是最终的,之后无法在程序中进行更改.这就是Java没有为它们提供任何默认值并迫使程序员初始化它的原因.
另一方面,非最终成员变量可以在以后更改.因此,编译器不会让它们保持未初始化,确切地说,因为这些可以在以后更改.关于局部变量,局部变量的范围要窄得多.编译器知道它何时被使用.因此,强制程序员初始化变量是有道理的.
你的问题的实际答案是因为方法变量只是通过向堆栈指针添加一个数字来实例化.将它们归零将是一个额外的步骤.对于类变量,它们被放入堆上的初始化内存中.
为什么不采取额外的步骤?退后一步 - 没有人提到在这种情况下的"警告"是一件非常好的事情.
您永远不应该在第一次传递时将变量初始化为零或为零(当您第一次编码时).将它分配给实际值或根本不分配它,因为如果你不这样做,那么java可以告诉你什么时候你真的搞砸了.以Electric Monk的答案为例.在第一种情况下,它实际上非常有用,它告诉你如果try()失败因为SomeObject的构造函数抛出异常,那么你最终会得到一个NPE.如果构造函数不能抛出异常,那么它不应该在try中.
这个警告是一个很棒的多路径程序员检查程序,它使我免于做愚蠢的事情,因为它检查每个路径并确保如果你在某个路径中使用该变量,那么你必须在导致它的每个路径中初始化它.我现在从未明确初始化变量,直到我确定它是正确的做法.
最重要的是,明确说"int size = 0"而不是"int size"并不是更好,并让下一个程序员弄清楚你打算将其设为零吗?
另一方面,我无法想出让编译器将所有未初始化的变量初始化为0的单一有效理由.