Programavimas

Efektyvus „Java NullPointerException“ tvarkymas

Norint sužinoti, kas yra „NullPointerException“, nereikia didelės „Java“ kūrimo patirties. Tiesą sakant, vienas asmuo atkreipė dėmesį į tai, kaip „Java“ kūrėjų klaidą. Anksčiau rašiau tinklaraštį naudodamas String.value (Object), norėdamas sumažinti nepageidaujamus „NullPointerExceptions“. Yra keletas kitų paprastų metodų, kuriuos galima naudoti norint sumažinti arba pašalinti šio įprasto „RuntimeException“ tipo atvejus, kurie buvo su mumis nuo JDK 1.0. Šiame tinklaraščio įraše kaupiami ir apibendrinami vieni populiariausių šių būdų.

Prieš naudodami patikrinkite, ar kiekvienas objektas nėra tuščias

Patikimiausias būdas išvengti „NullPointerException“ yra patikrinti visas objekto nuorodas ir įsitikinti, kad jos nėra niekinės prieš prieigą prie vieno iš objekto laukų ar metodų. Kaip rodo kitas pavyzdys, tai yra labai paprasta technika.

final String causStr = "Stringo pridėjimas prie" Deque ", kuris nustatytas į nulį."; galutinis eilutės elementasStr = "Fudd"; Deque deque = nulis; pabandykite {deque.push (elementStr); žurnalas („Sėkmingas“ + causStr, System.out); } gaudyti (NullPointerException nullPointer) {žurnalas (causStr, nullPointer, System.out); } pabandykite {if (deque == null) {deque = new LinkedList (); } deque.push (elementStr); žurnalas ("Sėkmingas" + causStr + "(pirmiausia patikrinus, ar nėra nulinės vertės, ir Deque įdiegimas)", System.out); } gaudyti (NullPointerException nullPointer) {žurnalas (causStr, nullPointer, System.out); } 

Aukščiau pateiktame kode naudojamas „Deque“ yra specialiai inicijuojamas iki nulio, kad būtų lengviau pavyzdį. Kodas pirmame bandyti prieš bandydamas pasiekti „Deque“ metodą, blokas netikrina, ar nėra nieko. Kodas antrame bandyti blokas tikrina, ar nėra niekinių, ir tiesiogiai įdiegia Deque („LinkedList“), jei jis yra nulinis. Abiejų pavyzdžių rezultatas atrodo taip:

KLAIDA: „NullPointerException“ įvyko bandant pridėti „Deque“ eilutę, kurios vertė yra nulinė. java.lang.NullPointerException INFO: Sėkmingai pridedant eilutę prie „Deque“, kuri nustatyta į nulį. (pirmiausia patikrinant, ar nėra niekinių, ir „Deque“ diegimas) 

Pranešimas po klaida aukščiau esančiame išvestyje rodo, kad a „NullPointerException“ metamas, kai bandoma iškviesti metodą nuliui Deque. Pranešimas po INFO aukščiau esančiame išvestyje rodo, kad patikrinus Deque Iš pradžių išimties buvo išvengta, jei pirmiausia buvo null, o tada sukuriamas naujas jo įgyvendinimas, kai jis yra nulinis.

Šis metodas dažnai naudojamas ir, kaip parodyta aukščiau, gali būti labai naudingas siekiant išvengti nepageidaujamų (netikėtų) „NullPointerException“ atvejų. Tačiau tai nėra be išlaidų. Prieš naudojant kiekvieną objektą, patikrinus, ar nėra nieko, gali būti išpūstas kodas, gali būti varginantis rašyti ir atsiras daugiau vietos problemoms, susijusioms su papildomo kodo kūrimu ir priežiūra. Dėl šios priežasties buvo kalbėta apie „Java“ kalbos palaikymo įdiegimą įmontuotam „null“ aptikimui, automatinį šių patikrinimų pridėjimą, jei nėra pradinio kodavimo, „null-safe“ tipai, „Aspect-Oriented Programming“ (AOP) naudojimas norint pridėti „null“ tikrinimą baito kodą ir kitus niekinio aptikimo įrankius.

„Groovy“ jau suteikia patogų mechanizmą, kaip tvarkyti objektų nuorodas, kurios gali būti niekinės. „Groovy“ saugios navigacijos operatorius (?.) grąžina nulį, o ne mesti a „NullPointerException“ kai pasiekiama nulinė objekto nuoroda.

Kadangi kiekvienos objekto nuorodos tikrinimas gali būti varginantis ir išpūtęs kodą, daugelis kūrėjų nusprendžia protingai pasirinkti, kuriuos objektus tikrinti, ar nėra nieko. Tai paprastai lemia visų potencialiai nežinomos kilmės objektų tikrinimą. Idėja yra ta, kad objektus galima patikrinti atvirose sąsajose, o po pirminio patikrinimo jie gali būti laikomi saugiais.

Tai situacija, kai trijų komponentų operatorius gali būti ypač naudingas. Vietoj

// gavo „BigDecimal“, vadinamą someObject String returnString; if (someObject! = null) {returnString = someObject.toEngineeringString (); } else {returnString = ""; } 

trinaris operatorius palaiko šią glaustesnę sintaksę

// gavote „BigDecimal“, vadinamą „someObject final“ eilutę returnString = (someObject! = null)? someObject.toEngineeringString (): ""; } 

Patikrinkite metodo argumentus „Null“

Ką tik aptartą techniką galima naudoti visiems objektams. Kaip teigiama tos technikos aprašyme, daugelis kūrėjų nusprendžia, ar objektai nėra niekiniai, tik tada, kai jie gaunami iš „nepatikimų“ šaltinių. Tai dažnai reiškia, kad pirmiausia bandoma atlikti niekinius metodus, kuriuos veikia išoriniai skambinantieji. Pvz., Konkrečioje klasėje kūrėjas gali pasirinkti patikrinti, ar visi objektai, kuriems perduota, nėra niekiniai visuomenės metodai, bet netikrinkite, ar nėra niekinio privatus metodai.

Šis kodas rodo, kad metodo įvedimo metu tikrinama, ar nėra nulinės vertės. Tai apima vieną metodą kaip parodomąjį metodą, kuris apsisuka ir iškviečia du metodus, kiekvieną metodą perduodamas po vieną nulinį argumentą. Vienas iš metodų, gaunantis argumentą „null“, pirmiausia patikrina, ar šis argumentas yra nulinis, bet kitas tik daro prielaidą, kad perduotas parametras nėra nulinis.

 / ** * Pridėkite iš anksto apibrėžtą teksto eilutę prie pateikto „StringBuilder“. * * @param builder StringBuilder, prie kurio bus pridėtas tekstas; turėtų * būti ne nulinis. * @throws IllegalArgumentException metamas, jei pateiktas „StringBuilder“ yra * nulinis. * / private void appendPredefinedTextToProvidedBuilderCheckForNull (final StringBuilder builder) {if (builder == null) {thrown new IllegalArgumentException ("Pateiktas StringBuilder buvo nulinis; turi būti pateikta ne nulinė vertė."); } builder.append ("Ačiū, kad tiekėte" StringBuilder ".); } / ** * Pridėkite iš anksto apibrėžtą teksto eilutę prie pateikto „StringBuilder“. * * @param builder StringBuilder, prie kurio bus pridėtas tekstas; turėtų * būti ne nulinis. * / private void appendPredefinedTextToProvidedBuilderNoCheckForNull (final StringBuilder builder) {builder.append ("Ačiū, kad pateikėte" StringBuilder ".); } / ** * Prieš bandydami naudoti * perduotus parametrus, kurie gali būti niekiniai, parodykite, ar parametrai nėra niekiniai. * / public void demonstrCheckingArgumentsForNull () {final String reasonStr = "pateikti metodo reikšmę kaip argumentą."; logHeader ("NULL TIKRINIMO METODO PARAMETRŲ PARODYMAS", System.out); pabandykite {appendPredefinedTextToProvidedBuilderNoCheckForNull (null); } gaudyti (NullPointerException nullPointer) {žurnalas (causStr, nullPointer, System.out); } pabandykite {appendPredefinedTextToProvidedBuilderCheckForNull (null); } pagauti (IllegalArgumentException neteisėtasArgument) {žurnalas (sukeltiStr, neteisėtasArgumentas, System.out); }} 

Kai bus įvykdytas aukščiau pateiktas kodas, išvestis pasirodys taip, kaip parodyta toliau.

KLAIDA: NullPointerException įvyko bandant pateikti metodo reikšmę kaip argumentą. java.lang.NullPointerException KLAIDA: „IllegalArgumentException“ įvyko bandant pateikti metodo reikšmę kaip argumentą. java.lang.IllegalArgumentException: Pateiktas StringBuilder buvo niekinis; turi būti pateikta nenulinė vertė. 

Abiem atvejais buvo užregistruotas klaidos pranešimas. Tačiau atvejis, kai buvo patikrintas nulinis rodiklis, išmetė reklamuojamą „IllegalArgumentException“, į kurį įtraukta papildoma konteksto informacija apie tai, kada įvyko „null“. Arba šis nulinis parametras galėjo būti tvarkomas įvairiais būdais. Tuo atveju, kai nulinis parametras nebuvo tvarkomas, nebuvo galimybių, kaip jį tvarkyti. Daugelis žmonių nori mesti a „NullPolinterException“ su papildoma kontekstine informacija, kai yra aiškiai aptikta nulinė vertė (žr. 60 antrojo leidimo punktą) Veiksminga „Java“ arba Pirmo leidimo Nr. 42), bet aš šiek tiek norėčiau Neteisėtas argumentas. Išimtis kai tai yra metodo argumentas, kuris yra niekinis, nes manau, kad pati išimtis pateikia konteksto detales ir į temą lengva įtraukti „null“.

Metodo argumentų „null“ tikrinimo technika iš tikrųjų yra bendresnės visų objektų „null“ tikrinimo technikos pogrupis. Tačiau, kaip išdėstyta pirmiau, argumentai viešai rodomiems metodams dažnai yra mažiausiai patikimi programoje, todėl jų tikrinimas gali būti svarbesnis nei vidutinio objekto tikrinimas.

Metodo parametrų tikrinimas pagal nulį taip pat yra bendresnio metodo parametrų visuotinio galiojimo tikrinimo pogrupis, aptartas 2003 m. Antrojo leidimo 38 punkte. Veiksminga „Java“ (Pirmojo leidimo 23 punktas).

Apsvarstykite primityvumą, o ne objektus

Nemanau, kad tai yra gera idėja pasirinkti primityvų duomenų tipą (pvz., tarpt) virš atitinkamo objekto nuorodos tipo (pvz., Sveikasis skaičius), kad būtų išvengta a „NullPointerException“, tačiau negalima paneigti, kad vienas iš pirmykščių tipų pranašumų yra tas, kad jie nelemia „NullPointerException“s. Tačiau vis dar reikia patikrinti primityvų galiojimą (mėnuo negali būti neigiamas sveikasis skaičius), todėl ši nauda gali būti maža. Kita vertus, primityvūs elementai negali būti naudojami „Java“ kolekcijose ir kartais norima, kad vertė būtų nulinė.

Svarbiausia būti labai atsargiems dėl primityvų, nuorodų tipų ir autobokso derinio. Yra įspėjimas Veiksminga „Java“ (Antrasis leidimas, 49 punktas) apie pavojus, įskaitant jų mėtymą „NullPointerException“, susijusios su neatsargiu primityvių ir etaloninių tipų maišymu.

Atidžiai apsvarstykite grandinės metodo skambučius

A „NullPointerException“ gali būti labai lengva rasti, nes eilutės numeryje bus nurodyta, kur jis atsirado. Pvz., Kamino pėdsakas gali atrodyti taip, kaip parodyta toliau:

java.lang.NullPointerException at dustin.examples.AvoidingNullPointerExamples.demonstrateNullPointerExceptionStackTrace (AvoidingNullPointerExamples.java:222) at dustin.examples.AvoidingNullPointerExamx.xamx. 

Kamino pėdsakai akivaizdu, kad „NullPointerException“ buvo išmestas dėl kodo, įvykdyto 222 eilutėje VengiamaNullPointerExamples.java. Net ir nurodžius eilutės numerį, vis tiek gali būti sunku susiaurinti, kuris objektas yra niekinis, jei toje pačioje eilutėje yra keli objektai su metodais ar laukais.

Pavyzdžiui, teiginys patinka someObject.getObjectA (). getObjectB (). getObjectC (). toString (); turi keturis galimus skambučius, kurie galėjo sukelti „NullPointerException“ priskiriama tai pačiai kodo eilutei. Naudojant derintuvą tai gali padėti, tačiau gali būti situacijų, kai pageidautina paprasčiausiai suskaidyti aukščiau nurodytą kodą, kad kiekvienas skambutis būtų atliekamas atskiroje eilutėje. Tai leidžia linijos numeriui, esančiam kamino pėdsakuose, lengvai nurodyti, kuris tikslus skambutis buvo problema. Be to, tai palengvina aiškų kiekvieno objekto patikrinimą, ar jis nėra niekinis. Tačiau trūkumas yra tas, kad kodo suskaidymas padidina kodų skaičių (kai kuriems tai yra teigiamas!) Ir gali būti ne visada pageidautinas, ypač jei yra tikras, kad nė vienas iš nagrinėjamų metodų niekada nebus nulinis.

Padarykite „NullPointerExceptions“ informatyvesnes

Pirmiau pateiktoje rekomendacijoje įspėjime turėjo būti atidžiai apsvarstytas skambučių grandinės metodo naudojimas, visų pirma dėl to, kad linijos numeris kamino „NullPointerException“ mažiau naudinga, nei gali būti kitaip. Tačiau eilutės numeris rodomas kamino pėdsakuose tik tada, kai kodas buvo kompiliuotas su įjungta derinimo vėliava. Jei jis buvo kompiliuotas be derinimo, kamino pėdsakas atrodo taip, kaip parodyta toliau:

java.lang.NullPointerException at dustin.examples.AvoidingNullPointerExamples.demonstrateNullPointerExceptionStackTrace (nežinomas šaltinis) at dustin.examples.AvoidingNullPointerExamples.main (nežinomas šaltinis) 

Kaip rodo pirmiau pateiktas išvestis, yra metodo pavadinimas, bet nėra eilutės numerio „NullPointerException“. Tai apsunkina iškart nustatyti, kas kode lėmė išimtį. Vienas iš būdų tai išspręsti yra pateikti konteksto informaciją bet kuriame metime „NullPointerException“. Ši idėja buvo pademonstruota anksčiau, kai a „NullPointerException“ buvo sugautas ir išmestas su papildoma kontekstine informacija kaip a Neteisėtas argumentas. Išimtis. Tačiau net jei išimtis tiesiog permetama kaip kita „NullPointerException“ turint informacijos apie kontekstą, ji vis tiek yra naudinga. Konteksto informacija padeda kodą derinančiam asmeniui greičiau nustatyti tikrąją problemos priežastį.

Šis pavyzdys parodo šį pavyzdį.

galutinis kalendorius nullCalendar = null; pabandykite {final Date date = nullCalendar.getTime (); } sugauti (NullPointerException nullPointer) {log ("NullPointerException su naudingais duomenimis", nullPointer, System.out); } pabandykite {if (nullCalendar == null) {mest naują NullPointerException ("Nepavyko išgauti datos iš pateikto kalendoriaus"); } final Date date = nullCalendar.getTime (); } sugauti (NullPointerException nullPointer) {log ("NullPointerException su naudingais duomenimis", nullPointer, System.out); } 

Išvestis vykdant minėtą kodą atrodo taip.

KLAIDA: NullPointerException įvyko bandant NullPointerException naudingais duomenimis java.lang.NullPointerException KLAIDA: NullPointerException įvyko bandant NullPointerException su naudingais duomenimis java.lang.NullPointerException Pateiktas 
$config[zx-auto] not found$config[zx-overlay] not found