Почему String toCharArray не использует Arrays.copyOf?

В исходном коде JDK для java.lang.String.toCharArray он не использует Arrays.copyOf для реализации этого и говорит:

Cannot use Arrays.copyOf because of class initialization order issues

В чем заключаются «проблемы с порядком инициализации класса»?

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;
}

где инициализировать value? вы используете конструктор копирования? см. i-programmer.info/programmer-puzzles/168-c/…

AsfK 08.04.2018 09:05

@AsfK Я предполагаю, что OP принимает часть исходного кода JDK. (не MCVE?)

user202729 08.04.2018 09:08

Честно говоря, без серьезного анализа @BrianGoetz мог бы быть единственным человеком, который мог бы ответить.

chrylis -cautiouslyoptimistic- 08.04.2018 09:09

@Asfk Я думаю, что Arrays.copyOf означает использование Arrays.copyOf(value , value.length) для реализации.

shaoyihe 08.04.2018 09:10

@shaoyihe, ты прав, я ошибся. Я не знаком с этим методом, но когда я меняю значение на char [], он работает, возможно, возникает проблема несоответствия типов ..

AsfK 08.04.2018 09:16
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
42
5
1 281
1

Ответы 1

Я сделал тест по этому поводу. Ниже приведены работы, которые я сделал:

  1. Получите исходный код String.java от JDK.
  2. Измените его метод toCharArray, чтобы использовать Arrays.copyOf.

    как это:

    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. скомпилируйте этот модифицированный String и сохраните его обратно в файл JRE rt.jar.

  4. Напишите простой Java-код HelloWorld.

  5. Скомпилируйте и запустите код с помощью программы java.

И наконец, я получаю вот что:

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)

Мы видим, что это действительно ошибка initialization. И поскольку System.initProperties - это нативный метод, я не могу проверить его код.

Однако мы можем догадаться, почему это могло произойти:

  1. System.initProperties должен обрабатывать некоторые строки при инициализации свойств системы.
  2. И во время инициализации он может вызвать String.toCharArray для получения массивов символов из этих строк.
  3. Строки вызывают Arrays.copyOf, но в этот момент и на этот раз Arrays не был загружен / инициализирован.
  4. В отличие от запуска кода Java, собственный код не запрашивает class initializing request (я не уверен в этом, сообщите мне, если я ошибаюсь !!), и это приведет к NullPointerException и завершению работы виртуальной машины.

2018.04.10 Обновление.

Хочу поблагодарить @Radiodef за подсказку. Но когда я попытался войти в коды C++, меня остановило так много путей выполнения, с которыми я не мог справиться без запуска или отладки JVM OpenJDK.

А потом я изменил свою стратегию. Я сделал еще несколько тестов на основе вышеизложенного, которые я делал в течение нескольких дней.

На этот раз я не собираюсь использовать Arrays.copyOf с String.toCharArray, вместо этого я пытаюсь выяснить, какие коды будут вызывать метод toCharArray при инициализации JVM.

Итак, я модифицирую String, добавляя к нему две статические переменные:

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

В котором count используется для подсчета вызовов toCharArray, logs используется для хранения трассировки стека этих вызовов.

В методе toCharArray:

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;
}

После этого я снова компилирую String и сохраняю его обратно в rt.jar.

Затем я пишу тестовую программу для печати count и трассировки стека вызовов:

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);
}

Мы не можем получить доступ к String.count & String.logs непосредственно в наших кодах, поскольку компилятор (javac) не распознает эти поля и вызовет ошибку компиляции. Вот почему я использую для этого рефлексию.

Запустите программу, которую мы только что написали, и результат будет:

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)

Какой длинный список призывов. Однако он более ясен, чем предыдущий тест. Мы ясно видим, какие классы вызывают toCharArray.

Собственный код можно найти здесь, если кто хочет в нем поглядеть. Исключение в Hashtable строка 491 предполагает, что ключ, который является null, каким-то образом передается методу Properties.remove.
Radiodef 09.04.2018 00:30

Другие вопросы по теме