修改NPE问题的收获

最近为公司的项目修改NullPointerException的问题,有些收获。
null可以被强制转换成任意类型的引用,强制过程不会抛异常
在修改一处NPE问题时,本来看日志已经定位了大概的范围,但是就是找不到具体是哪里发生了问题。引用看起来都不可能为空啊,其中有一个引用就是通过强制转换,转换成另外一个类型的。当时觉得,这里也不能是空指针异常的地方啊,如果被强转引用为空,则强转不能成功啊。
后来发现,事实不是这样的,23333.举个栗子:

1
2
3
4
5
6
7
8
9
10
public class NullTest{

public Object returnNull(){
return null;
}
public static void main(String [] args){
String nullStr = (String)returnNull(); //此行运行时不会抛任何异常
System.out.println("value of nullStr: " + nullStr);//打印出来的值为null
}
}

知道真相的我眼泪掉下来,怎么能这样,这样不是很坑吗。如果平时写程序的时候调用的是别人的方法,别人的方法有可能返回null,如returnNull()方法。调用别人的方法后自己又使用了强制转换,那在强转这步,其实结果就已经不对了,但是程序是不会抛异常的,隐藏的坑啊!!!
以后自己写代码的时候要注意这些隐藏的坑。

slf4j中打log的方法,最后一个参数默认为Throwable类型
其实这个是在解决sonar扫描后,代码中blocker级别问题中发现的。问题是这样的。
很多人在编程过程中,在使用try-catch时,都不会很好的处理异常。最常见的做法如下:

1
2
3
4
5
try{
do something here
}catch(Exception e){
e.printStackTrace();
}

这样的做法很危险的,因为一旦有异常,这个异常信息不会打印到日志中,给定位问题增加了难度。所以,正确的解决方法如下:

1
2
3
4
5
6
7
8
9
10
11
public class HandleTryCatch{
private static final Logger LOGGER = LoggerFactory.getLogger(HandleTryCatch.class);

public void properHandleTryCatch(){
try{
do something here
}catch(Exception e){
LOGGER.error("error message", e);
}
}
}

这样就能够将全部的stacktrace信息打印到log中,对异常现象定位更有帮助。
这种方法有效的原因在于logger的实现:

1
2
3
4
5
6
7
8
/**
* Log an exception (throwable) at the ERROR level with an
* accompanying message.
*
* @param msg the message accompanying the exception
* @param t the exception (throwable) to log
*/

public void error(String msg, Throwable t);

logger专门提供了一个方法,这个方法的第二个参数是Throwable类型,这样所有stacktrace信息都在Throwable这个参数里,所以可以打印完整的stack trace信息。

但是,我们有时候也会这样使用logger:

1
2
3
4
5
6
7
8
9
10
11
12
public class HandleTryCatch{
private static final Logger LOGGER = LoggerFactory.getLogger(HandleTryCatch.class);

public void properHandleTryCatch(){
try{
do something here
String errorMsg = getErrorMsg();
}catch(Exception e){
LOGGER.error("error message{}", errorMsg,e);
}
}
}

而此时logger的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Log a message at the ERROR level according to the specified format
* and arguments.
* <p/>
* <p>This form avoids superfluous object creation when the logger
* is disabled for the ERROR level. </p>
*
* @param format the format string
* @param arg1 the first argument
* @param arg2 the second argument
*/

public void error(String format, Object arg1, Object arg2);

可以看到,arg2是Object类型的,不是Throwable类型的,那这样会不会不能打印出完整的stack trace信息了呢。
这种担心在slf4j的1.6版本以后就不存在了。证据如下:

  1. stack overflow
  2. slf4j faq
    如果使用logger时,最后一个参数是Exception,logger会将其当做Exception处理,而不会将其当做普通的Object类型。

这下方便啦,不用担心了,尽情的给logger的最后一个参数传Exception类型的引用吧!

在修改完所有NPE后,对Java中null这个东西十分感兴趣,私下里又多了解了下,下次分享吧!