面对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做操作时会出现失败或异常。

相互初始化的类