回 帖 发 新 帖 刷新版面

主题:程序员必知:Java代码常见的十种错误

每一个顺序员在编写代码的进程中都免不了呈现错误或是小的失误, 这些小的错误和失误往往使得顺序员还得返工。 那么, 如何才干尽量防止这些错误的发生呢?笔者总结只要在日常的编写代码中总结出经历, 在这篇文章中, 笔者列出了10个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 border="1" Height and width. Both border="1" Height and width must be nonnegative * or an exception is thrown. */   public synchronized void setValues (int border="1" Height, int width) throws IllegalArgumentException. . . {   if (border="1" Height <0 || width <0)   throw new IllegalArgumentException();   d. border="1" height = border="1" Height;   d. width = width;   }   public synchronized Dimension getValues(). . . {   // Ooops! Breaks encapsulation   return d;   }  }   Example类保证了它所存储的border="1" Height和width值永远非负数, 试图使用setValues()方法来设置负值会触发异常。 不幸的是, 由于getValues()返回d的引用, 而不是d的拷贝, 你可以编写如下的破坏性代码:     Example ex = new Example();   Dimension d = ex. getValues();   d. border="1" height = -5;  d. width = -10;   如今, Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和border="1" 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 return it instead of a copy.   return i;  }   Java顺序比C++顺序包含更多的不可变对象。 JDK 所提供的若干不可变类包括:     ·Boolean   ·Byte   ·Character   ·Class   ·Double   ·Float   ·Integer   ·Long   ·Short   ·String  ·大局部的Exception的子类  四、罕见错误4:自编代码来拷贝数组   Java允许你克隆数组, 但是开发者通常会错误地编写如下的代码, 效果在于如下的循环用三行做的事情, 如果采用Object的clone方法用一行就可以完成:     public class Example. . . {   private int[] copy;   /** *//*** Save a copy of "data". "data" cannot be null. */   public void saveCopy (int[] data). . . {   copy = new int[data. length];   for (int i = 0; i   copy[i] = data[i];   }  }   这段代码是正确的, 但却不必要地复杂。 saveCopy()的一个更好的完成是:     void saveCopy (int[] data). . . {   try. . . {   copy = (int[])data. clone();   }catch (CloneNotSupportedException e). . . {   // Can"t get here.   }  }   如果你经常克隆数组, 编写如下的一个工具方法会是个好主意:     static int[] cloneArray (int[] data). . . {   try. . . {   return(int[])data. clone();   }catch(CloneNotSupportedException e). . . {   // Can"t get here.   }  }   这样的话, 我们的saveCopy看起来就更简约了:     void saveCopy (int[] data). . . {   copy = cloneArray ( data);  }  五、罕见错误5:拷贝错误的数据   有时候顺序员晓得必需返回一个拷贝, 但是却不小心拷贝了错误的数据。 由于仅仅做了局部的数据拷贝工作, www.dota8.cn dota omg ai 上面的代码与顺序员的意图有偏差:     import java. awt. Dimension;   /** *//*** Example class. The border="1" Height and width values should never * be   negative. */   public class Example. . . {   static final public int TOTAL_VALUES = 10;   private Dimension[] d = new Dimension[TOTAL_VALUES];   public Example (). . . { }   /** *//*** Set border="1" Height and width. Both border="1" Height and width must be nonnegative * or an exception will be thrown. */   public synchronized void setValues (int index, int border="1" Height, int width) throws IllegalArgumentException. . . {   if (border="1" Height <0 || width <0)   throw new IllegalArgumentException();   if (d[index] == null)   d[index] = new Dimension();   d[index]. border="1" height = border="1" Height;   d[index]. width = width;   }   public synchronized Dimension[] getValues()   throws CloneNotSupportedException. . . {   return (Dimension[])d. clone();   }  }   这儿的效果在于getValues()方法仅仅克隆了数组, 而没有克隆数组中包含的Dimension对象, 因此, 虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象, 但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。 方法getValues()的更好版本为:  public synchronized Dimension[] getValues() throws CloneNotSupportedException. . . {   Dimension[] copy = (Dimension[])d. clone();   for (int i = 0; i   // NOTE: Dimension isn"t cloneable.   if (d != null)   copy[i] = new Dimension (d[i]. border="1" Height, d[i]. width);   }   return copy;  }   在克隆原子类型数据的多维数组的时候, 也会犯类似的错误。 原子类型包括int, float等。 简单的克隆int型的一维数组是正确的, 如下所示:  public void store (int[] data) throws CloneNotSupportedException. . . {   this. data = (int[])data. clone();   // OK  }   拷贝int型的二维数组更复杂些。 Java没有int型的二维数组, 因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。 简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误, 因此应该防止这么做。 上面的例子演示了在克隆int型二维数组时错误的和正确的做法:  public void wrongStore (int[][] data) throws CloneNotSupportedException. . . {   this. data = (int[][])data. clone(); // Not OK!   }   public void rightStore (int[][] data). . . {   // OK!   this. data = (int[][])data. clone();   for (int i = 0; i   if (data != null)   this. data[i] = (int[])data[i]. clone();   }  } &nbsp; TAG: Java JAVA java

回复列表 (共5个回复)

沙发

我来学习了。。

板凳

了解一下

3 楼

还真是那么回事呢

4 楼

跟大家分享一下,学习SCJP-SE5/SE6 Java程序设计语言, 能够掌握基本Java语言语法,能够应用面向对象技术(如继承多态)建立复杂的Java应用程序; 创建独立的Java应用程序;并且能通过相关认证考试。
http://sh.easthome.com/public/zt/c_detail.aspx?thematicid=22&courseid=174

5 楼

路过,支持一下楼主

我来回复

您尚未登录,请登录后再回复。点此登录或注册