Почему строка не указывает на один и тот же объект в объединенной области строк?

Я знаю, что String неизменяем. В приведенном ниже примере объект константы String будет создан в объединенной области String, и s1 будет указывать на "Hello". Также s2 сделает константу String с тем же значением "Hello".

Но я не понимаю, почему s2 не указывают на первый "Привет". Насколько я понимаю, строка "Hello" уже существует в объединенной области строк, и если я создам другую строку с этим значением, она будет указывать на существующий объект, а не создавать другой объект. Например, s3 указывает на тот же объект, что и s1.

Я не использовал ключевое слово new для s2. Почему s2 не указывает на тот же объект, что и s1 и s3?

public class DemoApp {

    public static void main(String args[]) {

        String s1 = "Hello";
        String s2 = "Hello friends".substring(0, 5);
        String s3 = "Hello";

        System.out.println(s2);        //Hello
        System.out.println(s1 == s2);  //false
        System.out.println(s1 == s3);  //true
    }
}

Результат:

Hello
false 
true

person Community    schedule 22.04.2018    source источник
comment
Нет s3 и двух s1...?   -  person user202729    schedule 22.04.2018
comment
Я помню, что видел почти идентичный вопрос, подобный этому, с использованием toUpperCase... (я думаю, что он был отклонен и удален)   -  person user202729    schedule 22.04.2018
comment
Я думаю здесь...   -  person user202729    schedule 22.04.2018
comment
Я не использовал ключевое слово new для s2, вы не использовали его явно, но оно использовалось внутри методом substring.   -  person Pshemo    schedule 22.04.2018


Ответы (2)


Если вы посмотрите на реализацию метода подстроки, вы увидите, что он создает строку с оператором new, поэтому возвращаемая строка отсутствует в пуле строк.

public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
}

Замените свой код

String s2 = "Hello friends".substring(0, 5);

со строкой s2 = "Привет, друзья".substring(0, 5).intern();

вы увидите, что он возвращает true.

person Joydeep Bhattacharya    schedule 22.04.2018
comment
Обратите внимание, что String.intern() обычно не рекомендуется, используется здесь только для иллюстрации. - person k_ssb; 22.04.2018
comment
точно, только для демонстрационных целей. - person Joydeep Bhattacharya; 22.04.2018
comment
НЕ ИСПОЛЬЗУЙТЕ В ПРОИЗВОДСТВЕННОМ КОДЕ - person Joydeep Bhattacharya; 22.04.2018
comment
(если вы не знаете, что делаете!) - person k_ssb; 22.04.2018
comment
Вот как это реализовано, но это не объясняет почему: просто создание новой строки требует меньше усилий, чем предположительное создание строки и проверка, не находится ли она уже в пуле строк; в подавляющем, подавляющем большинстве случаев это просто не имеет значения, а в тех немногих случаях, которые имеют значение, у вас есть intern(). - person Andy Turner; 22.04.2018

Только строковые литералы (и константы во время компиляции) объединяются.

Вызов substring вычисляет новый String, который не поступает из пула. В конце концов, substring заранее не знает, объединен ли уже его результат. Поэтому он должен выделить место для нового String на тот случай, если результат еще не находится в пуле. Затем, если место для результата уже выделено, бесполезно «проверять», не объединен ли уже этот результат. Следовательно, вы получаете поведение, которое видите: результат substring не объединяется, даже если он равен чему-то еще в пуле.

person k_ssb    schedule 22.04.2018
comment
В пул объединяются не только литералы, любое константное выражение времени компиляции типа String будет в пуле, например. "" + 0. - person Andy Turner; 22.04.2018