面对Java问题的定位-表现得不那么自信,有时我在想是我把问题想的太难,还是问题本身就难,还是我没有专心去看代码...,因为总总,有时还没有看到真正的问题,就阵亡啦,想来死得好冤呀。
本文属于《软件缺陷模式与测试》的读书摘要,感谢作者们辛苦写书,受益良多,书中对Java故障模式进行了总结,分6大类,对每个故障形成原因、表现形式进行分析,并给出了解决方案,值得细细阅读,去体会示例代码,相信读后再看到程序报错,会多那么点底气。
- 空指针
日志:NullPointerException
表现在:
- 可能为null的引用变量
增加是否为null的判断 - 不完全的null条件判断
判断逻辑不严谨,前面虽判断了是否为null,但是其他地方又用到该对象--修改判断逻辑 - 函数返回值可能为null
list.getNext().dosomething() --list.getNext()为null
使用返回值前 先做判断 - 函数返回值是数组类型时返回null
return new string[0];
改成
return null;
- 所调用函数的参数约束为not null
if ( getClass() != obj.getClass() )
#如果obj为null,则抛出异常
- 重载equals 方法时没有处理好参数null的情况
总结:获取到的对象有可能为null,在使用前最好先做判断,不为null,才做下一步。
- 数组越界
基础知识:数组的下标index从(0--数组长度-1)
即是:数组长度为2,数组两个值array_name[0]、array_name[1]
任何index小于0,大于数组长度-1的-都会抛出数组越界
日志:ArrayIndexOutOfBoundsException
1)显示指定数组长度,数组使用越界
public void arrayStudy(){
int max_len = 4;
int[] array = new int[max_len];
for (int i=0; i<= max_len; i++){
array[i] = i;
}
}
#当array[4]时报
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
at pers.qingqian.study.eight.ErrorStudy.arrayStudy(ErrorStudy.java:32)
at pers.qingqian.study.eight.ErrorStudy.main(ErrorStudy.java:38)
- 隐式指定数组长度,数组使用越界
public void arrayStudy(){
int max_len = 4;
// int[] array = new int[max_len];
int[] array = new int[]{1,2,3,4};
for (int i=0; i<= max_len; i++){
array[i] = i;
}
}
3)使用数组,length方法不当,数组使用越界
4)多维数组越界
总结:index只要在0-length-1内,不管是一维还是二维都不会有数组越界
- 资源泄漏
基础知识:指的是封装了类似文件句柄、Socket、数据库连接、图形界面对象等这样的操作系统底层资源的对象被使用后,没有被显性的释放-及时将其回收。
- 函数内部资源泄漏
public void printLines(String fName,Line lines){
try{
File file = new File(fName);
PrintWriter pstr = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
... 省略N行代码
}catch (IOException e){
e.printStackTrace();
}
}
修改成
public void printLines(String fName,Line lines){
PrintWriter pstr = null;
try{
File file = new File(fName);
pstr = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
... 省略N行代码
}catch (IOException e){
e.printStackTrace();
}finally{
pstr.close();
}
}
2)异常路径导致的资源泄漏
public void f() throws FileNotFoundException, IOException {
FileOutputStream e = null;
try {
e = new FileOutputStream("C:\test.java");
e.write(20);
e.close();
}
finally {}// Defect
}
当程序运行到e.write(20)抛出异常后,e.close()将不会被执行。
3)私有域资源不能释放导致泄漏
4)函数间调用产生的资源泄漏
简单的讲:A方法中中new了一个资源,没有写释放,直接将资源传递给B方法,B方法也没有是否该资源的地方
5)包装类构造方法出错可能造成资源泄漏
总结:不管出的现象怎么变,都是一个原因,创建了可和底层操作相关的对象时 就必须有释放该对象的方法,否则就会产生资源泄漏。
附:JDK库中资源分配和释放需要注意的问题
- 在JDK库中有一些修饰类-不是资源,但它们的构造方法中可能包装了一个资源。调用这些类的释放函数,也释放了内部包装的资源。
2)不同的资源使用不同的释放函数close、dispose、disconnect
3)不用资源具有不同的分配方式
4)释放函数为close
FileOutputStream
FilterOutputStream
...
#这类最多
5)释放函数为dispose
StreamPrintService
CompositeContext
Graphics
InputContext
InputMethod
PaintContext
Window
6)释放函数为disconnect
HttpURLConnection
7)其他分配方式
getConnection
createStatement
executeQuery
getResultSet
prepareStatement
prepareCall
accept
8)可能被包装的非资源类
ByteArrayOutputStream
9)可能被包装的资源类
FileOutputStream
- 非法计算
- 除法或取余运算的第二个操作数可能为0
2)常用的数学函数参数超出其定义范围
如:asin(x) --- -1 <=x <=1
- 死循环
- 循环语句中的死循环结构
- while语句中的死循环结构
- do-while语句中的死循环结构
- 函数递归调用造成的死循环
当真有死循环时非常容易发现的,只要访问到死循环代码,你就会发现CPU超级高,去看下线程就能找到是哪里死循环啦。
- 并发
线程并发问题导致的缺陷模式
- 不正确的同步
- 死锁
- 多线程应用中方法调用时机或方式不正确
- 同一变量的双重验证
public static Singleton getInstance(){
if ( instance == null ){
synchronized (Singleton.class) {
if ( instance == null )
instance = new Singleton();
}
}
return instance;
}
线程1已运行到 instance = new Singleton(); ,被线程2抢占CPU,线程2运行 因instance不为null,返回对象。
由于instance已经被分配了内存空间,但没有初始化数据,因此利用线程2返回的instance做操作时会出现失败或异常。
- 相互初始化的类