Je lisais "Penser en Java" récemment et j'ai vu ce passage :
Les primitives qui sont des champs dans une classe sont automatiquement initialisées à zéro, comme indiqué dans le Chapitre Tout est un objet. Mais les références d'objet sont initialisées à null, et si vous essayez d'appeler des méthodes pour l'une d'entre elles, vous obtiendrez une exception - une erreur d'exécution. Idéalement, vous pouvez toujours imprimer une référence nulle sans lancer d'erreur. exception.
L'idée principale est la suivante : le type natif sera automatiquement initialisé à 0, mais la référence de l'objet sera initialisée à null. Si vous essayez d'appeler la méthode de l'objet, une exception de pointeur nul sera levée. Normalement, vous pouvez imprimer un objet nul sans lever d'exception.
Je pense que tout le monde comprendra facilement la première phrase. Il s'agit des connaissances de base sur l'initialisation de type, mais la deuxième phrase me rend très confus : pourquoi l'impression d'un objet nul ne lève-t-elle pas d'exception ? C’est avec cette question à l’esprit que j’ai commencé mon voyage de compréhension. Ci-dessous, je développerai mes idées pour résoudre ce problème et explorerai le code source du JDK pour trouver la réponse au problème.
On peut constater qu'il existe en fait plusieurs situations pour ce problème, nous discuterons donc de diverses situations en catégories pour voir si nous pouvons obtenir la réponse dans le fin.
Tout d'abord, nous divisons ce problème en trois petits problèmes et les résolvons un par un.
Quel résultat obtiendrez-vous si vous imprimez directement un objet String nul ? Le résultat de l'exécution de
String s = null; System.out.print(s);
est
null
, comme le dit le livre, aucune exception n'est levée, mais null
est imprimé. Évidemment, l'indice du problème réside dans le code source de la fonction print
. Nous avons trouvé le code source de print
:
public void print(String s) { if (s == null) { s = "null"; } write(s); }
Quand j'ai vu le code source, j'ai réalisé que c'était juste un jugement. C'est simple et grossier. Peut-être êtes-vous un peu déçu du simple. implémentation du JDK. Ne vous inquiétez pas, la première question n’est qu’une entrée, le repas est encore à venir.
Imprimer un objet non-String nul, par exemple Integer :
Integer i = null; System.out.print(i);
Le résultat de l'opération n'est pas surprenant :
null
Jetons un coup d'œil au code source de print
:
public void print(Object obj) { write(String.valueOf(obj)); }
est un peu différent. Il semble que le secret soit caché dans valueOf
.
public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
En voyant cela, nous avons finalement découvert le secret de l'impression d'objets nuls sans lever d'exceptions. La méthode print
gère séparément les objets String et les objets non-String.
Objet chaîne : déterminez directement s'il est nul. S'il est nul, attribuez la valeur à l'objet nul comme "null"
.
Objet non-String : En appelant la méthode String.valueOf
, s'il s'agit d'un objet nul, renvoyez "null"
, sinon appelez le toString
de l'objet méthode.
Grâce au traitement ci-dessus, il peut être garanti que l'impression d'objets nuls ne provoquera pas d'erreurs.
Ici, cet article devrait se terminer.
Quoi ? Où est le dîner promis ? Il ne suffit pas de tenir entre les dents.
Je plaisante. Explorons la troisième question ci-dessous.
Quel sera le résultat de la concaténation d'un objet nul et d'une chaîne ?
String s = null; s = s + "!"; System.out.print(s);
Vous avez peut-être deviné le résultat :
null!
Pourquoi ? Le suivi de l'exécution du code montre que cette fois cela n'a rien à voir avec print
. Mais le code ci-dessus appelle la fonction print
, qui d'autre cela pourrait-il être ? +
est le plus suspect, mais +
n'est pas une fonction. Comment pouvons-nous voir son code source ? Dans ce cas, la seule explication est que le compilateur l'a falsifié. Le code source est introuvable. Nous pouvons jeter un œil au bytecode généré par le compilateur.
L0 LINENUMBER 27 L0 ACONST_NULL ASTORE 1 L1 LINENUMBER 28 L1 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V ALOAD 1 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; LDC "!" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; ASTORE 1 L2 LINENUMBER 29 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
Êtes-vous confus après avoir lu le bytecode ci-dessus ? Ici, nous allons changer de sujet et parler du +
principe de l'épissage des cordes.
Le compilateur optimisera l'ajout de chaînes. Tout d'abord, instanciera un StringBuilder
, puis mettra les chaînes ajoutées dans l'ordre append
, et enfin appellera toString
pour renvoyer un objet String
. Si vous ne me croyez pas, jetez un œil au bytecode ci-dessus pour voir si StringBuilder
apparaît. Pour une explication détaillée, veuillez vous référer à cet article Détails Java : Épissure de chaînes.
String s = "a" + "b"; //等价于 StringBuilder sb = new StringBuilder(); sb.append("a"); sb.append("b"); String s = sb.toString();
Retour à notre problème, nous savons maintenant que le secret est dans le code source de la fonction StringBuilder.append
.
//针对 String 对象 public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } //针对非 String 对象 public AbstractStringBuilder append(Object obj) { return append(String.valueOf(obj)); } private AbstractStringBuilder appendNull() { int c = count; ensureCapacityInternal(c + 4); final char[] value = this.value; value[c++] = 'n'; value[c++] = 'u'; value[c++] = 'l'; value[c++] = 'l'; count = c; return this; }
Maintenant, nous réalisons soudain que si la fonction append
détermine que l'objet est nul, elle appellera appendNull
et remplira "null"
.
Nous avons discuté de trois problèmes ci-dessus, qui conduisent à la gestion tolérante aux pannes des objets String null en Java. L'exemple ci-dessus ne couvre pas toutes les situations de traitement et est considéré comme une introduction.
Comment garder l'objet nul dans le programme sous notre contrôle est quelque chose auquel nous devons toujours prêter attention lors de la programmation.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!