第八章 通用程序设计
本章讨论Java语言的具体细节,讨论局部变量的处理,控制结构,类库的用法,各种数据类型的用法,以及两种不是语言本身提供的机制(反射机制和本地方法),最后讨论优化和命名惯例.
第45条 将局部变量的作用域最小化
本条目和第13条(使类和成员的可访问性最小化)本质上是类似的,将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并且降低出错的可能性.
C语言要求局部变量必须在一个代码块的开头出进行声明,出于习惯,程序员依旧沿着这个习惯,这个习惯应该改正.在此提醒,java允许你在任何可以出现语句的地方声明变量.
No1.要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明.如果变量在使用之前声明,对于阅读者来说只会分散注意力.等到用到该变量的时候,阅读者已经记不得该变量类型及其初始值.
过早声明局部变量不仅会使它的作用域过早地拓展,而且结束地过晚.局部变量的作用域从它被声明的点开始拓展,一直到外围块的结束处.
如果变量是在”使用它的块”之外被声明,当程序退出该块之后,该变量仍是可见的.如果变量在它的目标使用区域之前或者之后被意外使用的话,后果将可能是灾难性的(降低出错的可能性).
No2.几乎每个局部变量的声明都应该包含一个初始化表达式.如果没有足够信息来对一个变量进行有意义的初始化,就应该推迟这个声明,直到可以初始化为止.例外情况是与try..catch有关.
如果一个变量被一个方法初始化,而这个方法可能抛出一个受检查的异常(checkedexception),该变量就必须在try块的内部被初始化.
如果变量的值必须在try块的外部被使用到,它就必须在try块之前被声明,但是在try块之前,它还不能被有”意义地初始化”.
publicstatic void main(String[] args) {
Class<?>cl = null;
try{
cl= Class.forName(args[0]);
}catch (ClassNotFoundException e){
System.err.println("Classnot find");
System.exit(1);
}
}
案例中的Class<?> cl在try之前声明,在try内部有意义地初始化.
循环提供特殊机会将变量作用域最小化.(无论传统for,还foreach).for循环,都允许声明循环变量,它们的作用域被限定在正好的作用范围之内.(范围包括循环体,以及循环体之前的初始化,测试,更新).
如果循环终止之后不再需要循环变量内容,for循环优于while循环.
遍历集合首选做法:
for (Element e : c) {
doSomething(e);
}
在java1.5之前,首选做法如下(现在仍然可用):
for (Interator i = c.iterator(); i.hasNext();) {
doSomething((Element)i.next());
}
考虑for比while循环更好,请看大屏幕(下面案例)
Interator i1 = c.iterator();
while (i1.hasNext()) {
doSomething((Element)i1.next());
}
Interator i2 = c2.iterator();
while (i1.hasNext()) { //bug,i2 not i1
doSomething((Element)i2.next());
}
第1个循环包含复制-粘贴错误:它本来要初始化新的循环变量i2,却使用旧的i1,遗憾的是,i1仍然在有效范围内.
结果代码可以正常编译,运行时候不会抛异常.但是它所做的事情确实错误的.
第2个循环并没有在c2上迭代,而是立即终止,造成c2为空的假想.因为错误是悄悄发生的.
如果类似的复制-粘贴发生在前面的for循环中,编译无法通过.在第2个循环中,第1个迭代器已经不在作用范围内.
for (Interator i1 = c.iterator(); i1.hasNext();) {
doSomething((Element)i1.next());
}
for (Interator i2 = c2.iterator(); i2.hasNext();) {
doSomething((Element)i2.next());
}
通常没有必要在两个循环内中使用不同的变量名,循环是完全独立的,所以重用元素变量名称不会有危害.实际上,这也是流行的做法.
for循环比while循环另外优势:更简短,增加可读性.
案例:
for (int i=0,n=expensieComputation(); i<n; i++){
doSomething(i);
}
关注点在于两个循环变量,两者具有相同的作用域.第2个变量n用来保存第1个变量的极限值,从而避免在每次迭代中执行冗余计算的开销.
通常,如果循环测试中涉及方法调用,它可以保证在每次迭代中都会返回相同的结果,就应该使用这种方法.
No3.将局部变量作用域最小化的方法是使方法小而集中.如果把两个操作合并到同一方法中,与其中一个操作相关的局部变量可能会出现执行另一个操作的代码范围内.为了防止这种情况发生,只要把这个方法分成两个,每个方法各执行一个操作.