Java 语言编程通用原则清单 —— 编写高质量的Java代码

本文结合《Effective Java》第八章《通用程序设计》和自己的理解及实践,讲解了编写Java代码的一些优秀实践,文章发布于专栏Effective Java,欢迎读者订阅。

在这之前,专栏已经发布了两篇通用原则清单 :

  • Java 设计类和接口的八条优秀实践清单 
  • Java 设计方法的五条优秀实践清单 

分别介绍了在Java语言中,设计类和接口,以及设计方法的一些通用原则,而在了解完如何设计类接口和方法之后,就要去写它们的具体实现细节,这也就是本文将重点介绍的内容。


清单1: 将局部变量的作用域最小化

这条清单,其实是封装原则的一个衍生,我们都知道,封装是使得一个类的属性不被其他类所访问和篡改,那么在一个代码块里面,我们同样需要让代码块里的各个子代码块的局部变量,不互相影响。

要做到这条原则,有两个小原则:

  • 在第一次使用局部变量的地方再去声明它;
  • 使方法小而集中,如果把两个操作合并到一个方法,那么其中一个操作的局部变量就会有可能被另一个操作误用。

以下面这两个while循环为例,本意是遍历两个List,但是由于后面的一个while循环,是直接复制上面那个循环然后粘贴的,使用的是上面循环的迭代器,但是由于上面循环的iterator仍在有效范围,因此编译还通过了:

public class TestCycle {
	public static void main(String[] args) {
		testCycle();
	}

	private static void testCycle() {
		String[] sArr = {"1","2","3"};
		List strings = Arrays.asList(sArr);
		List strings2 = Arrays.asList(sArr);
		
		Iterator iterator = strings.iterator();
		while (iterator.hasNext()) {
			String string = (String) iterator.next();
			System.out.println(string);
		}
		
		Iterator iterator2 = strings2.iterator();
		//copy from the while code above, make wrong!
		while (iterator.hasNext()) {
			String string = (String) iterator.next();
			System.out.println(string);
		}
	}
}


这个例子说明了不控制好局部变量作用域的代价,要修复这个隐藏的缺陷,我们可以使用for循环或者foreach循环:

for(Iterator iterator = strings.iterator(); iterator.hasNext();)
		{
			String string = (String) iterator.next();
			System.out.println(string);
		}


使用for循环可以很好的将局部变量的作用域限制在特定的代码块中,当然,我们更推荐使用foreach,为什么?请看下一条清单。


清单2: for-each循环优先于传统for循环

从上一条清单的例子,我们知道,for循环比while好,但是它也并非完美,for循环暴露了迭代器和索引变量,会造成一些混乱,比如下面这个双层for循环,在第二层for循环中调用了第一层for循环的迭代器的next方法,破坏了整个循环的顺序:

for (Iterator i = suits.iterator(); i.hasNext(); )
            for (Iterator j = ranks.iterator(); j.hasNext(); )
                deck.add(new Card(i.next(), j.next()));


代码原意应该是这样:

for (Iterator i = suits.iterator(); i.hasNext(); )
		{
			Suit suit = i.next();
            for (Iterator j = ranks.iterator(); j.hasNext(); )
                deck.add(new Card(suit, j.next()));
		}


这个例子说明了暴露迭代器的后果,使用for-each可以完美的隐藏迭代器和索引变量:

 for (Suit suit : suits)
    	  for (Rank rank : ranks)
    		  deck.add(new Card(suit, rank));


for-each循环不仅仅可以遍历集合和数组,凡是实现了iterable接口的对象,都可以使用for-each去遍历,因此,如果你正在编写的类型表示的是一组元素,那么如果你给它实现了iterable接口,允许用户使用for-each去遍历,会令用户感激不尽的!

当然,由于隐藏了迭代器和索引,因此在一些需要使用到迭代器或者索引的场合,比如遍历并删除(参考  Java集合遍历删除),那么for-each是不适用的,但是大多数情况下,for-each可以满足我们的需要并且实现十分简洁和高质量的代码。


清单3: 了解和使用类库  不要重复制造轮子

JDK提供了很多有用的类库,比如随机函数Random.nextInt(int),集合框架,并发工具类库等。

在代码实现中,我们应该优先使用这些提供好的类库,而不是自己去实现。

使用jdk类库的好处在于:

  • 这些方法,经过了大量的测试发行和成千上万程序员的使用,可靠性得到保障;
  • 不必浪费时间提供和业务功能无关的解决方案;
  • 这些方法的性能会随着每一次jdk的版本更新而提升;
  • 使自己代码融入主流,更易读,更加容易维护;

从经济学的角度来看,使用已有类库的功能,是一种成本最小的方法。


清单4: 基本类型优先于装箱类型

经常发现有代码这样写:

private Long mills = ....

大概他是忘了Long还有一个基本类型long吧。

装箱类型和基本类型相比,有两个不同点:

  • 在进行 == 比较时,装箱类型比较的是两个对象是不是同一对象,而不是比较值是不是相同;
  • 装箱类型可以为null;

这两点都会给正常的运算带来不必要的麻烦,当然从性能上看,基本类型也比装箱类型快。

所以,除非你是在需要使用装箱类型的场合(比如List),否则基本类型总是优先于装箱类型。


以上,希望对你在实现高质量的Java程序上能有所帮助。




你可能感兴趣的:(Java,SE,Effective,Java)