Объединение нулевых строк в Java

Почему работает следующая работа? Я ожидал бы, что будет NullPointerException.

String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"

Ответ 1

Зачем это нужно?

JLS 5, раздел 15.18.1.1 JLS 8 § 15.18.1 "Оператор конкатенации строк +" , приводящий к JLS 8, § 5.1.11 "Преобразование строк" ​​, эта операция должна выполняться без сбоев:

... Теперь нужно учитывать только опорные значения. Если ссылка равна null, она преобразуется в строку "null" (четыре символа ASCII n, u, l, l). В противном случае преобразование выполняется как бы путем вызова метода toString ссылочного объекта без аргументов; но если результат вызова метода toString равен нулю, вместо этого используется строка "null".

Как это работает?

Посмотрите на байт-код! Компилятор принимает ваш код:

String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"

и скомпилирует его в байт-код, как если бы вы написали это:

String s = null;
s = new StringBuilder(String.valueOf(s)).append("hello").toString();
System.out.println(s); // prints "nullhello"

(Вы можете сделать это самостоятельно, используя javap -c)

Применить методы StringBuilder все дескриптор null просто отлично. В этом случае, поскольку null - это первый аргумент, вместо него вызывается String.valueOf(), поскольку StringBuilder не имеет конструктора, который принимает произвольный ссылочный тип.

Если бы вы сделали s = "hello" + s вместо этого, эквивалентный код:

s = new StringBuilder("hello").append(s).toString();

где в этом случае метод append принимает значение null, а затем передает его на String.valueOf().

Примечание. Конкатенация строк на самом деле является одним из редких мест, где компилятор принимает решение о том, какую оптимизацию выполнить. Таким образом, "точный эквивалентный" код может отличаться от компилятора к компилятору. Эта оптимизация разрешена JLS, раздел 15.18.1.2:

Чтобы увеличить производительность повторной конкатенации строк, компилятор Java может использовать класс StringBuffer или подобный метод для уменьшения количества промежуточных объектов String, созданных при оценке выражения.

Компилятором, который я использовал для определения "эквивалентного кода" выше, был компилятор Eclipse, ecj.

Ответ 2

См. раздел 5.4 и 15.18 языка Java спецификация:

Преобразование строк применяется только к операнды двоичного + оператора, когда одним из аргументов является String. В этот единственный случай, другой аргумент + преобразуется в String и новую строку, которая является конкатенация двух строк результат+. Преобразование строк подробно указывается в описание строки конкатенация + оператор.

и

Если только одно выражение операнда type String, тогда преобразование строки выполняется на другом операнде вывести строку во время выполнения. результатом является ссылка на строку объект (вновь созданный, если только выражение - постоянная времени компиляции выражение (§15.28)), то есть конкатенация двух операндов строки. Персонажи левый операнд предшествует символы правого операнда в вновь созданной строке. Если операнд типа String равен null, тогда строка "null" используется вместо этот операнд.

Ответ 3

Вторая строка преобразуется в следующий код:

s = (new StringBuilder()).append((String)null).append("hello").toString();

Прикладные методы могут обрабатывать аргументы null.

Ответ 4

Вы не используете "null", и поэтому вы не получите исключения. Если вы хотите NullPointer, просто сделайте

String s = null;
s = s.toString() + "hello";

И я думаю, что вы хотите сделать:

String s = "";
s = s + "hello";

Ответ 5

Это поведение, указанное в методе Java API String.valueOf(Object). Когда вы выполняете конкатенацию, valueOf используется для получения представления String. Существует специальный случай, если Object null, и в этом случае используется строка "null".

public static String valueOf(Object obj)

Возвращает строковое представление аргумента Object.

Параметры:     obj - объект.

Возврат:

если аргумент равен null, тогда строка равна "null"; в противном случае возвращается значение obj.toString().