Walt You - 行是知之始

《Effective Java》学习日志(八)57:最小化本地变量范围

2018-12-19

接下来进入新的一章,专注于语言细节,它讨论了局部变量,控制结构,库,数据类型和两个语言设施:反射和本地方法。 最后,它讨论了优化和命名约定。


学习资料主要参考: 《Effective Java Third Edition》,作者:Joshua Bloch



此项目与第15项“最小化类和成员的可访问性”类似。通过最小化局部变量的范围,可以提高代码的可读性和可维护性,并降低出错的可能性。

较旧的编程语言(如C)要求必须在块的头部声明局部变量,并且一些程序员继续习惯这样做。这是一个值得打破的习惯。作为一个温和的提示,Java允许您在声明合法的任何地方声明变量(从C到C99)。

用于最小化局部变量范围的最强大的技术点就是将其声明为首次使用的位置。如果变量在使用之前被声明,那就更加混乱 - 另一件事就是分散那些试图弄清楚程序运行情况的读者的注意力。到使用变量时,读者可能不记得变量的类型或初始值。

过早地声明局部变量可能导致其范围不仅过早开始而且结束太晚。局部变量的范围从声明它的位置延伸到封闭块的末尾。如果变量在使用它的块之外声明,则在程序退出该块后它仍然可见。如果在其预定用途区域之前或之后意外使用变量,则后果可能是灾难性的。

几乎每个局部变量声明都应该包含一个初始化器。如果您还没有足够的信息来合理地初始化变量,那么您应该推迟声明直到您这样做。此规则的一个例外是try-catch语句。如果将变量初始化为其求值可以抛出已检查异常的表达式,则必须在try块内初始化该变量(除非封闭方法可以传播异常)。如果该值必须在try块之外使用,则必须在try块之前声明它,它不能“合理地初始化”。例如,请参见第283页。

循环提供了一个特殊的机会来最小化变量的范围。 for循环以其传统形式和for-each形式允许您声明循环变量,将其范围限制在需要它们的确切区域。 (这个区域由循环体和for关键字和body之间括号中的代码组成。)因此,要偏向使用for循环而不是while循环,这样子循环变量的内容就可以在循环终止后不用了。

例如,这是迭代集合的首选习语(第58项):


如果你需要访问迭代器,也许是为了调用它的remove方法,首选的习惯用法代替for-each循环使用传统的for循环:


要了解为什么这些for循环优于while循环,请考虑以下代码片段,其中包含两个while循环和一个bug:


第二个循环包含一个复制和粘贴错误:它初始化一个新的循环变量i2,但是使用旧的变量i,不幸的是,它仍在范围内。 生成的代码编译时没有错误,并且在不抛出异常的情况下运行,但它做错了。 第二个循环不是迭代在c2上,而是立即终止,给出了c2为空的错误印象。 由于程序无声地错误,因此错误可能会长时间未被检测到。

如果与for循环(for-each或traditional)中的任何一个一起进行类似的复制和粘贴错误,则生成的代码甚至不会编译。 第一个循环中的元素(或迭代器)变量不在第二个循环的范围内。 以下是传统for循环的外观:


此外,如果使用for循环,则不太可能产生复制和粘贴错误,因为没有动机在两个循环中使用不同的变量名称。 循环是完全独立的,因此重用元素(或迭代器)变量名称没有坏处。 事实上,这样做通常很时尚。

for循环比while循环还有一个优点:它更短,增强了可读性。

这是另一个循环习惯用法,它最小化了局部变量的范围:


关于这个习语的重要注意事项是它有两个循环变量,i和n,两者都有完全正确的范围。 第二个变量n用于存储第一个变量的极限,从而避免了每次迭代中冗余计算的成本。 通常,如果循环测试涉及方法调用,并且保证在每次迭代时返回相同的结果,则应使用此惯用法。

最小化局部变量范围的最终技术是保持方法小而集中。 如果在同一方法中组合两个活动,则与一个活动相关的局部变量可能在执行另一个活动的代码范围内。 为了防止这种情况发生,只需将方法分为两个:每个活动一个。


Content