Нет. Это последовательность UTF-16 кодовых единиц и длины. Строки Java и C # могут содержать встроенные NUL.
Каждая кодовая единица UTF-16 занимает два байта, поэтому вы можете думать о строке "\n\0\n"
как:
{
length: 3, // 3 pairs of bytes == 3 UTF-16 code units
bytes: [0, 10, // \n
0, 0, // \0
0, 10] // \n
}
Обратите внимание, что последний байт в bytes
не равен 0. Поле length
сообщает, сколько байтов используется. Это позволяет substring
быть очень эффективным - повторно использовать тот же байтовый массив, но с другой длиной (и смещением, если ваша реализация виртуальной машины не может указывать на массив).
UTF-16 (16-битный формат преобразования Unicode) - это кодировка символов для Unicode, способная кодировать 1 112 064 числа (называемых кодовыми точками) в кодовом пространстве Unicode от 0 до 0x10FFFF. Он выдает результат переменной длины, состоящий из одной или двух 16-битных кодовых единиц на кодовую точку.
С Javadoc
String представляет строку в формате UTF-16, в которой дополнительные символы представлены суррогатными парами (см. Раздел Представления символов Unicode в классе Character для получения дополнительной информации). Значения индекса относятся к единицам кодов символов, поэтому дополнительный символ использует две позиции в строке.
C # System.String
определяется аналогично
Каждый символ Unicode в строке определяется скалярным значением Unicode, также называемым кодовой точкой Unicode или порядковым (числовым) значением символа Unicode. Каждая кодовая точка кодируется с использованием кодировки UTF-16, а числовое значение каждого элемента кодирования представляется символом. Результирующая коллекция объектов Char составляет String.
Я не уверен, что C # защищает от осиротевших суррогатов, но приведенный выше текст, кажется, смешивает термины «скалярное значение» и «кодовая точка», что сбивает с толку. скалярное значение определяется таким образом: unicode.org
:
Любая кодовая точка Unicode, за исключением кодов с высоким и низким суррогатом
Java определенно использует представление кодовой точки и не пытается защититься от недопустимых скалярных значений в строках.
«Неизменность и постоянство строк» объясняет преимущества эффективности этого представления.
Одно из преимуществ неизменяемых типов данных, о которых я говорил здесь ранее, заключается в том, что они не только неизменяемы, но и «постоянны». Под «постоянным» я подразумеваю неизменный тип данных, так что общие операции с этим типом (например, добавление нового элемента в очередь или удаление элемента из дерева) могут повторно использовать большую часть или всю память существующих данных. состав. Поскольку все они неизменны, вы можете повторно использовать их части, не беспокоясь о том, что они могут измениться на вас.
EDIT:
Вышесказанное верно концептуально и на практике, но виртуальные машины и CLR могут свободно делать вещи по-разному в определенных ситуациях.
Спецификация языка Java требует, чтобы строки располагались определенным образом в .class
файлах, а его тип JNI jstring
абстрагируется в детали представления памяти, так что теоретически виртуальная машина может представлять строку в памяти в виде строки UTF-8 с NUL-концом и двухбайтовой формой, используемой для встроенных символов NUL вместо представления int32 length
и uint16[] bytes
, которое позволяет для эффективного произвольного доступа к кодовым единицам.
Виртуальные машины на практике этого не делают. «Самая дорогая однобайтовая ошибка» утверждает, что NUL-завершенные строки были огромной ошибкой в C, поэтому я сомневаюсь, что виртуальные машины примут их внутренне из соображений эффективности.
Лучший кандидат, который мне удалось найти, - это использование в C / Unix / Posix текстовых строк с NUL-завершением. Выбор был действительно прост: должен ли язык C представлять строки в виде кортежа адрес + длина или просто как адрес с магическим символом (NUL), отмечающим конец?
...
Немного подумав о системах виртуальной памяти, мы решили этот вопрос. Оптимизация перемещения строки байтов известной длины может использовать всю ширину шин памяти и строк кэша, не затрагивая область памяти, которая не является частью строки источника или назначения.
Одним из примеров является libc FreeBSD, где реализация bcopy (3) / memcpy (3) будет перемещать как можно больше данных в кусках «unsigned long», обычно 32 или 64 бита, а затем «очищать любые завершающие байты» "как описано в комментарии, с байтовыми операциями. 2
Если исходная строка завершена NUL, однако, попытка получить к ней доступ в единицах, превышающих байты, может привести к попытке чтения символов после NUL. Если символ NUL является последним байтом страницы [виртуальной памяти], а следующая страница [виртуальной памяти] не определена, это может привести к смерти процесса из-за необоснованной ошибки «страница отсутствует».