Bu neden bir NullPointerException oluşturmuyor?


89

Aşağıdaki kod için nead açıklama:

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample.append("B");
System.out.println(sample);

Bu, Bkanıtların sampleve referToSamplenesnelerin aynı bellek referansına başvurması için yazdırılacaktır .

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
sample.append("A");
referToSample.append("B");
System.out.println(referToSample);

Bu ABda aynı şeyi kanıtlayan basılacaktır .

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
referToSample.append("A");
System.out.println(sample);

Açıkçası bu atacak NullPointerExceptionçünkü appendboş bir referans çağırmaya çalışıyorum .

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
sample.append("A");
System.out.println(sample);

İşte sorum şu, neden son kod örneği atılmıyor NullPointerExceptionçünkü ilk iki örnekte gördüğüm ve anladığım şey, aynı nesneye atıfta bulunan iki nesnenin herhangi bir değeri değiştirdiğimizde diğerine de yansıyacak çünkü her ikisi de aynı bellek referansı. Öyleyse bu kural neden burada uygulanmıyor? Ben atarsanız nullreferToSample sonra numune de null olmalıdır ve bir NullPointerException atarsam ama bir tane atma değil, neden?


31
samplehala sample. Sadece değiştin referToSample.
Dave Newton

25
Olumlu oy verildi / yıldız verildi! Çok basit bir soru, ama bu sorununuzu açıklamanın ve soruyu iyi sormanın güzel bir örneğidir .
Ryan Ransford

15
Sorunuzda bir terminoloji noktası: nesneleresample ve nesnelere atıfta bulunmaya devam edersiniz referToSample, ancak bunlar nesne değildir, değişkenlerdir. Bir değişken, bir nesneye referans tutabilir, ancak kendisi bir nesne değildir. Bu ince bir ayrım, ama temelde kafa karışıklığınızın özü.
Daniel Pryden

1
Nesne değişkenlerini sadece işaretçiler olarak düşünmeye yardımcı olur. Bir değişken etki eder herhangi bir operatör ( volatile, final, =, ==...) bir amacı, değişken uygulandığında etki gösterici değil, bunun ifade eder nesne.
MikeFHay

2
@Arpit Saygıyla katılmıyorum. Orada bu soruda gizli büyük bir kavramdır ve bu, bir arasındaki farktır nesne ve bir referans olduğunu nesneye. Çoğu zaman, bu farkın farkına varmamıza gerek yok (ne de istemiyoruz) ve dil tasarımcıları bunu bizden saklamak için çok çalışıyorlar. Örneğin, C ++ 'daki referansla geçiş argümanlarını düşünün. Bu yüzden yeni başlayanların tüm bu sihirle karıştırıldığını görmek benim için şaşırtıcı değil!
Gyom

Yanıtlar:


89

nullatamalar, bu nesneyi genel olarak yok ederek değeri değiştirmez . Bu tür davranışlar, izlenmesi zor hatalara ve mantık dışı davranışlara yol açar. Sadece bu spesifik referansı kırarlar .

Basit olması için, bunun sample12345 adresini gösterdiğini varsayalım. Bu muhtemelen adres değildir ve sadece burada işleri basitleştirmek için kullanılır. Adres tipik olarak verilen garip onaltılık ile temsil edilir Object#hashCode(), ancak bu uygulamaya bağlıdır. 1

StringBuilder sample = new StringBuilder(); //sample refers to 
//StringBuilder at 12345 

StringBuilder referToSample = sample; //referToSample refers to 
//the same StringBuilder at 12345 
//SEE DIAGRAM 1

referToSample = null; //referToSample NOW refers to 00000, 
//so accessing it will throw a NPE. 
//The other reference is not affected.
//SEE DIAGRAM 2

sample.append("A"); //sample STILL refers to the same StringBuilder at 12345 
System.out.println(sample);

İşaretlenen satırlardan See diagramo zamanki nesnelerin diyagramları aşağıdaki gibidir:

Diyagram 1:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]
                                                      ↑
[StringBuilder referToSample] ------------------------/

Diyagram 2:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]

[StringBuilder referToSample] ---->> [null pointer]

Diyagram 2, iptal referToSampleetmenin sample, adresindeki StringBuilder başvurusunu bozmadığını gösterir 00012345.

1 GC değerlendirmeleri bunu mantıksız kılar.


@commit, sorunuzu bu yanıt veriyorsa, lütfen yukarıdaki metnin solundaki oy oklarının altındaki onay işaretine tıklayın.
Ray Britton

@RayBritton İnsanların, en yüksek oyu alan cevabın, soruyu gereği gibi cevaplayan olduğunu varsaymalarına bayılıyorum. Bu sayı önemli olsa da, tek ölçüm değildir; -1 ve -2 cevapları, OP'ye en çok yardımcı oldukları için kabul edilebilir.
nanofarad

11
hashCode()olduğu değil hafıza adresi ile aynı şey
Kevin Panko

6
Kimlik hashCode, genellikle yöntem ilk çağrıldığında Nesnenin bellek konumundan türetilir. O andan itibaren bu hashCode sabitlenir ve bellek yöneticisi nesneyi farklı bir bellek konumuna taşımaya karar verse bile hatırlanır.
Holger

