技术开发 频道

使用IBM静态工具优化Java代码(二):分析错误报告

  这是报出的 ERROR7 错误模式。什么是数组访问越界呢?如果一个数组(在 Java 中,Vector,ArrayList 和 List 也算是数组类型)定义为有 n 个元素,那么对这 n 个元素(0~n-1)的访问都是合法的,如果对这 n 个元素之外的访问,就是非法的,称为“越界”。这种错误同样不会造成编译错误,会危险地“埋伏”在你的程序中。在 C/C++ 中遇到数组访问越界,可导致程序崩溃,甚至宕机;在 Java 中,会抛出 runtime 异常 java.lang.ArrayIndexOutOfBoundsException 或 java.lang.IndexOutOfBoundsException,并终止程序运行。请看程序员容易犯的几个典型数组访问越界的例子:

  清单 7. 越界访问 String 数组元素 1

1 int index = 2;
2 String[] names = new String[] { "developer", "Works" };
3
4 System.out.println( names[index] );

  index 为 2,而数组只有两个元素,最后一个元素的下标索引是 1,所以导致数组访问越界。注意,如果 index 为负数,仍然是数组访问越界。

  清单 8. 越界访问 Vector

1 Vector<String> vec = new Vector<String>();
2
3 for ( int i = 0; i <= vec.size(); i ++ ) {
4 System.out.println( vec.get(i) );
5 }
6

  Vector 和 ArrayList 的起始索引是 0,所以用其数组大小作为索引会导致数组访问越界,其数组最后一个元素的索引应该是“数组大小 -1 ”。

  清单 9. 越界访问 String 数组元素 2

1 int a = 0;
2 String[] names = null;
3 StringBuffer buf = new StringBuffer();
4     
5 if ( a > 0 ) {
6      names = new String[] { "developer", "Works" };
7 } else {
8      names = new String[] { "developerWorks" };
9 }
10         
11 buf.append( names[0] ).append( names[1] );

  程序员调用 append 时以为数组 names 中有两个元素,其实只有一个。

  清单 10. 越界访问 ArrayList

1 ArrayList<String> arrList = new ArrayList<String>();
2 int len = 5;
3     
4 for( int i = 0; i < len; i++ ) {
5 arrList.add( String.valueOf(i) );
6 }
7     
8 arrList.remove( len - 1 );
9 System.out.println(arrList.get( len - 1 ));
10

  ArrayList 中最后一个元素已经被 remove 了,所以该位置已经没有任何东西,访问它将导致 java.lang.ArrayIndexOutOfBoundsException。

  总结:导致数组访问越界主要有以下几个原因:

  使用某个变量作为数组索引时,没有之前对该变量值进行检查,变量的取值可能会超出合法的数组索引范围,从而导致数组访问越界,如清单 7 。

  使用与数组元素个数相同的值作为数组索引,因为数组的最后一个元素的索引是“数组大小 -1 ”,所以导致数组访问越界,如清单 8 。

  数组初始化代码中某个不起眼的 if 之类的条件不成立或者 for/while循环的条件不成立,导致接下来的赋值动作并没有进行,从而接下来访问了未初始化完全的数组,导致数组访问越界,如清单 9 。

  程序员编码时忘记 Vector,ArrayList 或 List 中某些位置的元素已经被 remove 了,后来仍然对该位置元素进行访问,可能会导致数组访问越界,如清单 10 。

  建议的解决方法:在判断数组是否有效不为空的同时,也要对访问的数组元素的索引是否超出了上下限进行检查,如果索引是个变量,一定要确保变量取值在数组范围之类(反例是清单 7);如果索引不是个变量,在确保索引正确的同时还要确保之前定义的数组足够大(反例是清单 9)。最好是使用 try/catch 访问数组,并对数组访问越界异常进行捕获,进行特殊处理,如清单 11 。

  清单 11 利用 try/catch 安全访问数组

1 try {
2 // 访问数组
3 }
4 catch( IndexOutOfBoundsException e ) {
5 // 捕获数组访问越界的异常并做特殊处理
6 }
7

  除 0 错误

  这是报出的 ERROR22 错误模式。在 Java 中,如果除数为 0,会导致 runtime 异常 java.lang.ArithmeticException 并终止程序运行,如清单 12 所示。

  清单 12 除数为 0

1 int num = 0;
2     …
3 int a = 5 / num;
4

  总结:导致除 0 错误的主要原因是使用变量作为除数,并且程序员在写除法语句时,以为变量值到此已经被改变(不是 0),但是实际上可能某条不被注意的语句路径导致除数为 0,从而造成了错误。

  建议的解决方法:做除法前,一定不能将除数直接写为 0 ;如果除数为变量,而且该变量值在进行除法前经过了很多运算,导致不能确定在被除前是否为 0,则在除法前,先对除数变量进行是否为 0 的判断,并对除数为 0 的情况做特殊处理。

0
相关文章