89 votes

Pourquoi cela ne déclenche-t-il pas une NullPointerException ?

J'ai une confusion sur le comportement suivant, s'il vous plaît regardez le code ci-dessous :

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

Ceci imprimera B donc cela prouve sample et referToSample font référence à la même référence mémoire.

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

Ceci imprimera AB qui prouve également la même chose.

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

Évidemment, cela va jeter NullPointerException parce que j'essaie d'appeler append sur une référence nulle.

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

Voici donc ma question, pourquoi le dernier exemple de code ne lance pas NullPointerException parce que ce que je vois et comprends dans les deux premiers exemples, c'est que si deux objets se réfèrent au même objet, si nous changeons une valeur, cela se reflétera également dans l'autre car les deux objets pointent vers la même référence mémoire. Alors pourquoi cette règle ne s'applique-t-elle pas ici ? Si j'assigne null pour referToSample, l'échantillon devrait également être nul et une NullPointerException devrait être levée, mais ce n'est pas le cas, pourquoi ?

89voto

hexafraction Points 16201

null les affectations ne changent pas valeur en détruisant globalement cet objet. Ce genre de comportement entraînerait des bogues difficiles à repérer et un comportement contre-intuitif. Ils ne cassent que cet objet spécifique référence .

Pour simplifier, disons que sample indique l'adresse 12345. Ce n'est probablement pas l'adresse, et elle est seulement utilisée pour simplifier les choses ici. L'adresse est la suivante généralement représenté avec l'hexadécimal bizarre donné dans Object#hashCode() mais cela dépend de l'implémentation. 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);

A partir des lignes marquées See diagram les diagrammes des objets à cette époque sont les suivants :

Diagramme 1 :

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

Diagramme 2 :

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

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

Le diagramme 2 montre que l'annulation referToSample ne rompt pas la référence de sample au StringBuilder à 00012345 .

1 Des considérations de GC rendent cela peu plausible.

62voto

Parth Points 2777

Au départ, c'était comme vous l'avez dit referToSample faisait référence à sample comme indiqué ci-dessous :

1. Scénario 1 :

referToSample refers to sample

2. Scénario1 (suite) :

referToSample.append("B")

  • Ici, comme referToSample faisait référence à sample donc il a ajouté "B" pendant que vous écrivez

    referToSample.append("B")

La même chose s'est produite dans Scénario2 :

Mais, en 3. Scénario3 : comme l'a dit l'hexafraction,

lorsque vous attribuez null à referToSample alors qu'il faisait référence sample il n'a pas changé valeur au lieu de cela, il casse simplement le référence de sample et maintenant il indique nulle part . comme indiqué ci-dessous :

when referToSample = null

Maintenant, comme referToSample points nulle part donc pendant que vous referToSample.append("A"); il n'y aurait pas de valeurs ou de références où il pourrait ajouter A. Donc, il jetterait NullPointerException .

MAIS sample est toujours le même que celui que vous aviez initialisé avec

StringBuilder sample = new StringBuilder(); donc il a été initialisé, donc maintenant il peut ajouter A, et ne jettera pas NullPointerException

17voto

En résumé : On assigne null à une variable de référence, pas à un objet.

Dans un exemple, vous modifiez le état d'un objet qui est référencé par deux variables de référence. Lorsque cela se produit, les deux variables de référence reflètent le changement.

Dans un autre exemple, vous modifiez la référence affectée à une variable, mais cela n'a aucun effet sur l'objet lui-même. Ainsi, la deuxième variable, qui fait toujours référence à l'objet original, ne remarquera aucun changement dans l'état de l'objet.


En ce qui concerne vos "règles" spécifiques :

Si deux objets se réfèrent au même objet, si nous changeons une valeur, cela se reflétera également sur l'autre car les deux objets pointent vers la même référence mémoire.

Encore une fois, vous faites référence à la modification de la état de l'unique objet que les deux variables se référer.

Alors pourquoi cette règle ne s'applique-t-elle pas ici ? Si j'assigne null à referToSample, alors sample devrait aussi être null et il devrait lancer nullPointerException mais il ne le fait pas, pourquoi ?

Encore une fois, vous changez le référence d'une variable qui a absolument aucun effet sur le référence de l'autre variable.

Il s'agit de deux actions complètement différentes qui donneront deux résultats complètement différents.

8voto

Doorknob Points 23912

Voir ce simple diagramme :

diagram

Lorsque vous appelez un méthode sur referToSample alors [your object] est mis à jour, ce qui affecte sample aussi. Mais quand vous dites referToSample = null alors vous changez simplement ce que referToSample renvoie à à.

3voto

CaZbaN Points 242

Ici, 'sample' et 'referToSample' font référence au même objet, ce qui est le concept de pointeur différent accédant au même emplacement mémoire. Ainsi, l'affectation d'une variable de référence à null ne détruit pas l'objet.

   referToSample = null;

signifie que 'referToSample' ne pointe que vers null, l'objet reste le même et les autres variables de référence fonctionnent bien. Donc pour 'sample' qui ne pointe pas vers null et qui a un objet valide

   sample.append("A");

fonctionne bien. Mais si nous essayons d'ajouter null à 'referToSample', il y aura une NullPointException. C'est-à-dire,

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

C'est pourquoi vous avez obtenu une exception NullPointerException dans votre troisième extrait de code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X