3
Geçersiz kılan sınıflar, hashCodegenellikle bellek adresleriyle ilgisi olmayan bir değer döndürmek için onu tanımlar. Nesne referansları ve nesneler hakkında bahsetmeden fikir verebileceğinizi düşünüyorum hashCode. Hafıza adresinin nasıl alınacağına dair daha ince noktalar başka bir güne bırakılabilir.
Kevin Panko

62

Başlangıçta, aşağıda gösterildiği gibi referToSampleatıfta bulunduğunu söylediğiniz gibiydi sample:

1. Senaryo1:

referToSample, örnek anlamına gelir

2. Senaryo1 (devam):

referToSample.append ("B")

  • Burada referToSamplebahsedildiği gibi , samplesiz yazarken "B" eklenmiştir

    referToSample.append("B")

Senaryo2'de de aynı şey oldu :

Ancak, 3. Senaryo 3'te: hexafraction'ın dediği gibi,

Eğer atadığınızda nulliçin referToSampleo atıfta bulundu zaman samplebunu değiştirmek vermedi değeri yerine sadece kırar referansı gelen sampleve şimdi işaret hiçbir yerde . Aşağıda gösterildiği gibi:

referToSample = null olduğunda

Şimdi, hiçbir yerdereferToSample işaret etmeyeceği için, sizde A'yı ekleyebileceği herhangi bir değer veya referansa sahip olmayacaksınız . Yani, atacaktı .referToSample.append("A");NullPointerException

ANCAK sample, onu başlattığınız ile hala aynı

StringBuilder sample = new StringBuilder(); bu nedenle, baş harfine dönüştürülmüştür, yani şimdi A ekleyebilir ve NullPointerException


5
Güzel diyagramlar. Göstermeye yardımcı olur.
nanofarad

güzel şema, ancak alttaki not 'Now referToSample, ""' anlamına gelmiyor olmalı çünkü bunu referToSample = sample;yaptığınızda örneklemden bahsediyorsunuz, sadece referans verilen adresi kopyalıyorsunuz
Khaled.K

17

Özetle: Bir nesneye değil, bir referans değişkenine null atarsınız.

Bir örnekte , iki referans değişkeni tarafından başvurulan bir nesnenin durumunu değiştirirsiniz . Bu gerçekleştiğinde, her iki referans değişkeni de değişikliği yansıtacaktır.

Başka bir örnekte, bir değişkene atanan referansı değiştirirsiniz, ancak bunun nesnenin kendisi üzerinde bir etkisi yoktur ve bu nedenle, hala orijinal nesneye başvuran ikinci değişken, nesne durumunda herhangi bir değişiklik fark etmez.


Özel "kurallarınıza" gelince:

Eğer iki nesne aynı nesneye atıfta bulunursa, o zaman herhangi bir değeri değiştirirsek, o zaman diğerine de yansıyacaktır çünkü her ikisi de aynı bellek referansına işaret etmektedir.

Yine, her iki değişkenin de başvurduğu bir nesnenin durumunu değiştirmekten bahsediyorsunuz.

Öyleyse bu kural neden burada uygulanmıyor? ReferToSample öğesine null atarsam, örnek de null olmalıdır ve nullPointerException atması gerekir, ancak atmıyor, neden?

Yine, diğer değişkenin referansı üzerinde kesinlikle hiçbir etkisi olmayan bir değişkenin referansını değiştirirsiniz .

Bunlar tamamen farklı iki eylemdir ve tamamen farklı iki sonuçla sonuçlanacaktır.


3
@commit: Java'nın temelini oluşturan ve bir kez gördüğünüzde asla unutamayacağınız temel ama kritik bir kavramdır.
Hovercraft Full Of Eels

8

Şu basit şemaya bakın:

diyagram

Bir çağırdığınızda yöntemi üzerinde referToSample, daha sonra [your object]bu etkiler, böylece güncellenir samplede. Ama dediğinizde referToSample = null, o zaman sadece neyi referToSample ifade ettiğini değiştiriyorsunuz .


3

Burada 'örnek' ve 'referToSample' aynı nesneye referans veriyor. Bu aynı bellek konumuna erişen farklı işaretçi kavramıdır. Dolayısıyla null'a bir referans değişkeninin atanması nesneyi yok etmez.

   referToSample = null;

'referToSample' sadece boşa işaret ediyor, nesne aynı kalıyor ve diğer referans değişkenleri iyi çalışıyor. Bu nedenle, boş değeri göstermeyen ve geçerli bir nesnesi olan 'örnek' için

   sample.append("A");

iyi çalışıyor. Ancak 'referToSample'ye null eklemeye çalışırsak, NullPointException gösterecektir. Yani,

   referToSample .append("A");-------> NullPointerException

Üçüncü kod pasajınızda NullPointerException olmasının nedeni budur.


0

Bir zaman yeni bir anahtar kelime kullanılır o Heap bir nesnesi oluşturur

1) StringBuilder örneği = new StringBuilder ();

2) StringBuilder referToSample = örnek;

2) 'de referSample referansı aynı nesne örneğinde oluşturulur

bu nedenle referToSample = null; Sadece referSample Referansı sıfırlanıyor, bu yüzden Java'nın Çöp Toplama İşlemi Sayesinde NULL İşaretçi İstisnası almıyorsunuz


0

Sadece kolay, Java referansla geçişe sahip değil, sadece nesne referansını iletiyor.


2
Parametre geçiren anlambilimin anlatılan durumla gerçekten hiçbir ilgisi yoktur.
Michael Myers
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.