Maison  >  Article  >  Java  >  Résumé de la gestion tolérante aux pannes des objets nuls par Java String

Résumé de la gestion tolérante aux pannes des objets nuls par Java String

黄舟
黄舟original
2017-03-31 11:07:061542parcourir

Avant-propos

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.

Le processus de résolution du 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.

La première question

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.

La deuxième question

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.

  1. Objet chaîne  : déterminez directement s'il est nul. S'il est nul, attribuez la valeur à l'objet nul comme "null".

  2. 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.

La troisième question (fête cachée)

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++] = &#39;n&#39;;
    value[c++] = &#39;u&#39;;
    value[c++] = &#39;l&#39;;
    value[c++] = &#39;l&#39;;
    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".

Résumé

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn