Jawaban:
Ketika Anda mendeklarasikan variabel referensi (yaitu objek), Anda benar-benar membuat pointer ke objek. Pertimbangkan kode berikut di mana Anda mendeklarasikan variabel tipe primitif int
:
int x;
x = 10;
Dalam contoh ini, variabelnya x
adalah int
dan Java akan menginisialisasi 0
untuk Anda. Ketika Anda menetapkan nilai 10
pada baris kedua, nilai Anda 10
ditulis ke dalam lokasi memori yang dirujuk oleh x
.
Tetapi, ketika Anda mencoba untuk mendeklarasikan tipe referensi , sesuatu yang berbeda terjadi. Ambil kode berikut:
Integer num;
num = new Integer(10);
Baris pertama mendeklarasikan variabel bernama num
, tetapi sebenarnya belum mengandung nilai primitif. Sebaliknya, ini berisi pointer (karena tipenya adalah Integer
tipe referensi). Karena Anda belum mengatakan apa yang harus ditunjukkan , Java mengaturnya null
, yang berarti " Saya tidak menunjuk apa - apa ".
Di baris kedua, new
kata kunci digunakan untuk instantiate (atau membuat) objek bertipe Integer
dan variabel pointer num
ditugaskan ke Integer
objek itu.
Itu NullPointerException
terjadi ketika Anda mendeklarasikan variabel tetapi tidak membuat objek dan menetapkan ke variabel sebelum mencoba menggunakan konten variabel (disebut dereferencing ). Jadi Anda menunjuk sesuatu yang sebenarnya tidak ada.
Dereferencing biasanya terjadi ketika menggunakan .
untuk mengakses metode atau bidang, atau menggunakan [
untuk mengindeks array.
Jika Anda mencoba melakukan dereferensi num
SEBELUM membuat objek yang Anda dapatkan NullPointerException
. Dalam kasus yang paling sepele, kompiler akan menangkap masalah dan memberi tahu Anda bahwa " num may not have been initialized
," tetapi kadang-kadang Anda dapat menulis kode yang tidak secara langsung membuat objek.
Misalnya, Anda mungkin memiliki metode sebagai berikut:
public void doSomething(SomeObject obj) {
//do something to obj
}
Dalam hal ini, Anda tidak membuat objek obj
, tetapi dengan asumsi bahwa itu dibuat sebelum doSomething()
metode dipanggil. Catatan, adalah mungkin untuk memanggil metode seperti ini:
doSomething(null);
Dalam hal ini, obj
adalah null
. Jika metode ini dimaksudkan untuk melakukan sesuatu ke objek yang lewat, itu tepat untuk melempar NullPointerException
karena itu adalah kesalahan pemrogram dan pemrogram akan membutuhkan informasi itu untuk keperluan debugging. Harap sertakan nama variabel objek dalam pesan pengecualian, seperti
Objects.requireNonNull(a, "a");
Atau, mungkin ada kasus-kasus di mana tujuan metode ini tidak semata-mata untuk beroperasi pada objek yang diteruskan, dan oleh karena itu parameter nol dapat diterima. Dalam hal ini, Anda perlu memeriksa parameter nol dan berperilaku berbeda. Anda juga harus menjelaskan ini dalam dokumentasi. Misalnya, doSomething()
dapat ditulis sebagai:
/**
* @param obj An optional foo for ____. May be null, in which case
* the result will be ____.
*/
public void doSomething(SomeObject obj) {
if(obj == null) {
//do something
} else {
//do something else
}
}
Akhirnya, Bagaimana menentukan pengecualian & penyebab menggunakan Stack Trace
Metode / alat apa yang dapat digunakan untuk menentukan penyebabnya sehingga Anda menghentikan pengecualian dari menyebabkan program berakhir sebelum waktunya?
Sonar dengan findbugs dapat mendeteksi NPE. Dapat sonar menangkap pengecualian pointer nol yang disebabkan oleh JVM secara dinamis
int a=b
NPE saat menggunakan autoboxing: dapat melempar NPE jika b adalah Integer
. Ada kasus di mana ini membingungkan untuk debug.
NullPointerException
masalah dalam kode Anda adalah dengan menggunakan @Nullable
dan @NotNull
anotasi. Jawaban berikut memiliki informasi lebih lanjut tentang ini. Meskipun jawaban ini khusus tentang IDE IntelliJ, ini juga berlaku untuk alat lain seperti yang mungkin dari komentar. (BTW saya tidak diizinkan mengedit jawaban ini secara langsung, mungkin penulis dapat menambahkannya?)
NullPointerException
s adalah pengecualian yang terjadi ketika Anda mencoba menggunakan referensi yang menunjuk ke tidak ada lokasi dalam memori (nol) seolah-olah itu merujuk objek. Memanggil metode pada referensi nol atau mencoba mengakses bidang referensi nol akan memicu a NullPointerException
. Ini adalah yang paling umum, tetapi cara lain terdaftar di InternetNullPointerException
halaman javadoc.
Mungkin contoh kode tercepat yang dapat saya berikan untuk menggambarkan NullPointerException
adalah:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
Pada baris pertama di dalam main
, saya secara eksplisit menetapkan Object
referensi obj
sama dengan null
. Ini berarti saya memiliki referensi, tetapi tidak menunjuk ke objek apa pun. Setelah itu, saya mencoba untuk memperlakukan referensi seolah-olah menunjuk ke suatu objek dengan memanggil metode di atasnya. Ini menghasilkan aNullPointerException
karena tidak ada kode untuk dieksekusi di lokasi yang ditunjuk oleh referensi.
(Ini adalah masalah teknis, tapi saya rasa perlu disebutkan: Referensi yang menunjuk ke nol tidak sama dengan pointer C yang menunjuk ke lokasi memori yang tidak valid. Sebuah pointer nol secara harfiah tidak menunjuk ke mana pun , yang secara subtil berbeda dari menunjuk ke lokasi yang tidak valid.)
null
sebelum menggunakannya, seperti ini . Dengan variabel lokal, kompiler akan menangkap kesalahan ini, tetapi dalam kasus ini tidak. Mungkin itu akan menjadi tambahan yang berguna untuk jawaban Anda?
Tempat yang baik untuk memulai adalah JavaDocs . Mereka memiliki ini meliputi:
Dilemparkan ketika aplikasi mencoba menggunakan null dalam kasus di mana objek diperlukan. Ini termasuk:
- Memanggil metode instance dari objek null.
- Mengakses atau memodifikasi bidang objek nol.
- Mengambil panjang nol seolah-olah array.
- Mengakses atau memodifikasi slot nol seolah-olah array.
- Melempar nol seolah-olah itu adalah nilai yang bisa dibuang.
Aplikasi harus membuang instance kelas ini untuk menunjukkan penggunaan ilegal lainnya dari objek nol.
Ini juga merupakan kasus bahwa jika Anda mencoba menggunakan referensi nol synchronized
, itu juga akan membuang pengecualian ini, sesuai JLS :
SynchronizedStatement: synchronized ( Expression ) Block
- Kalau tidak, jika nilai Ekspresi adalah nol, a
NullPointerException
dilemparkan.
Jadi, Anda punya NullPointerException
. Bagaimana Anda memperbaikinya? Mari kita ambil contoh sederhana yang melempar NullPointerException
:
public class Printer {
private String name;
public void setName(String name) {
this.name = name;
}
public void print() {
printString(name);
}
private void printString(String s) {
System.out.println(s + " (" + s.length() + ")");
}
public static void main(String[] args) {
Printer printer = new Printer();
printer.print();
}
}
Identifikasi nilai nol
Langkah pertama adalah mengidentifikasi dengan tepat nilai mana yang menyebabkan pengecualian . Untuk ini, kita perlu melakukan debugging. Sangat penting untuk belajar membaca stacktrace . Ini akan menunjukkan di mana pengecualian dilemparkan:
Exception in thread "main" java.lang.NullPointerException
at Printer.printString(Printer.java:13)
at Printer.print(Printer.java:9)
at Printer.main(Printer.java:19)
Di sini, kita melihat bahwa pengecualian dilemparkan pada baris 13 (dalam printString
metode). Lihatlah baris dan periksa nilai-nilai mana yang nol dengan menambahkan pernyataan logging atau menggunakan debugger . Kami menemukan bahwa s
itu nol, dan memanggil length
metode di atasnya melempar pengecualian. Kita dapat melihat bahwa program berhenti melempar pengecualian ketika s.length()
dihapus dari metode.
Lacak dari mana nilai-nilai ini berasal
Selanjutnya periksa dari mana nilai ini berasal. Dengan mengikuti penelepon metode, kita melihat bahwa s
diteruskan dengan printString(name)
dalam print()
metode ini, dan this.name
nol.
Lacak di mana nilai-nilai ini harus ditetapkan
Di mana this.name
diatur? Dalam setName(String)
metode. Dengan lebih banyak debugging, kita dapat melihat bahwa metode ini tidak dipanggil sama sekali. Jika metode dipanggil, pastikan untuk memeriksa urutan metode ini dipanggil, dan metode yang ditetapkan tidak dipanggil setelah metode cetak.
Ini cukup untuk memberi kami solusi: tambahkan panggilan printer.setName()
sebelum memanggil printer.print()
.
Variabel dapat memiliki nilai default (dan setName
dapat mencegahnya disetel ke nol):
private String name = "";
Entah metode print
atau printString
dapat memeriksa nol , misalnya:
printString((name == null) ? "" : name);
Atau Anda bisa mendesain kelas sehingga name
selalu memiliki nilai bukan nol :
public class Printer {
private final String name;
public Printer(String name) {
this.name = Objects.requireNonNull(name);
}
public void print() {
printString(name);
}
private void printString(String s) {
System.out.println(s + " (" + s.length() + ")");
}
public static void main(String[] args) {
Printer printer = new Printer("123");
printer.print();
}
}
Lihat juga:
Jika Anda mencoba men-debug masalah dan masih belum memiliki solusi, Anda dapat memposting pertanyaan untuk bantuan lebih lanjut, tetapi pastikan untuk memasukkan apa yang telah Anda coba sejauh ini. Minimal, sertakan stacktrace dalam pertanyaan, dan tandai nomor baris penting dalam kode. Juga, cobalah menyederhanakan kode terlebih dahulu (lihat SSCCE ).
NullPointerException
(NPE)?Seperti yang Anda ketahui, jenis Java dibagi menjadi tipe primitif ( boolean
, int
, dll) dan jenis referensi . Jenis referensi di Jawa memungkinkan Anda untuk menggunakan nilai khususnull
yang merupakan cara Java untuk mengatakan "tidak ada objek".
A NullPointerException
dilempar pada saat runtime setiap kali program Anda mencoba menggunakan null
seolah-olah itu adalah referensi nyata. Misalnya, jika Anda menulis ini:
public class Test {
public static void main(String[] args) {
String foo = null;
int length = foo.length(); // HERE
}
}
pernyataan yang berlabel "DI SINI" akan mencoba menjalankan length()
metode pada null
referensi, dan ini akan membuang aNullPointerException
.
Ada banyak cara Anda bisa menggunakan null
nilai yang akan menghasilkan a NullPointerException
. Faktanya, satu-satunya hal yang dapat Anda lakukan dengan null
tanpa menyebabkan NPE adalah:
==
atau !=
operator, atau instanceof
.Misalkan saya mengkompilasi dan menjalankan program di atas:
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:4)
$
Pengamatan pertama: kompilasi berhasil! Masalah dalam program BUKAN kesalahan kompilasi. Ini adalah kesalahan runtime . (Beberapa IDE mungkin memperingatkan program Anda akan selalu mengeluarkan pengecualian ... tetapi standarjavac
kompiler tidak.)
Pengamatan kedua: ketika saya menjalankan program, ini menghasilkan dua baris "gobbledy-gook". SALAH!! Itu bukan gobbledy-gook. Ini adalah stacktrace ... dan memberikan informasi penting yang akan membantu Anda melacak kesalahan dalam kode Anda jika Anda meluangkan waktu untuk membacanya dengan cermat.
Jadi mari kita lihat apa yang dikatakannya:
Exception in thread "main" java.lang.NullPointerException
Baris pertama jejak tumpukan memberi tahu Anda beberapa hal:
java.lang.NullPointerException
.NullPointerException
tidak biasa dalam hal ini, karena jarang memiliki pesan kesalahan.Baris kedua adalah yang paling penting dalam mendiagnosis NPE.
at Test.main(Test.java:4)
Ini memberi tahu kita beberapa hal:
main
metode Test
kelas.Jika Anda menghitung baris dalam file di atas, baris 4 adalah yang saya beri label dengan komentar "DI SINI".
Perhatikan bahwa dalam contoh yang lebih rumit, akan ada banyak baris dalam jejak tumpukan NPE. Tapi Anda bisa yakin bahwa baris kedua (baris "at" pertama) akan memberi tahu Anda di mana NPE dilempar 1 .
Singkatnya, jejak stack akan memberi tahu kita dengan jelas pernyataan program mana yang telah melemparkan NPE.
1 - Tidak sepenuhnya benar. Ada hal-hal yang disebut pengecualian bersarang ...
Ini bagian yang sulit. Jawaban singkatnya adalah untuk menerapkan inferensi logis pada bukti yang diberikan oleh jejak stack, kode sumber, dan dokumentasi API yang relevan.
Mari kita ilustrasikan dengan contoh sederhana (di atas) terlebih dahulu. Kita mulai dengan melihat garis yang diceritakan jejak stack kepada kita adalah di mana NPE terjadi:
int length = foo.length(); // HERE
Bagaimana itu bisa melempar NPE?
Padahal, hanya ada satu cara: itu hanya bisa terjadi jika foo
punya nilai null
. Kami kemudian mencoba menjalankan length()
metode null
dan ... BANG!
Tapi (saya dengar Anda bilang) bagaimana jika NPE dilemparkan ke dalam length()
pemanggilan metode?
Nah, jika itu terjadi, jejak stack akan terlihat berbeda. Baris "at" pertama akan mengatakan bahwa eksepsi dilemparkan pada beberapa baris di java.lang.String
kelas dan baris 4 Test.java
akan menjadi baris "at" kedua.
Jadi dari mana null
asalnya? Dalam hal ini, jelas, dan jelas apa yang perlu kita lakukan untuk memperbaikinya. (Tetapkan nilai bukan nol ke foo
.)
OK, jadi mari kita coba contoh yang sedikit lebih rumit. Ini akan membutuhkan beberapa pengurangan logis .
public class Test {
private static String[] foo = new String[2];
private static int test(String[] bar, int pos) {
return bar[pos].length();
}
public static void main(String[] args) {
int length = test(foo, 1);
}
}
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.test(Test.java:6)
at Test.main(Test.java:10)
$
Jadi sekarang kita memiliki dua garis "at". Yang pertama untuk baris ini:
return args[pos].length();
dan yang kedua untuk baris ini:
int length = test(foo, 1);
Melihat baris pertama, bagaimana itu bisa melempar NPE? Ada dua cara:
bar
ini null
maka bar[pos]
akan melempar NPE.bar[pos]
ini null
kemudian memanggilnya length()
akan melempar NPE.Selanjutnya, kita perlu mencari tahu skenario mana yang menjelaskan apa yang sebenarnya terjadi. Kami akan mulai dengan menjelajahi yang pertama:
Dari mana datangnya bar
? Ini adalah parameter untuk test
pemanggilan metode, dan jika kita melihat bagaimana test
dipanggil, kita dapat melihat bahwa itu berasal dari foo
variabel statis. Selain itu, kita dapat melihat dengan jelas bahwa kita diinisialisasi foo
ke nilai yang bukan nol. Itu cukup untuk sementara menolak penjelasan ini. (Secara teori, sesuatu yang lain bisa berubah foo
menjadinull
... tapi itu tidak terjadi di sini.)
Jadi bagaimana dengan skenario kedua kita? Nah, kita dapat melihat bahwa pos
adalah 1
, sehingga berarti bahwa foo[1]
harus null
. Apakah ini mungkin?
Memang itu! Dan itu masalahnya. Ketika kita menginisialisasi seperti ini:
private static String[] foo = new String[2];
kami mengalokasikan String[]
dengan dua elemen yang diinisialisasi kenull
. Setelah itu, kami belum mengubah konten foo
... jadi foo[1]
akan tetap demikian null
.
Ini seperti Anda mencoba mengakses objek yang ada null
. Pertimbangkan contoh di bawah ini:
TypeA objA;
Pada saat ini Anda baru saja mendeklarasikan objek ini tetapi tidak diinisialisasi atau dipakai . Dan setiap kali Anda mencoba mengakses properti atau metode apa pun di dalamnya, itu akan membuang NullPointerException
yang masuk akal.
Lihat contoh di bawah ini juga:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Pengecualian null pointer dilemparkan ketika aplikasi mencoba menggunakan null dalam kasus di mana objek diperlukan. Ini termasuk:
null
objek.null
objek.null
seolah-olah array.null
seolah-olah array.null
seolah-olah itu adalah nilai yang bisa dibuang.Aplikasi harus membuang instance dari kelas ini untuk menunjukkan penggunaan ilegal lain dari null
benda .
Referensi: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
null
sebagai target synchronized
blok, 2) menggunakan null
sebagai sebagai target dari switch
, dan unboxing null
.
Sebuah null
pointer adalah salah satu yang poin ke mana-mana. Ketika Anda dereference pointer p
, Anda mengatakan "memberi saya data di lokasi yang disimpan di 'p'. Ketika p
adalah null
pointer, lokasi disimpan dalam p
adalah nowhere
, Anda mengatakan 'memberi saya data di lokasi 'tempat''. Jelas, itu tidak bisa melakukan ini, jadi itu melempar a null pointer exception
.
Secara umum, itu karena sesuatu belum diinisialisasi dengan benar.
NULL
ditulis seperti null
dalam java. Dan itu hal yang sensitif.
Banyak penjelasan sudah ada untuk menjelaskan bagaimana itu terjadi dan bagaimana cara memperbaikinya, tetapi Anda juga harus mengikuti praktik terbaik untuk menghindari NullPointerException
sama sekali.
Lihat juga: Daftar praktik terbaik yang baik
Saya akan menambahkan, sangat penting, manfaatkan final
pengubah.
Menggunakan pengubah "final" kapan pun berlaku di Jawa
Ringkasan:
final
pengubah untuk menegakkan inisialisasi yang baik.@NotNull
dan@Nullable
if("knownObject".equals(unknownObject)
valueOf()
lebih toString()
.StringUtils
metode aman nol StringUtils.isEmpty(null)
.@Nullable
seperti yang tercantum di atas) dan memperingatkan tentang kemungkinan kesalahan. Dimungkinkan juga untuk menyimpulkan dan membuat anotasi semacam itu (mis. IntelliJ dapat melakukan itu) berdasarkan pada struktur kode yang ada.
if (obj==null)
. Jika itu null maka Anda harus menulis kode untuk mengatasinya juga.
Di Jawa, semuanya (tidak termasuk tipe primitif) adalah dalam bentuk kelas.
Jika Anda ingin menggunakan objek apa pun maka Anda memiliki dua fase:
Contoh:
Object object;
object = new Object();
Sama untuk konsep array:
Item item[] = new Item[5];
item[0] = new Item();
Jika Anda tidak memberikan bagian inisialisasi maka NullPointerException
muncul.
Pengecualian null pointer adalah indikator bahwa Anda menggunakan objek tanpa menginisialisasi itu.
Sebagai contoh, di bawah ini adalah kelas siswa yang akan menggunakannya dalam kode kita.
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
Kode di bawah ini memberi Anda pengecualian null pointer.
public class School {
Student student;
public School() {
try {
student.getId();
}
catch(Exception e) {
System.out.println("Null pointer exception");
}
}
}
Karena Anda menggunakan student
, tetapi Anda lupa untuk menginisialisasi seperti dalam kode yang benar yang ditunjukkan di bawah ini:
public class School {
Student student;
public School() {
try {
student = new Student();
student.setId(12);
student.getId();
}
catch(Exception e) {
System.out.println("Null pointer exception");
}
}
}
Di jawa semua variabel yang Anda nyatakan sebenarnya "referensi" ke objek (atau primitif) dan bukan objek itu sendiri.
Ketika Anda mencoba untuk mengeksekusi satu metode objek, referensi meminta objek hidup untuk mengeksekusi metode itu. Tetapi jika referensi merujuk NULL (tidak ada, nol, batal, nada) maka tidak ada cara metode dijalankan. Kemudian runtime memberi tahu Anda hal ini dengan melempar NullPointerException.
Referensi Anda adalah "menunjuk" ke nol, dengan demikian "Null -> Pointer".
Objek hidup di ruang memori VM dan satu-satunya cara untuk mengaksesnya adalah menggunakan this
referensi. Ambil contoh ini:
public class Some {
private int id;
public int getId(){
return this.id;
}
public setId( int newId ) {
this.id = newId;
}
}
Dan di tempat lain dalam kode Anda:
Some reference = new Some(); // Point to a new object of type Some()
Some otherReference = null; // Initiallly this points to NULL
reference.setId( 1 ); // Execute setId method, now private var id is 1
System.out.println( reference.getId() ); // Prints 1 to the console
otherReference = reference // Now they both point to the only object.
reference = null; // "reference" now point to null.
// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );
// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...
Ini hal yang penting untuk diketahui - ketika tidak ada lagi referensi ke suatu objek (dalam contoh di atas kapan reference
dan otherReference
keduanya menunjuk ke nol) maka objek tersebut "tidak dapat dijangkau". Tidak ada cara kita dapat bekerja dengannya, jadi objek ini siap untuk dikumpulkan, dan pada titik tertentu, VM akan membebaskan memori yang digunakan oleh objek ini dan akan mengalokasikan yang lain.
Kejadian lain dari suatu NullPointerException
terjadi ketika seseorang menyatakan array objek, kemudian segera mencoba untuk meringkas elemen di dalamnya.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
NPE khusus ini dapat dihindari jika urutan perbandingan dibalik; yaitu, gunakan.equals
pada objek non-null yang dijamin.
Semua elemen di dalam array diinisialisasi ke nilai awal bersama ; untuk semua jenis array objek, itu artinya semua elemen adalahnull
.
Anda harus menginisialisasi elemen dalam array sebelum mengakses atau mereferensikannya.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Optional
adalah mengembalikan nol. Kata kuncinya baik-baik saja. Mengetahui bagaimana cara menjaganya tidak penting. Ini menawarkan satu kejadian umum dan cara untuk menguranginya.