41 votes

Pourquoi String toCharArray n'utilise-t-il pas Arrays.copyOf?

Dans la source JDK de java.lang.String.toCharArray , il n'utilise pas Arrays.copyOf pour l'implémenter et il dit:

Impossible d'utiliser Arrays.copyOf en raison de problèmes d'ordre d'initialisation de classe

Quels sont les "problèmes d'ordre d'initialisation de classe"?

 public char[] toCharArray() {
    // Cannot use Arrays.copyOf because of class initialization order issues
    char result[] = new char[value.length];
    System.arraycopy(value, 0, result, 0, value.length);
    return result;
}
 

43voto

terry.qiao Points 1102

J'ai fait un test à ce sujet. Voici les pièces que j'ai fait:

  1. Obtenir le code source d' String.java de JDK.
  2. Modifier son toCharArray méthode Arrays.copyOf.

    comme ceci:

    public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        /*char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;*/
        return Arrays.copyOf(value, value.length);
    }
    
  3. compiler ce modifiés String, et l'enregistrer de nouveau dans JRE rt.jar.

  4. Écrire une simple HelloWorld code Java.

  5. Compiler et exécuter le code à l'aide d' java programme.

Et enfin, j'obtiens ceci:

PS D:\> & 'C:\Program Files (x86)\Java\jdk1.8.0_121\jre\bin\java.exe' StringTest
Error occurred during initialization of VM
java.lang.NullPointerException
    at java.util.Hashtable.remove(Hashtable.java:491)
    at java.lang.System.initProperties(Native Method)
    at java.lang.System.initializeSystemClass(System.java:1166)

Nous pouvons voir qu'il y a vraiment un initialization d'erreur. Et parce qu' System.initProperties est une méthode native, je ne peux pas vérifier son code.

Cependant, Nous pouvons faire deviner pourquoi cela pourrait se produire:

  1. System.initProperties doit gérer certaines Chaînes tandis qu'il initialise les propriétés du système.
  2. Et tout en faisant initialiser, il pourrait invoquer String.toCharArray pour obtenir des tableaux de char de ces chaînes.
  3. Les chaînes invoquer Arrays.copyOf, mais à ce point, & cette fois, Arrays n'a pas été chargé / initialisé.
  4. Différentes de l'exécution du code Java, le code natif n'en demandait pas un class initializing request (je ne suis pas sûr à ce sujet, s'il vous plaît laissez-moi savoir si je me trompe!!), et qui va conduire à un NullPointerException et de faire des VM de sortie.

2018.04.10 mise à Jour.

Je tiens à Remercier @Radiodef pour son astuce. Mais quand j'ai essayé d'aller dans le C++ codes, j'ai été arrêté par un si grand nombre de chemins d'exécution qui je ne pouvais pas gérer sans courir le débogage ou de l'OpenJDK de la JVM.

Et puis, j'ai changé ma stratégie. J'ai fait un peu plus de test basée sur l'au-dessus de laquelle je l'avais fait pour quelques jours.

Cette fois, je ne vais pas utiliser Arrays.copyOf avec String.toCharArray, au lieu de cela, je tente de trouver lequel les codes de l'invoquer toCharArray méthode alors que la JVM en cours d'initialisation.

J'ai donc modifier String, ajouter deux variables statiques pour elle:

public static int count;
public static Throwable[] logs = new Throwable[10000];

En ce qui count est utilisé pour compter de l'invocation de l' toCharArray, logs est utilisé pour garder ceux invocation de traces de pile.

En toCharArray méthode:

public char[] toCharArray() {
    if (count < logs.length) {
        try {
            throw new RuntimeException();
        } catch (Throwable e) {
            logs[count] = e;
        }
    }
    count++;

    // Cannot use Arrays.copyOf because of class initialization order issues
    char result[] = new char[value.length];
    System.arraycopy(value, 0, result, 0, value.length);
    return result;
}

Après avoir fait ces, je compile String nouveau et de les enregistrer en arrière dans rt.jar.

Ensuite, j'écris un programme de test pour imprimer count et l'invocation des traces de pile en sortie:

Class<String> clazz = String.class;
Field count = clazz.getDeclaredField("count");
System.out.println(count.getInt(null));
Field logs = clazz.getDeclaredField("logs");
Throwable[] arr = (Throwable[]) logs.get(null);
for (Throwable e : arr) {
    if (e != null)
        e.printStackTrace(System.out);
}

Nous ne pouvons pas accéder String.count & String.logs directement dans nos codes, comme compilateur (javac) ne reconnaît pas ces champs et provoquera une erreur de compilation. C'est pourquoi je suis à l'aide de réfléchir façon de le faire.

Exécutez le programme que nous avons réalisé, et les résultats seront les suivants:

525
java.lang.RuntimeException
    at java.lang.String.toCharArray(String.java:2889)
    at sun.nio.cs.ext.GBK.initb2c(Unknown Source)
    at sun.nio.cs.ext.GBK.newDecoder(Unknown Source)
    at java.lang.StringCoding$StringDecoder.<init>(Unknown Source)
    at java.lang.StringCoding$StringDecoder.<init>(Unknown Source)
    at java.lang.StringCoding.decode(Unknown Source)
    at java.lang.String.<init>(String.java:414)
    at java.lang.String.<init>(String.java:479)
    at java.lang.System.initProperties(Native Method)
    at java.lang.System.initializeSystemClass(Unknown Source)

......

java.lang.RuntimeException
    at java.lang.String.toCharArray(String.java:2889)
    at sun.net.www.ParseUtil.encodePath(Unknown Source)
    at sun.misc.URLClassPath$FileLoader.getResource(Unknown Source)
    at sun.misc.URLClassPath.getResource(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

Ce qu'une longue liste d'invocation. Cependant il est plus clair que le test précédent. Nous pouvons clairement voir les classes invoquer toCharArray.

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