前言
您在編寫Android程序時(shí),有沒(méi)有為經(jīng)常遇到NullPointerException(空指針異常,簡(jiǎn)稱NPE)而頭疼,代碼調(diào)試過(guò)程中,應(yīng)用運(yùn)行,跑著跑著就崩潰了,看下調(diào)用棧,絕大多數(shù)都是因?yàn)榭罩羔樢鸬模词箲?yīng)用上線后,你收到自己應(yīng)用異常崩潰數(shù)據(jù),也會(huì)發(fā)現(xiàn)絕大多數(shù)都是NPE。此時(shí)您可能會(huì)責(zé)怪自己在寫代碼時(shí)不細(xì)心,沒(méi)有在可能產(chǎn)生異常的地方判斷一下。但我想說(shuō)的是,這并不能怪您,這是Java語(yǔ)言的設(shè)計(jì)問(wèn)題,連Java之父(James Gosling)都承認(rèn)這是他的一項(xiàng)巨大失誤。所以接下來(lái)我們就探討下為什么NPE會(huì)經(jīng)常出現(xiàn),為什么它一直沒(méi)有解決,我們?cè)诰幊虝r(shí)應(yīng)該注意什么。
為什么產(chǎn)生NPE
null是什么,就像每種原始類型都有默認(rèn)值一樣,如int是0,boolean是false。null是java中任何引用類型的默認(rèn)值。它和public、static、final一樣是一個(gè)關(guān)鍵字,表示缺失的東西。Java中的引用,有點(diǎn)像是C語(yǔ)言中的指針,因?yàn)樗彩侵赶蛄艘粋€(gè)對(duì)象,所以Java一旦看到為null的引用被使用,就會(huì)報(bào)一個(gè)運(yùn)行時(shí)錯(cuò)誤--NullPointerException。
為什么未解決
Java的引用不完全像C語(yǔ)言中指針的概念,C語(yǔ)言指針可以指向任何內(nèi)存塊,甚至可以越界訪問(wèn)內(nèi)存,而Java中的引用,你只能指向特定對(duì)象,當(dāng)然也不能在它的范圍之外訪問(wèn),實(shí)現(xiàn)此是需要以少量?jī)?nèi)存開(kāi)銷和運(yùn)行時(shí)的下標(biāo)檢查為代價(jià)的,但由此換來(lái)的安全和效率的提高是很值得的。Java中的引用存在于棧區(qū),所分配的對(duì)象在堆區(qū),Java的內(nèi)存回收機(jī)制會(huì)根據(jù)算法清理堆區(qū)無(wú)用的對(duì)象,比如這個(gè)對(duì)象沒(méi)有對(duì)應(yīng)的引用指向它,它就可能會(huì)被回收。如果一個(gè)對(duì)象被回收后,您再使用這個(gè)對(duì)象,就會(huì)產(chǎn)生NPE。比如Android中很臭名昭著的Activity被回收,而在Fragment中獲得上下文就會(huì)出現(xiàn)NPE的問(wèn)題。
我們要怎么做
我們可以在可能會(huì)產(chǎn)生NPE的地方做下判斷,比如上文提到的Activity被回收問(wèn)題,我們可以在fragment中要使用上下文的地方加一個(gè)isAdded判斷一下Activity是否存在再來(lái)使用。當(dāng)然您也可以期待Android可以盡快支持Java 8,因?yàn)镴ava 8中引入了一個(gè)Optional機(jī)制可以用來(lái)解決NPE。詳情可以查看:《Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional
!》。但不幸的是,根據(jù)上次IO大會(huì)時(shí)Android開(kāi)發(fā)團(tuán)隊(duì)的回答,Android要支持Java 8,近期是無(wú)法實(shí)現(xiàn)的了(最新消息,Android N的preview版本發(fā)布了,并且這一版本中支持了Java 8)。當(dāng)然您也可以嘗試使用kotlin語(yǔ)言,kotlin語(yǔ)言可以有效的防止NPE問(wèn)題。這里有關(guān)于kotlin介紹的文章還不錯(cuò):《Kotlin, the Swift of Android》
最后推薦下:《Java編程思想》Bruce Eckel著,大師的作品每一句話都耐人尋味。