代码审查是消灭Bug最重要的方法之一,如果审查者能够有意识地寻找特定的错误, 而不是靠漫无目的的浏览代码来发现错误, 那么代码审查的效果会事半功倍。 在这篇文章中, 我列出了11个Java编程中常见的错误。
一、常见错误1# :多次拷贝字符串
测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。 不可变对象是不 可改变的,因此不需要拷贝它。最
常用的不可变对象是String。 如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常 工作: String s = new String ("Text here"); 但这段代码性能差, 而且没有必要这么复杂。 你还可以用以下的方式来重写上面的代码: String temp = "Text here"; String s = new String (temp); 但是这段代码包含额外的String, 并非完全必要。 更好的代码为: String s = "Text here";
二、常见错误2#: 没有克隆(clone)返回的对象
封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提 供了方便——Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: import java.awt.Dimension; /***Example class.The x and y values should never*be negative.*/ public class Example{ private Dimension d = new Dimension (0, 0); public Example (){ } /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ public synchronized void setValues (int height,int width) throws IllegalArgumentException{ if (height < 0 || width < 0) throw new IllegalArgumentException(); d.height = height; d.width = width; } public synchronized Dimension getValues(){ // Ooops! Breaks encapsulation return d; } } Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法 来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可 以编写如下的破坏性代码:
Example ex = new Example(); Dimension d = ex.getValues(); d.height = -5; d.width = -10; 现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的 Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时 候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。更好的方式是让 getValues()返回拷贝: public synchronized Dimension getValues(){ return new Dimension (d.x, d.y); } 现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的 状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。
三、常见错误3#:不必要的克隆
我们现在知道了get方法应该返回内部数据对象的拷贝,而非引用。但事情没有绝对: /*** Example class.The value should never * be negative.*/ public class Example{ private Integer i = new Integer (0); public Example (){ } /*** Set x. x must be nonnegative* or an exception will be thrown*/ public synchronized void setValues (int x) throws IllegalArgumentException{ if (x < 0) throw new IllegalArgumentException(); i = new Integer (x); } public synchronized Integer getValue(){ // We can’t clone Integers so we makea copy this way. return new Integer (i.intValue()); } } 这段代码是安全的,但是就象在错误1#那样,又作了多余的
工作。Integer对象,就象 String对象那样, 一旦被创建就是不可变的。 因此, 返回内部Integer对象, 而不是它的拷贝, 也是安全的。 方法getValue()应该被写为: public synchronized Integer getValue(){ // ’ i’ is immutable, so it is safe to retu