Почему работает следующее? Я ожидаю, что будет выброшено NullPointerException
.
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
Почему работает следующее? Я ожидаю, что будет выброшено NullPointerException
.
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
JLS 5, раздел 15.18. .1.1 JLS 8 § 15.18.1 "Оператор конкатенации строк +", ведущий к JLS 8, § 5.1.11 "Преобразование строк" требует, чтобы эта операция выполнялась без ошибок:
...Теперь необходимо учитывать только эталонные значения. Если ссылка пустая, она преобразуется в строку "null" (четыре символа ASCII n, u, l, l). В противном случае преобразование выполняется, как если бы вызывался метод toString объекта, на который делается ссылка, без аргументов; но если результатом вызова метода toString является null, вместо него используется строка "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.
StringBuilder
технически не использует String.valueOf
, если вызывается какой-либо метод append
, отличный от append(Object)
(например, append(String)
или append(CharSequence)
), хотя null
в любом случае заменяется на "null"
.
- person ColinD; 23.11.2010
StringBuilder(Object)
. Я отредактировал свой пост, чтобы напрямую отразить байт-код.
- person Mark Peters; 24.11.2010
hello
.
- person aliopi; 08.10.2015
null
должен вызвать NPE. Я думаю, что печать null
— это что-то вроде мета-поведения, например, при отладке, когда я хочу увидеть, что находится внутри этой переменной, но по умолчанию немета поведение должно быть метание НПЭ.
- person aliopi; 21.07.2017
logger.print("doing something to " + obj);
). В настоящее время у нас есть лучшая композиция журнала, чем конкатенация строк, но это не обязательно верно в 1995 году.
- person Mark Peters; 21.07.2017
См. раздел 5.4 и 15.18 спецификации языка Java:
Преобразование строки применяется только к операндам бинарного оператора +, когда один из аргументов является строкой. В этом единственном особом случае другой аргумент + преобразуется в строку, а новая строка, представляющая собой конкатенацию двух строк, является результатом +. Преобразование строк подробно описано в описании оператора конкатенации строк +.
и
Если только одно выражение операнда имеет тип String, то преобразование строки выполняется для другого операнда, чтобы создать строку во время выполнения. Результатом является ссылка на объект String (вновь созданный, если только выражение не является выражением-константой времени компиляции (§15.28)), представляющим собой конкатенацию двух строк операндов. Символы левого операнда предшествуют символам правого операнда во вновь созданной строке. Если операнд типа String имеет значение null, то вместо этого операнда используется строка null.
Вторая строка преобразуется в следующий код:
s = (new StringBuilder()).append((String)null).append("hello").toString();
Методы добавления могут обрабатывать null
аргументов.
Вы не используете "null" и поэтому не получаете исключения. Если вам нужен NullPointer, просто сделайте
String s = null;
s = s.toString() + "hello";
И я думаю, что вы хотите сделать это:
String s = "";
s = s + "hello";
Это поведение указано в String.valueOf(Object)
метод. Когда вы выполняете конкатенацию, valueOf
используется для получения представления String
. Существует особый случай, когда Объект равен null
, и в этом случае используется строка "null"
.
public static String valueOf(Object obj)
Возвращает строковое представление аргумента Object.
Параметры: obj - Объект.
Возвращает:
если аргумент нулевой, то строка равна "null"; в противном случае возвращается значение obj.toString().
+ operator
перегружен для типа String (например, см. Ответ Джонатана). В строкеs + "hello"
нет вызовов методов и, следовательно, нет шансов для NPE, поскольку нет получателя объекта (и «преобразования кода» должны соблюдать этот контракт). Удачного кодирования. - person   schedule 24.11.2010nullhello
по-прежнему остается нелогичным и бесполезным поведением IMO. - person aliopi   schedule 08.10.2015+
применительно кString
значениям. В конце концов, он не пытается вызвать метод для любого из операндов, если только он не имеет другого типа, когда должен быть вызван неявныйtoString
. Очевидно, что целью оператора+
является упрощение отображения значения, а не ссылка на длину результата и т. д. - person tishma   schedule 18.06.2021