Java中的空指針異常怎么避免?

Java中如何避免空指針異常,這也是由初級(jí)程序員成長(zhǎng)到中級(jí)程序員的時(shí)候經(jīng)常會(huì)遇到的問題。

程序員不知道或不信任正在使用的約定,并且小心的檢查著null。還有當(dāng)程序員寫代碼的時(shí)候,總是會(huì)依賴于通過返回空(NULL)來表明某些意義,因此需要調(diào)用者去檢查Null。

有兩種空指針的檢查場(chǎng)景:

期望的結(jié)果就是null。

期望的結(jié)果不是null。

第二種很簡(jiǎn)單,可以通過用assert或者允許程序報(bào)錯(cuò),例如拋出NullPointerException。Assertions是一個(gè)從Java1.4加進(jìn)來的高度未被利用的特性,語(yǔ)法是:

assert <condition>
或者

assert <condition> : <object>
condition是一個(gè)布爾表達(dá)式,object是一個(gè)對(duì)象(其toString()方法的輸出將會(huì)被包含在錯(cuò)誤里)。

校對(duì)注:我測(cè)試了下,JDK1.4及其以上,運(yùn)行前設(shè)置vm參數(shù)-ea

public static void main(String[] args) {
String name = null;
assert (name != null) : "name為空null";
}
Exception in thread "main";
 java.lang.AssertionError: 變量name為空nullat LogUtil.main(LogUtil.java:37)
如果condition為false的話,assert將會(huì)拋出一個(gè)Error(AssertionError)。默認(rèn)Java會(huì)忽略斷言你可以通過在JVM中傳入一個(gè)-ea參數(shù)來啟用斷言。

你可以為單獨(dú)的一個(gè)包或者類啟動(dòng)關(guān)閉assertions。這意味著你可以在開發(fā)和測(cè)試的時(shí)候通過斷言來驗(yàn)證代碼,在發(fā)布產(chǎn)品的時(shí)候關(guān)閉它,盡管我下面展示的測(cè)試中并沒有因?yàn)閍ssertions而損失性能。在這個(gè)代碼段中不用斷言也可以,因?yàn)樗麜?huì)運(yùn)行失敗的,就像加了斷言一樣。唯一的區(qū)別是有了斷言可能會(huì)發(fā)生的更快一些,更有意義,并且會(huì)附加一些額外的信息,而這可以幫助你弄明白失敗的原因。

第一種有一點(diǎn)棘手。如果你對(duì)不能控制正在調(diào)用的這段代碼,那你就卡住了。如果Null是一個(gè)合理的返回值,你就應(yīng)該檢查它。如果是你能夠控制的代碼,那就是個(gè)完全不同的故事情景了。盡量避免用NULL作為返回值。對(duì)于返回Collections的集合很容易,返回Empty(一個(gè)空集合或者數(shù)組),而不是一直用null作為返回值。對(duì)于不是返回Collections的方法會(huì)有一點(diǎn)復(fù)雜??紤]下面這個(gè)例子:

public interface Action {
  void doSomething();
  }
  public interface Parser {
   Action findAction(String userInput);
  }
Parser采用用戶的輸入作為參數(shù),然后做一些事情(例如模擬一個(gè)命令行)?,F(xiàn)在你可能會(huì)返回null,如果沒找到對(duì)應(yīng)輸入的動(dòng)作的話,這就導(dǎo)致了剛才說過的空指針檢查。

一個(gè)可選的解決方案是永遠(yuǎn)不要返回null,而是返回一個(gè)空對(duì)象

public class MyParser implements Parser {  private static Action DO_NOTHING = new Action() {    public void doSomething() { /* do nothing */ }
  };  public Action findAction(String userInput) {    // ...
    if ( /* we can't find any actions */ ) {   
       return DO_NOTHING;
    }
  }}
比較這段代碼:

Parser parser = ParserFactory.getParser();if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response}Action action = parser.findAction(someInput);if (action == null) {
  // do nothing} else {
  action.doSomething();}
和這段:

ParserFactory.getParser().findAction(someInput).doSomething();
這是個(gè)更好的設(shè)計(jì),因?yàn)樽銐蚝?jiǎn)潔,避免了多余的判斷。

即便如此,或許比較合適的設(shè)計(jì)是:findAction()方法之惡杰拋出一個(gè)異常,其中包含一些有意義的錯(cuò)誤信息—–特別是在這個(gè)案例中你依賴于用戶的輸入。

讓findAction()方法拋出一個(gè)異常而不是簡(jiǎn)單的產(chǎn)生一個(gè)沒有任何解釋的NullPointerException 要好得多。

try {
    ParserFactory.getParser().findAction(someInput).doSomething();} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());}
或者你認(rèn)為try/catch 的機(jī)制太丑了,你的action應(yīng)該跟用戶提供一個(gè)反饋而不是什么都不做:

public Action findAction(final String userInput) {    /* Code to return requested Action if found */
    return new Action() {    
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }}






作者:碼出宇宙

歡迎關(guān)注微信公眾號(hào) :碼出宇宙

掃描添加好友邀你進(jìn)技術(shù)交流群,加我時(shí)注明【姓名+公司(學(xué)校)+職位】