Voyons une question sur la chaîne en Java: la différence entre "ABC" + '/' et "ABC" + "/". Grâce à cet exemple, nous pouvons pratiquer l'utilisation de Javap dans les outils JDK. La question initiale est la suivante:
Quelle est la différence entre le traitement des barres obliques / comme des personnages ou des chaînes?
L'un est utilisé comme type de type de données de base, et l'autre est la chaîne d'objet. Quelle est la différence spécifique?
Sera-t-il plus efficace de le traiter comme un personnage?
String str = "ABC" + '/';
et
String str = "ABC" + "/";
1. Optimisation du compilateur
Tout d'abord, vous devez savoir que les deux phrases ci-dessus ont le même effet, car le compilateur optimisera les deux phrases ci-dessus dans ce qui suit:
String str = "ABC /";
Nous pouvons le prouver via Javap. Pour Javap, nous pouvons nous référer à "5 outils JDK que chaque développeur Java devrait connaître". Nous créons d'abord une classe: StringOne et remplissons le code suivant dans la méthode principale:
StringOne.Javastrring str1 = "ABC" + '/'; String str2 = "ABC" + "/";system.out.println(str1 == str2);
Compiler et s'exécuter, le résultat de sortie est vrai. Ensuite, notre Javap est lancé, entrez la commande suivante dans la ligne de commande:
javap -v -l stringone.class> stringone.s
Vérifiez ensuite le fichier stringone.s généré. Vous constaterez qu'il y a plusieurs lignes
StringOne.S # 2 = chaîne # 20 // ABC /...# 20 = UTF8 ABC / ... 0: LDC # 2 // String ABC / 2: Store_13: LDC # 2 // String ABC / 5: Store_2
Explication: STR1 et STR2 se réfèrent à la chaîne "ABC /".
2. Utilisez Javap pour analyser les différences
Changeons maintenant la question. Quelle est la différence entre les méthodes StringAddString et StringAddChar dans le code suivant?
StringTwopublic Static String StringAddString (String str1, string str2) {return str1 + str2;} public static string stringAddchar (String str, char ch) {return str + ch;}Cette fois, Javap est utilisé pour la décompilation, et certains des contenus du fichier généré sont les suivants
Stringtwo.spublic java.lang.string stringAddString (java.lang.string, java.lang.string); Descripteur: (Ljava / Lang / String; Ljava / Lang / String;) Ljava / Lang / String; Indicateurs: CODE ACC_PUBLIC: Stack = 2, Locals = 3, args_size = 3 0: new # 2 // class Java / Lang / StringBuilder 3: Dup 4: InvokeSpecial # 3 // Method Java / Lang / StringBuilder. "<init>" :() V 7: ALOAD_1: invokevirtual # 4 // Method Java / Lang / Stringbuder. APPENDE: (Ljava / Lang / String;) Ljava / Lang / StringBuilder; 11: ALOAD_2 12: invokeVirtual # 4 // Méthode Java / Lang / StringBuilder. APPENDE: (Ljava / Lang / String;) Ljava / Lang / StringBuilder; 15: InvokeVirtual # 5 // Méthode Java / Lang / StringBuilder. toString :() ljava / lang / string; 18: Areturnpublic java.lang.string stringaddchar (java.lang.string, char); Descripteur: (Ljava / Lang / String; C) Ljava / Lang / String; Flags: CODE ACC_PUBLIC: Stack = 2, Locals = 3, args_size = 3 0: new # 2 // class Java / Lang / StringBuilder 3: Dup 4: InvokeSpecial # 3 // Method Java / Lang / StringBuilder. "<init>" :() V 7: aload_1 8: invokevirtual # 4 // Method Method java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 11: Iload_2 12: invokevirtual # 6 // Méthode java / lang / stringBuilder.append: (c) ljava / lang / stringBuilder; 15: invokevirtual # 5 // Méthode java / lang / stringBuilder.tostring :() ljava / lang / string; 18: Areturn
Maintenant, nous pouvons clairement voir le processus d'exécution de ces deux méthodes:
stringAddString
La procédure de StringAddChar est la même que StringAddString, sauf que lorsque la méthode d'ajout est appelée la deuxième fois, le paramètre de StringAddString est de type String, tandis que le paramètre de StringAddChar est de type char.
3. Méthode d'ajout (char) et de la méthode d'appel (String) de la classe StringBuilder
Ici, nous devons simplement vérifier directement le code source (le mien est le code source qui est livré avec JDK1.8.0_60). Notez que bien que le document montre que StringBuilder hérite de l'objet, du code source, il est hérité de la classe abstraite AbstractStringBuilder. Et la méthode d'ajout est implémentée par AbstractStringBuilder.
AbstractStringBuilder.java
Public AbstractStringBuilder APPEND (char c) {AssurecapacityInternal (Count + 1); // Assurez-vous que le tableau peut accueillir le nombre de comptes + 1 caractères [Count ++] = C; renvoie ceci;} public abstractStringBuilder append (String str) {if (str == null) return appendNull (); int len = str.length (); AssurecapacityInternal (Count + Len); str.getchars (0, len, valeur, comte); // Copiez le tableau de caractères dans la chaîne dans le tableau de caractères de ce nombre d'objets + = len; retourne ceci;}Le reste ne sera plus affiché. String.getChars (int, int, char [], int) dépend finalement de l'arraycopie de void native statique statique (objet, int, objet, int, int). En d'autres termes, il peut être écrit en langue C, et l'efficacité devrait être meilleure lors de la copie de gros tableaux que les programmes écrits en Java. Alors, maintenant laissez-moi vous dire ce que je comprends:
En termes de mémoire directe, puisque la chaîne contient des tableaux de char, le tableau doit avoir des champs de longueur. Dans le même temps, la classe String a une propriété INT Hash, et l'objet lui-même occupera une mémoire supplémentaire pour stocker d'autres informations, de sorte que la chaîne occupera plus de mémoire. Cependant, si la chaîne est très longue, ces frais généraux de mémoire peuvent être presque ignorés; Et si la chaîne est (très) courte, il est très probable qu'il existe de nombreuses références partagées pour partager ces frais généraux de mémoire, de sorte que l'excès de surcharge de mémoire peut toujours être ignoré.
Dans la pile d'appels, puisque String n'a qu'un ou deux appels de fonction supplémentaires que Char ici, si la surcharge d'appel de fonction (y compris le temps et l'espace) n'est pas prise en compte, elle doit être similaire; Si la surcharge de l'appel de fonction est considérée, "ABC" + '/' devrait être meilleur; Mais lorsque plusieurs caractères doivent être connectés (je pense que cette situation devrait être plus courante, non?), car l'utilisation de char nécessite de nombreuses boucles pour terminer la connexion, le nombre de fonctions appelées ne sera plus appelée que l'utilisation de la chaîne. En même temps, la copie ne sera pas plus rapide que la chaîne en copie directement un tableau. Donc, pour le moment, il devient "ABC" + "/" avec un débit plus important.
Maintenant, j'ai l'impression que cette question se pose: est-il plus efficace d'utiliser des appels système lors de la lecture et de l'écriture de fichiers, ou est-il plus efficace d'utiliser la bibliothèque IO dans la bibliothèque de fonctions standard. Je pense personnellement que bien que la bibliothèque IO standard doit finalement appeler des appels système, et que certaines variables temporaires et des piles d'appels plus profondes seront générées, en raison des mécanismes tampon de la bibliothèque IO, le débit de la bibliothèque IO sera plus important, et les performances en temps réel des appels système seront meilleures. De même, bien que la classe String aura plus de champs et une pile de fonction plus profonde, le débit devrait être meilleur en raison du cache et de la copie plus directe.
Nouvelles questions
À en juger par le code de décompilation JAVAP ci-dessus, lorsque les deux chaînes seront additionnées, elles deviendront des chaînes d'ajout dans StringBuilder. Donc, en théorie, lequel des code suivant est le plus efficace?
String str1 = "ABC" + "123"; // 1StringBuilder StringBuilder = new StringBuilder (); // 2StringBuilder.append ("ABC"); StringBuilder.Apend ("123"); String str2 = stringBuilder.ToString ();Que cette question soit laissée à tout le monde pour réfléchir!
Ce qui précède est tout le contenu de cet article, ce qui vous aide à mieux distinguer String + String et String + Char en Java. J'espère que cela sera utile à l'apprentissage de tous.