在Java中,变量应该在函数顶部声明还是在需要时声明?

50
我正在为某人清理Java代码,他们以在顶部声明所有变量并将其初始化为null / 0 /任何其他值开头开始函数,而不是在需要时声明它们。
这方面的具体指导方针是什么?单独使用哪种方式是否有优化原因或只是良好的实践?是否存在可以偏离正确操作方式的情况?
12个回答

95

尽可能在第一次使用变量的地方声明变量。这并不是为了效率,而是使您的代码更可读。变量声明越接近其使用位置,阅读代码时需要滚动/搜索的内容就越少。将变量声明更接近它们首次使用的位置还会自然缩小它们的作用范围


提示:如果您使用Visual Studio,可以在任何地方突出显示变量并按F12键,将会带您到其定义处(Shift + F12将查找所有引用)。 - n00b

42

正确的方法是在第一次使用变量时声明它们,并将变量的作用域尽可能缩小,以使代码更易于理解。

在函数顶部声明变量是从C语言中继承下来的(在那里是必需的),并且没有任何优点(变量作用域仅存在于源代码中,在字节码中所有局部变量都按顺序存在于堆栈上)。永远不要这样做。

有些人可能会试图通过声称它更“整洁”来为这种做法辩护,但需要在方法内部“组织”代码通常表明该方法太长了。


20

摘自Java代码规范第6章关于声明的部分:

6.3 声明的位置

只在块的开头进行声明。(块是由大括号“{”和“}”包围的任何代码。)不要等到变量首次使用再声明它们;这可能会让不谨慎的程序员感到困惑,并且阻碍代码在作用域内的可移植性。

void myMethod() {
    int int1 = 0;         // beginning of method block

    if (condition) {
        int int2 = 0;     // beginning of "if" block
        ...
    }
}

唯一一个例外的规则是在Java中,for循环的索引可以在for语句中声明:

for (int i = 0; i < maxLoops; i++) { ... }

避免本地声明掩盖在更高级别上的声明。例如,不要在内部块中声明相同的变量名:

int count;
...
myMethod() {
    if (condition) {
        int count = 0;     // AVOID!
        ...
    }
    ...
}

30
你太可耻了,太阳,你太可耻了! - Michael Borgwardt
2
以上仅供参考。你们两个都没有对错之分,但就个人而言,我最喜欢Bill和S.Lott的回答。 - Tim
5
我怀疑这篇文章非常古老,“confuse the unwary programmer”指的是许多初接触Java的C语言程序员。现在这种情况非常少见,但这篇文章从未经过修订。 - Michael Borgwardt
2
我完全同意这个答案。我既不是来自Java世界,也不是来自C世界,我只是更喜欢它。像往常一样,你们在争论一些只是偏好问题的东西... - Sebas

13
如果在函数体内部有大量变量用于不同的隔离位置,那么你的函数就太大了。
如果你的函数大小适中,"全部提前声明"和"按需申明"之间没有区别。
唯一不是提前声明的变量将位于 for 语句的主体中。
for( Iterator i= someObject.iterator(); i.hasNext(); ) 

我正要写类似的东西。唯一需要补充的是,如果我只声明一个仅适用于方法内特定块(例如,在else语句块内)的变量,有时我会偏离在方法开头声明的规则。 - Adamski
1
@Adamski:在else块中与在if块中的变量非常罕见。if和else结构必须具有并行副作用,而仅限于一侧的局部变量是真正的稀有情况。 - S.Lott

8

来自Google Java风格指南:

4.8.2.2 在需要时声明

局部变量不习惯在其所在的块或类似块的开头声明。相反,局部变量在第一次使用它们的地方附近(合理范围内)声明,以最小化它们的作用域。局部变量声明通常具有初始化程序,或者在声明后立即进行初始化。

好吧,我会遵循Google的做法。表面上看,将所有变量在方法/函数的顶部声明可能会更“整洁”,但显然根据需要声明变量会更有益。这是主观的,取决于您感觉哪种方式更直观。


5
原则:将局部变量声明尽可能靠近其首次使用的位置,而不是仅仅放在方法的顶部。考虑以下示例:
/** Return true iff s is a blah or a blub. */
public boolean checkB(String s) {
    // Return true if s is a blah
    ... code to return true if s is a blah ... 

    // Return true if s is a blub. */
    int helpblub= s.length() + 1;
    ... rest of code to return true is s is a blah.

    return false;
}

在这里,局部变量helpblub被放置在必要的位置,用于测试s是否为blub的代码中。它是实现“如果s是blub,则返回true”的代码的一部分。

把声明helpblub作为方法的第一条语句是毫无逻辑意义的。可怜的读者会想,为什么要有那个变量?它是干什么用的?


5

我发现将变量在需要时声明比一开始就声明能减少错误。我还发现,在尽可能小的范围内声明它们也可以防止错误。

几年前,我查看了声明位置生成的字节码,发现它们大体上是相同的。根据赋值时间不同,有时会有差异。甚至像这样的:

for(Object o : list) {
   Object temp = ...;  //was not "redeclared" every loop iteration
}

vs

Object temp;
for(Object o : list) {
   temp = ...; //nearly identical bytecoode, if not exactly identical.
}

基本上是一样的


当变量在需要时声明时,重构代码(将代码片段提取为方法)也更容易。 - Rober2D2

5
我目前正在做同样的事情。我正在重新编写的代码中,所有变量都在函数顶部声明。在查看过程中,我发现有几个变量被声明但从未使用,或者它们被声明并且执行了操作(例如解析字符串,然后使用日期/时间值设置日历对象),但结果的日历对象从未被使用。
我正在逐步清理这些代码,通过将声明从顶部移动到更接近使用它的位置来进行优化。

1
Jeremy - 我不知道你使用的是哪种构建工具,不过 Eclipse 有一个选项可以在本地变量从未被使用时发出警告 - 只需简单地点击两下即可将其删除 :) - MetroidFan2002
1
我看到的大多数IDE工具都无法捕捉概念上未使用的代码,例如"MyObject o = new MyObject(); o.SetFlag(true);",然后再也没有被使用过。当然,从技术上讲它被使用了,但可能没有任何实际用途。 - James Schek
@MetroidFan2002 - 我没有使用构建工具。我在一个使用jsp页面的环境中,当执行时生成vxml并将其返回到vxml浏览器。这个环境中的编辑器没有编译器,因此即使是简单的语法错误也要等到部署到测试服务器后才能发现。我通过在BlueJ和Notepad++中编写一些代码来解决这个问题,但我已经接受了这是一个糟糕的开发环境的事实。幸运的是,这个环境的下一个版本将使用Eclipse。 - Jeremy Cron
@James Schek,这只是一个更新的评论。至少对于Eclipse来说,James的评论已经不再正确了。 - WendyG

5

将变量定义在比需要更宽泛的范围内会显著影响代码的可理解性。限定变量的作用域可以表明该变量仅对该小代码块有意义,阅读代码时您不必考虑更多。由于大脑只有微小的短期工作记忆(平均只能追踪7件事情),因此这是一个相当重要的问题。少一件需要追踪的事情就有很大的意义。

同样地,您真的应该尽量避免变量在文字上的意义。尝试一次性分配所有内容,并将其声明为 final,以向读者说明这一点。不必再考虑某些东西是否发生改变,真正减轻了认知负担。


4
我认为,事实上可以明显地证明,在编写代码时将变量声明放在顶部的风格更容易出错。如果你通过随机移动行(模拟错误合并或某人不经思考地剪切+粘贴)来测试两种风格的代码,则在功能上错误的情况下,将变量声明放在顶部的风格有更大的编译机会。我认为将变量声明放在顶部的风格没有任何相应的优势,这只是个人偏好。因此,假设你想编写可靠的代码,请学习喜欢即时声明的方式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接