Spec-Zone.ru › Java Language Specification 21

Глава 3. Лексическая структура

Содержание

3.1. Юникод
3.2. Лексические преобразования
3.3. Экранирование Юникода
3.4. Разделители строк
3.5. Элементы входных данных и токены
3.6. Пробелы
3.7. Комментарии
3.8. Идентификаторы
3.9. Ключевые слова
3.10. Литералы
3.10.1. Целочисленные литералы
3.10.2. Литералы с плавающей точкой
3.10.3. Булевы литералы
3.10.4. Символьные литералы
3.10.5. Строковые литералы
3.10.6. Текстовые блоки
3.10.7. Последовательности экранирования
3.10.8. Нулевой литерал
3.11. Разделители
3.12. Операторы

В данной главе описывается лексическая структура языка программирования Java.

Программы записываются в Юникоде (§3.1), но предоставляются лексические преобразования (§3.2), чтобы можно было включать любые символы Юникода, используя только символы ASCII с помощью экранирования Юникода (§3.3). Определены разделители строк (§3.4), чтобы поддерживать различные соглашения существующих систем, сохраняя при этом последовательные номера строк.

Символы Юникода, полученные в результате лексических преобразований, сводятся к последовательности элементов входных данных (§3.5), являющихся пробелами (§3.6), комментариями (§3.7) и токенами. Токены — это идентификаторы (§3.8), ключевые слова (§3.9), литералы (§3.10), разделители (§3.11) и операторы (§3.12) синтаксической грамматики.

3.1. Юникод

Программы пишутся с использованием набора символов Юникод (§1.7). Информация об этом наборе символов и его связанных кодировках может быть найдена на сайте https://www.unicode.org/.

Java SE Platform отслеживает Стандарт Юникод по мере его развития. Точная версия Юникода, используемая конкретным выпуском, указана в документации класса Character.

Стандарт Юникод изначально разрабатывался как кодировка символов с фиксированной шириной 16 бит. Позднее он был изменен, чтобы позволить символы, чье представление требует более 16 бит. Диапазон допустимых кодовых точек теперь U+0000 до U+10FFFF, используя обозначение шестнадцатеричного числа U+n. Символы, чьи кодовые точки больше U+FFFF, называются дополнительными символами. Для представления всего диапазона символов с использованием только 16-битовых единиц Стандарт Юникод определяет кодировку UTF-16. В этой кодировке дополнительные символы представлены парами 16-битовых кодовых единиц, первая из которых из диапазона высоких суррогатов (U+D800 до U+DBFF), а вторая из диапазона низких суррогатов (U+DC00 до U+DFFF). Для символов в диапазоне U+0000 до U+FFFF значения кодовых точек и кодовых единиц UTF-16 совпадают.

Язык программирования Java представляет текст в последовательностях 16-битовых кодовых единиц, используя кодировку UTF-16.

Некоторые API платформы Java SE, в основном в классе Character, используют 32-битовые целые числа для представления кодовых точек как отдельных сущностей. Платформа Java SE предоставляет методы для преобразования между 16-битными и 32-битными представлениями.

В данном спецификации используются термины кодовая точка и кодовая единица UTF-16, где представление важно, и общий термин символ, где представление не имеет отношения к обсуждению.

За исключением комментариев (§3.7), идентификаторов (§3.8) и содержимого символьных литералов, строковых литералов и текстовых блоков (§3.10.4, §3.10.5, §3.10.6), все элементы входных данных (§3.5) в программе формируются только из символов ASCII (или экранированных символов Юникода (§3.3), которые приводят к символам ASCII).

ASCII (ANSI X3.4) — это американский стандартный код для обмена информацией. Первые 128 символов кодировки Unicode UTF-16 — это символы ASCII.

3.2. Лексические преобразования

Поток символов Юникода преобразуется в последовательность токенов с помощью следующих трех шагов лексического преобразования, которые применяются поочередно:

  1. Преобразование экранированных символов Юникода (§3.3) в исходном потоке символов Юникода в соответствующий символ Юникода. Экранирование Юникода вида \uxxxx, где xxxx — шестнадцатеричное значение, представляет кодовую единицу UTF-16, чье кодирование — xxxx. Этот шаг преобразования позволяет выразить любую программу, используя только символы ASCII.

  2. Преобразование потока Юникода, полученного на шаге 1, в поток входных символов и разделителей строк (§3.4).

  3. Преобразование потока входных символов и разделителей строк, полученного на шаге 2, в последовательность элементов входных данных (§3.5), которые после удаления пробелов (§3.6) и комментариев (§3.7), составляют токены, которые являются терминальными символами синтаксической грамматики (§2.3).

На каждом шаге используется самое длинное возможное преобразование, даже если результат в конечном итоге не образует правильную программу, в то время как другое лексическое преобразование было бы правильным. Есть два исключения для учета ситуаций, которые требуют более подробного преобразования: на шаге 1 для обработки непрерывных \ символов (§3.3) и на шаге 3 для обработки контекстных ключевых слов и смежных > символов (§3.5).

Входные символы a--b токенизируются как a, -- и b, что не является частью какой-либо грамматически корректной программы, хотя токенизация a, -, -, b может быть частью грамматически корректной программы. Токенизация a, -, -, b может быть реализована с входными символами a- -b (с символом ASCII SP между двумя - символами).

Можно предположить, что исходный ввод \\u1234 преобразуется в символ \ и (следуя правилу «наибольшей длины») в экранирование Юникода вида \u1234. На самом деле, ведущий символ \ заставляет этот исходный ввод преобразовать в семь отдельных символов: \ \ u 1 2 3 4.

3.3. Экранирование Юникода

Компилятор языка программирования Java ("компилятор Java") сначала распознаёт экранирования Юникода в своём исходном коде, преобразуя ASCII-символы \u, за которыми следуют четыре шестнадцатеричных цифры, в символ исходного кода, обозначающий единицу кодировки UTF-16 (§3.1) для указанного шестнадцатеричного значения. Одно экранирование Юникода может представлять символы в диапазоне U+0000 до U+FFFF; для представления дополнительных символов в диапазоне U+010000 до U+10FFFF требуется два последовательных экранирования Юникода. Все другие символы в исходном коде компилятора распознаются как символы исходного кода и передаются без изменений.

Этот этап преобразования приводит к последовательности символов ввода Юникода, все из которых являются символами исходного кода (любые экранирования Юникода были преобразованы в символы исходного кода).

СимволВводаЮникода:
ЭкранированиеЮникода
СимволИсходногоКода
ЭкранированиеЮникода:
\ МаркерЮникода ШестнадцатеричнаяЦифра ШестнадцатеричнаяЦифра ШестнадцатеричнаяЦифра ШестнадцатеричнаяЦифра
МаркерЮникода:
u {u}
ШестнадцатеричнаяЦифра:
(одна из)
0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
СимволИсходногоКода:
любой символ Юникода

\, u и шестнадцатеричные цифры здесь — все ASCII-символы.

Производство СимволВводаЮникода неоднозначно, потому что ASCII-символ \ в исходном коде компилятора может быть преобразован либо в СимволИсходногоКода, либо в \ ЭкранированияЮникода (за которым следует ASCII-символ u). Чтобы избежать неоднозначности, при обработке каждого ASCII-символа \ в исходном коде компилятора необходимо учитывать последние символы исходного кода, полученные на данном этапе преобразования:

  • Если последний символ исходного кода в результате был сам преобразован из экранирования Юникода в исходном коде компилятора, то ASCII-символ \ может начинать экранирование Юникода.

    Например, если последний символ исходного кода в результате был обратной косой чертой, возникшей из экранирования Юникода \u005c в исходном коде, то следующий ASCII-символ \ в исходном коде может начинать ещё одно экранирование Юникода.

  • В противном случае, посчитайте, сколько обратных косых черт подряд появились как символы исходного кода в результате, начиная от символа, отличного от обратной косой черты или от начала результата. (Не имеет значения, возникла ли такая обратная косая черта из ASCII-символа \ в исходном коде компилятора или из экранирования Юникода \u005c в исходном коде компилятора). Если это число чётное, то ASCII-символ \ может начинать экранирование Юникода; если нечётное, то ASCII-символ \ не может начинать экранирование Юникода.

    Например, исходный код "\\u2122=\u2122" приводит к одиннадцати символам " \ \ u 2 1 2 2 = ™ ", так как, хотя второй ASCII-символ \ в исходном коде не может начинать экранирование Юникода, третий ASCII-символ \ может, а \u2122 является кодировкой Юникода для символа ™.

Если допустимый \ не следует за u, то он обрабатывается как СимволИсходногоКода и остаётся частью потока экранированного Юникода.

Если допустимый \ следует за u, или если за ним следует более одной u, и последняя u не следует за четырьмя шестнадцатеричными цифрами, то возникает ошибка времени компиляции.

Символ, полученный с помощью экранирования Юникода, не участвует в дальнейших экранированиях Юникода.

Например, исходный код \u005cu005a приводит к шести символам \ u 0 0 5 a, потому что 005c — это значение Юникода для обратной косой черты. Он не приводит к символу Z, который имеет значение Юникода 005a, потому что обратная косая черта, полученная в результате обработки экранирования Юникода \u005c, не интерпретируется как начало дальнейшего экранирования Юникода.

Обратите внимание, что \u005cu005a не может быть записан в строковой литерале для обозначения шести символов \ u 0 0 5 a. Это связано с тем, что первые два символа, полученные в результате преобразования, \ и u, интерпретируются в строковой литерале как недопустимая последовательность экранирования (§3.10.7).

К счастью, правило о последовательных символах обратной косой черты помогает программистам создавать исходные коды, обозначающие экранирования Юникода в строковой литерале. Для обозначения шести символов \ u 0 0 5 a в строковой литерале просто необходимо добавить ещё \ рядом с существующим \, например, "\\u005a is Z". Это работает, потому что вторая \ в исходном коде \\u005a не может начинать экранирование Юникода, поэтому первая \ и вторая \ сохраняются как символы исходного кода, как и следующие пять символов u 0 0 5 a. Две \ интерпретируются в строковой литерале как последовательность экранирования для обратной косой черты, что приводит к строке с желаемыми шестью символами \ u 0 0 5 a. Без правила исходный код \\u005a обрабатывался бы как символ исходного кода \, за которым следует экранирование Юникода \u005a, которое становится символом исходного кода Z; это было бы бесполезно, потому что \Z является недопустимой последовательностью экранирования в строковой литерале. (Обратите внимание, что правило преобразует \u005c\u005c в \\, потому что преобразование первого экранирования Юникода в символ исходного кода \ не препятствует преобразованию второго экранирования Юникода в другой символ исходного кода \.)

Это правило также позволяет программистам создавать исходные коды, обозначающие последовательности экранирования в строковой литерале. Например, исходный код \\\u006e приводит к трём символам \ \ n, потому что первая \ и вторая \ сохраняются как символы исходного кода, а третья \ может начинать экранирование Юникода, и поэтому \u006e преобразуется в символ исходного кода n. Три символа \ \ n интерпретируются в строковой литерале как \ n, что обозначает последовательность экранирования для перевода строки. (Обратите внимание, что \\\u006e может быть записано как \u005c\u005c\u006e, потому что каждое экранирование Юникода \u005c преобразуется в символ исходного кода \, поэтому оставшийся исходный код \u006e предшествует чётному числу обратных косых черт и обрабатывается как экранирование Юникода для n.)

Язык программирования Java определяет стандартный способ преобразования программы, написанной на языке Юникод, в ASCII, который преобразует программу в форму, которую могут обработать инструменты, основанные на ASCII. Преобразование включает в себя преобразование всех экранирований Юникода в исходном тексте программы в ASCII путём добавления дополнительной u — например, \uxxxx становится \uuxxxx — и одновременного преобразования символов в исходном тексте, не являющихся ASCII, в экранирования Юникода, содержащие по одному u.

Эта преобразованная версия так же приемлема для компилятора Java и представляет точно такую же программу. Точный исходный текст Юникода может быть восстановлен из этой ASCII-формы путём преобразования каждой последовательности экранирования, где присутствует несколько u, в последовательность символов Юникода с одной u меньше, и одновременного преобразования каждой последовательности экранирования с одной u в соответствующий единственный символ Юникода.

Компилятор Java должен использовать обозначение \uxxxx в качестве формата вывода для отображения символов Юникода, когда подходящий шрифт недоступен.

3.4. Разделители строк

Компилятор Java затем разделяет последовательность символов ввода Юникод на строки, распознавая разделители строк.

РазделительСтроки:
символ ASCII LF, также известный как "новая строка"
символ ASCII CR, также известный как "возврат"
символ ASCII CR, за которым следует символ ASCII LF
СимволВвода:
UnicodeInputCharacter но не CR или LF

Строки завершаются символами ASCII CR, или LF, или CR LF. Два символа CR, непосредственно за которыми следует LF, считаются одним разделителем строк, а не двумя.

Разделитель строк указывает на завершение формы комментария // (§3.7).

Строки, определяемые разделителями строк, могут определять номера строк, создаваемые компилятором Java.

Результат — последовательность разделителей строк и символов ввода, которые являются терминальными символами для третьего шага процесса токенизации.

3.5. Элементы ввода и токены

Символы ввода и разделители строк, которые получаются после обработки символов Юникод (§3.3) и затем распознавания строк (§3.4), сводятся к последовательности элементов ввода.

Вход:
{ЭлементВвода} [Под]
ЭлементВвода:
Пробелы
Комментарий
Токен
Токен:
Идентификатор
КлючевоеСлово
Литерал
Разделитель
Оператор
Под:
символ ASCII SUB, также известный как "управляющий символ Z"

Те элементы ввода, которые не являются пробелами или комментариями, являются токенами. Токены — терминальные символы синтаксической грамматики (§2.3).

Пробелы (§3.6) и комментарии (§3.7) могут служить для разделения токенов, которые, если будут рядом, могли бы быть токенизированы другим способом.

Например, символы ввода - и = могут образовывать операторный токен -= (§3.12) только в том случае, если между ними нет пробелов или комментариев. В качестве другого примера, десять символов ввода staticvoid образуют один токен идентификатора, в то время как одиннадцать символов ввода static void (с символом ASCII SP между c и v) образуют пару токенов ключевых слов, static и void, разделенных пробелами.

В качестве особой уступки для совместимости с некоторыми операционными системами символ ASCII SUB (\u001a, или управляющий символ Z) игнорируется, если он является последним символом в потоке экранированного ввода.

Производство Вход является неоднозначным, что означает, что для некоторых последовательностей символов ввода существует более одного способа уменьшения символов ввода до элементов ввода (то есть, токенизировать символы ввода). Неопределенности разрешаются следующим образом:

  • Последовательность символов ввода, которые могут быть сведены либо к токену идентификатора, либо к токену литерала, всегда сводится к токену литерала.

  • Последовательность символов ввода, которые могут быть сведены либо к токену идентификатора, либо к токену зарезервированного ключевого слова (§3.9), всегда сводится к токену зарезервированного ключевого слова.

  • Последовательность символов ввода, которые могут быть сведены либо к контекстному ключевому слову, либо к другим (не ключевым) токенам, уменьшается в соответствии с контекстом, как указано в §3.9.

  • Если символ ввода > появляется в контексте типа (§4.11), то есть как часть Типа или UnannType в синтаксической грамматике (§4.1, §8.3), то он всегда сводится к оператору численного сравнения >, даже когда он может быть объединён с соседним символом > для образования другого оператора.

    Без этого правила для символов >, два последовательных > скобки в типе, таком как List<List<String>>, были бы токенизированы как оператор сдвига вправо со знаком >>, в то время как три последовательных > скобки в типе, таком как List<List<List<String>>>, были бы токенизированы как оператор сдвига вправо без знака >>>. Хуже того, токенизация четырёх или более последовательных > скобок в типе, таком как List<List<List<List<String>>>>, была бы неоднозначной, так как различные комбинации токенов >, >> и >>> могли бы представлять символы >>>>.

Рассмотрим два токена x и y в результирующем потоке ввода. Если x предшествует y, то мы говорим, что x находится слева от y и что y находится справа от x.

Например, в этом простом фрагменте кода:

class Empty {
}

мы говорим, что токен } находится справа от токена {, даже если он появляется в этом двумерном представлении вниз и влево от токена {. Эта конвенция относительно использования слов слева и справа позволяет нам говорить, например, об правом операнде бинарного оператора или о левой части присваивания.

3.6. Пробелы

Пробелы определяются как символ ASCII пробела, символ горизонтальной табуляции, символ подачи страницы и символы разделителей строк (§3.4).

Пробелы:
символ ASCII SP, также известный как "пробел"
символ ASCII HT, также известный как "горизонтальная табуляция"
символ ASCII FF, также известный как "подача страницы"
РазделительСтроки

3.7. Комментарии

Существуют два вида комментариев:

  • /* текст */

    Традиционный комментарий: весь текст от ASCII-символов /* до ASCII-символов */ игнорируется (как в C и C++).

  • // текст

    Комментарий в конце строки: весь текст от ASCII-символов // до конца строки игнорируется (как в C++).

Comment:
TraditionalComment
EndOfLineComment
TraditionalComment:
/ * CommentTail
CommentTail:
* CommentTailStar
NotStar CommentTail
CommentTailStar:
/
* CommentTailStar
NotStarNotSlash CommentTail
NotStar:
InputCharacter но не *
LineTerminator
NotStarNotSlash:
InputCharacter но не * или /
LineTerminator
EndOfLineComment:
/ / {InputCharacter}

Эти правила подразумевают все следующие свойства:

  • Комментарии не вложены.

  • /* и */ не имеют специального значения в комментариях, начинающихся с //.

  • // не имеет специального значения в комментариях, начинающихся с /* или /**.

В результате, следующий текст является одним полным комментарием:

/* this comment /* // /** ends here: */

Лексическая грамматика подразумевает, что комментарии не встречаются внутри символьных литералов, строковых литералов или текстовых блоков (§3.10.4, §3.10.5, §3.10.6).

3.8. Идентификаторы

Идентификатор — это последовательность неограниченной длины символов-букв Java и цифр Java, первая из которых должна быть символом-буквой Java.

Identifier:
IdentifierChars но не ReservedKeyword или BooleanLiteral или NullLiteral
IdentifierChars:
JavaLetter {JavaLetterOrDigit}
JavaLetter:
любой символ Unicode, являющийся "символом-буквой Java"
JavaLetterOrDigit:
любой символ Unicode, являющийся "символом-буквой-или-цифрой Java"

"Символ-буква Java" — это символ, для которого метод Character.isJavaIdentifierStart(int) возвращает true.

"Символ-буква-или-цифра Java" — это символ, для которого метод Character.isJavaIdentifierPart(int) возвращает true.

"Символы-буквы Java" включают прописные и строчные латинские буквы ASCII A-Z (\u0041-\u005a), а также a-z (\u0061-\u007a), и, по историческим причинам, знак доллара ASCII ($, или \u0024) и символ подчеркивания (_, или \u005f). Знак доллара следует использовать только в генерируемом автоматически исходном коде или, в редких случаях, для доступа к именам, существующим в legacy-системах. Символ подчеркивания может использоваться в идентификаторах, состоящих из двух или более символов, но не может использоваться как односимвольный идентификатор из-за того, что является ключевым словом.

"Цифры Java" включают цифры ASCII 0-9 (\u0030-\u0039).

Буквы и цифры могут быть взяты из всего набора символов Unicode, который поддерживает большинство письменных систем, используемых сегодня в мире, включая большие наборы для китайского, японского и корейского языков. Это позволяет программистам использовать в своих программах идентификаторы, написанные на родных языках.

Два идентификатора считаются одинаковыми только если, после игнорирования игнорируемых символов, у идентификаторов совпадают символы Unicode для каждой буквы или цифры. Игнорируемый символ — это символ, для которого метод Character.isIdentifierIgnorable(int) возвращает true. Идентификаторы, имеющие одинаковый внешний вид, могут быть разными.

Например, идентификаторы, состоящие из одиночных букв LATIN CAPITAL LETTER A (A, \u0041), LATIN SMALL LETTER A (a, \u0061), GREEK CAPITAL LETTER ALPHA (A, \u0391), CYRILLIC SMALL LETTER A (a, \u0430) и MATHEMATICAL BOLD ITALIC SMALL A (a, \ud835\udc82) все разные.

Композиционные символы Unicode отличаются от их канонических эквивалентов, представленных в разложенном виде. Например, LATIN CAPITAL LETTER A ACUTE (Á, \u00c1) отличается от LATIN CAPITAL LETTER A (A, \u0041), сразу за которым следует NON-SPACING ACUTE (´, \u0301) в идентификаторах. См. Стандарт Unicode, раздел 3.11 "Формы нормализации".

Примеры идентификаторов:

  • String

  • i3

  • αρετη

  • MAX_VALUE

  • isLetterOrDigit

Идентификатор никогда не имеет такой же написания (последовательности символов Unicode), как зарезервированное ключевое слово (§3.9), булево значение (§3.10.3) или null-литерал (§3.10.8), из-за правил токенизации (§3.5). Однако, идентификатор может иметь такое же написания, как контекстное ключевое слово, поскольку токенизация последовательности символов ввода как идентификатора или контекстного ключевого слова зависит от того, где последовательность появляется в программе.

Для облегчения распознавания контекстных ключевых слов, синтаксическая грамматика (§2.3) иногда запрещает определённые идентификаторы, определяя производство, принимающее только подмножество идентификаторов. Подмножества следующие:

TypeIdentifier:
Identifier но не permits, record, sealed, var, или yield
UnqualifiedMethodIdentifier:
Identifier но не yield

TypeIdentifier используется при объявлении классов, интерфейсов и параметров типов (§8.1, §9.1, §4.4), и при ссылке на типы (§6.5). Например, имя класса должно быть TypeIdentifier, поэтому запрещено объявлять класс с именем permits, record, sealed, var или yield.

UnqualifiedMethodIdentifier используется, когда выражение вызова метода ссылается на метод по его простому имени (§6.5.7.1). Поскольку термин yield исключён из UnqualifiedMethodIdentifier, любой вызов метода с именем yield должен быть квалифицированным, что отличает вызов от оператора yield (§14.21).

3.9. Ключевые слова

51 последовательность символов, сформированных из ASCII-символов, зарезервированы для использования в качестве ключевых слов и не могут использоваться в качестве идентификаторов (§3.8). Ещё 17 последовательностей символов, также сформированных из ASCII-символов, могут интерпретироваться как ключевые слова или как другие токены в зависимости от контекста, в котором они появляются.

Ключевое слово:
Зарезервированное ключевое слово
Контекстуальное ключевое слово
Зарезервированное ключевое слово:
(одно из)
abstract   continue   for          new         switch
assert     default    if           package     synchronized
boolean    do         goto         private     this
break      double     implements   protected   throw
byte       else       import       public      throws
case       enum       instanceof   return      transient
catch      extends    int          short       try
char       final      interface    static      void
class      finally    long         strictfp    volatile
const      float      native       super       while
_ (underscore)
Контекстуальное ключевое слово:
(одно из)
exports      opens      requires     uses   yield
module       permits    sealed       var         
non-sealed   provides   to           when        
open         record     transitive   with        

Ключевые слова const и goto зарезервированы, даже если они в настоящее время не используются. Это может позволить компилятору Java генерировать более информативные сообщения об ошибках, если эти ключевые слова C++ неправильно используются в программах.

Ключевое слово strictfp устарело и не должно использоваться в новом коде.

Ключевое слово _ (подчёркивание) зарезервировано для возможного будущего использования в объявлениях параметров.

true и false не являются ключевыми словами, а являются булевыми литералами (§3.10.3).

null не является ключевым словом, а является литералом null (§3.10.8).

Во время сокращения входных символов до входных элементов (§3.5), последовательность входных символов, которая по смыслу соответствует контекстуальному ключевому слову, сокращается до контекстуального ключевого слова только в том случае, если выполняются оба следующих условия:

  1. Последовательность распознаётся как терминал, указанный в соответствующем контексте синтаксической грамматики (§2.3), как следует:

    • Для module и open, когда они распознаются как терминалы в ModuleDeclaration (§7.7).

    • Для exports, opens, provides, requires, to, uses и with, когда они распознаются как терминалы в ModuleDirective.

    • Для transitive, когда он распознаётся как терминал в RequiresModifier.

      Например, распознавание последовательности requires transitive ; не использует RequiresModifier, поэтому термин transitive сокращается здесь до идентификатора, а не до контекстуального ключевого слова.

    • Для var, когда он распознаётся как терминал в LocalVariableType (§14.4) или LambdaParameterType (§15.27.1).

      В других контекстах попытка использовать var как идентификатор приведёт к ошибке, потому что var не является TypeIdentifier (§3.8).

    • Для yield, когда он распознаётся как терминал в YieldStatement (§14.21).

      В других контекстах попытка использовать yield как идентификатор приведёт к ошибке, потому что yield не является ни TypeIdentifier, ни UnqualifiedMethodIdentifier.

    • Для record, когда он распознаётся как терминал в RecordDeclaration (§8.10).

    • Для non-sealed, permits и sealed, когда они распознаются как терминалы в NormalClassDeclaration (§8.1) или NormalInterfaceDeclaration (§9.1).

    • Для when, когда он распознаётся как терминал в Guard (§14.11.1).

  2. Последовательность не предшествует и не следует непосредственно входному символу, который соответствует JavaLetterOrDigit.

В общем случае случайное опускание пробелов в исходном коде приведёт к тому, что последовательность входных символов будет разложена на лексемы как идентификатор из-за правила «наибольшего возможного соответствия» (§3.2). Например, последовательность двенадцати входных символов p u b l i c s t a t i c всегда разлагается на лексемы как идентификатор publicstatic, а не как зарезервированные ключевые слова public и static. Если предполагается два токена, они должны быть разделены пробелами или комментарием.

Приведённое выше правило работает вместе с правилом «наибольшего возможного соответствия», чтобы получить интуитивный результат в контекстах, где могут появиться контекстуальные ключевые слова. Например, последовательность одиннадцати входных символов v a r f i l e n a m e обычно разлагается на лексемы как идентификатор varfilename, но в объявлении локальной переменной первые три входных символа предварительно распознаются как контекстуальное ключевое слово var в соответствии с первым условием вышеприведённого правила. Однако было бы неудобно игнорировать отсутствие пробелов в последовательности, распознавая следующие восемь входных символов как идентификатор filename. (Это означало бы, что последовательность подвергается различной разборке лексем в разных контекстах: идентификатор в большинстве контекстов, но контекстуальное ключевое слово и идентификатор в объявлениях локальных переменных.) Соответственно, второе условие предотвращает распознавание контекстуального ключевого слова var, поскольку следующий за ним входной символ f является JavaLetterOrDigit. Таким образом, последовательность v a r f i l e n a m e разлагается на лексемы как идентификатор varfilename в объявлении локальной переменной.

Ещё один пример внимательного распознавания контекстуальных ключевых слов – последовательность из 15 входных символов n o n - s e a l e d c l a s s. Эта последовательность обычно преобразуется в три токена — идентификатор non, оператор - и идентификатор sealedclass — но в объявлении обычного класса, где выполняется первое условие, первые десять входных символов предварительно распознаются как контекстуальное ключевое слово non-sealed. Чтобы избежать преобразования последовательности в два ключевых токена (non-sealed и class) вместо трёх не ключевых токенов и чтобы избежать награждения программиста за опускание пробелов перед class, второе условие предотвращает распознавание контекстуального ключевого слова. Поэтому последовательность n o n - s e a l e d c l a s s разлагается на три токена в объявлении класса.

В данном правиле первое условие зависит от деталей синтаксической грамматики, но компилятор языка программирования Java может реализовать это правило без полного разбора входной программы. Например, можно использовать эвристику для отслеживания контекстуального состояния разборщика лексем, если эвристика гарантирует, что допустимые использования контекстуальных ключевых слов разлагаются на лексемы как ключевые слова, а допустимые использования идентификаторов разлагаются на лексемы как идентификаторы. В качестве альтернативы компилятор может всегда разлагать контекстуальное ключевое слово на лексемы как идентификатор, оставив распознавание специальных случаев использования этих идентификаторов на более поздней фазе.

3.10. Литералы

A литерал — это представление значения примитивного типа (§4.2), типа String (§4.3.3) или нулевого типа (§4.1) в исходном коде.

Литерал:
ЦелочисленныйЛитерал
ВещественныйЛитерал
БулевыйЛитерал
СимвольныйЛитерал
СтроковыйЛитерал
БлокТекста
НулевойЛитерал

3.10.1. Целочисленные Литералы

Целочисленный литерал может быть представлен в десятичной (основание 10), шестнадцатеричной (основание 16), восьмеричной (основание 8) или двоичной (основание 2) системе счисления.

ЦелочисленныйЛитерал:
ДесятичныйЦелочисленныйЛитерал
ШестнадцатеричныйЦелочисленныйЛитерал
ВосьмеричныйЦелочисленныйЛитерал
ДвоичныйЦелочисленныйЛитерал
ДесятичныйЦелочисленныйЛитерал:
ДесятичнаяЦифра [СуффиксЦелогоТипа]
ШестнадцатеричныйЦелочисленныйЛитерал:
ШестнадцатеричнаяЦифра [СуффиксЦелогоТипа]
ВосьмеричныйЦелочисленныйЛитерал:
ВосьмеричнаяЦифра [СуффиксЦелогоТипа]
ДвоичныйЦелочисленныйЛитерал:
ДвоичнаяЦифра [СуффиксЦелогоТипа]
СуффиксЦелогоТипа:
(один из)
l L

Целочисленный литерал имеет тип long, если он имеет суффикс с ASCII буквой L или l (ell); в противном случае он имеет тип int (§4.2.1).

Суффикс L предпочтительнее, так как букву l (ell) часто трудно отличить от цифры 1 (один).

Разделители подчеркивания допускаются между цифрами, обозначающими целое число.

В шестнадцатеричном или двоичном литерале целое число обозначается только цифрами после символов 0x или 0b и перед любым суффиксом типа. Поэтому подчеркивания не могут появляться непосредственно после 0x или 0b, или после последней цифры в числе.

В десятичном или восьмеричном литерале целое число обозначается всеми цифрами в литерале перед любым суффиксом типа. Поэтому подчеркивания не могут появляться перед первой цифрой или после последней цифры в числе. Подчеркивания могут появляться после начальной 0 в восьмеричном числе (поскольку 0 — это цифра, обозначающая часть целого числа) и после начальной ненулевой цифры в ненулевом десятичном литерале.

Десятичная цифра — это либо одиночная ASCII цифра 0, представляющая ноль, или состоит из ASCII цифры от 1 до 9, за которой необязательно следуют одна или несколько ASCII цифр от 0 до 9, перемежающиеся подчеркиваниями, представляющими положительное целое число.

ДесятичнаяЦифра:
0
ПерваяЦифра [Цифры]
ПерваяЦифра Подчеркивания Цифры
ПерваяЦифра:
(одна из)
1 2 3 4 5 6 7 8 9
Цифры:
Цифра
Цифра [ЦифрыСПодчеркиваниями] Цифра
Цифра:
0
ПерваяЦифра
ЦифрыСПодчеркиваниями:
ЦифраИлиПодчеркивание {ЦифраИлиПодчеркивание}
ЦифраИлиПодчеркивание:
Цифра
_
Подчеркивания:
_ {_}

Шестнадцатеричная цифра состоит из ведущих ASCII символов 0x или 0X, за которыми следуют одна или несколько ASCII шестнадцатеричных цифр, перемежающиеся подчеркиваниями, и может представлять положительное, нулевое или отрицательное целое число.

Шестнадцатеричные цифры со значениями от 10 до 15 представлены ASCII буквами a до f или A до F соответственно; каждая буква, используемая в качестве шестнадцатеричной цифры, может быть заглавной или строчной.

ШестнадцатеричнаяЦифра:
0 x ШестнадцатеричныеЦифры
0 X ШестнадцатеричныеЦифры
ШестнадцатеричныеЦифры:
ШестнадцатеричнаяЦифра
ШестнадцатеричнаяЦифра [ШестнадцатеричныеЦифрыСПодчеркиваниями] ШестнадцатеричнаяЦифра
ШестнадцатеричнаяЦифра:
(одна из)
0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
ШестнадцатеричныеЦифрыСПодчеркиваниями:
ШестнадцатеричнаяЦифраИлиПодчеркивание {ШестнадцатеричнаяЦифраИлиПодчеркивание}
ШестнадцатеричнаяЦифраИлиПодчеркивание:
ШестнадцатеричнаяЦифра
_

Производство ШестнадцатеричнаяЦифра выше взято из §3.3.

Восьмеричная цифра состоит из ASCII цифры 0, за которой следует одна или несколько ASCII цифр от 0 до 7, перемежающиеся подчеркиваниями, и может представлять положительное, нулевое или отрицательное целое число.

ВосьмеричнаяЦифра:
0 ВосьмеричныеЦифры
0 Подчеркивания ВосьмеричныеЦифры
ВосьмеричныеЦифры:
ВосьмеричнаяЦифра
ВосьмеричнаяЦифра [ВосьмеричныеЦифрыСПодчеркиваниями] ВосьмеричнаяЦифра
ВосьмеричнаяЦифра:
(одна из)
0 1 2 3 4 5 6 7
ВосьмеричныеЦифрыСПодчеркиваниями:
ВосьмеричнаяЦифраИлиПодчеркивание {ВосьмеричнаяЦифраИлиПодчеркивание}
ВосьмеричнаяЦифраИлиПодчеркивание:
ВосьмеричнаяЦифра
_

Обратите внимание, что восьмеричные числа всегда состоят из двух или более цифр, так как 0 в одиночку всегда считается десятичным числом — не то, что это имеет большое значение на практике, так как числа 0, 00 и 0x0 представляют одно и то же целое значение.

Двоичная цифра состоит из ведущих ASCII символов 0b или 0B, за которыми следуют одна или несколько ASCII цифр 0 или 1, перемежающиеся подчеркиваниями, и может представлять положительное, нулевое или отрицательное целое число.

BinaryNumeral:
0 b BinaryDigits
0 B BinaryDigits
BinaryDigits:
BinaryDigit
BinaryDigit [BinaryDigitsAndUnderscores] BinaryDigit
BinaryDigit:
(один из)
0 1
BinaryDigitsAndUnderscores:
BinaryDigitOrUnderscore {BinaryDigitOrUnderscore}
BinaryDigitOrUnderscore:
BinaryDigit
_

Наибольшая десятичная литерал типа int равна 2147483648 (231).

Все десятичные литералы от 0 до 2147483647 могут встречаться в любом месте, где может стоять int литерал. Десятичная литерал 2147483648 может встречаться только как операнд унарного минуса - (§15.15.4).

Ошибка компиляции, если десятичная литерал 2147483648 встречается где-либо, кроме как операндом унарного минуса; или если десятичная литерал типа int больше, чем 2147483648 (231).

Наибольшие положительные шестнадцатеричные, восьмеричные и двоичные литералы типа int, каждый из которых представляет десятичное значение 2147483647 (231-1), соответственно:

  • 0x7fff_ffff,

  • 0177_7777_7777, и

  • 0b0111_1111_1111_1111_1111_1111_1111_1111

Наименьшие отрицательные шестнадцатеричные, восьмеричные и двоичные литералы типа int, каждый из которых представляет десятичное значение -2147483648 (-231), соответственно:

  • 0x8000_0000,

  • 0200_0000_0000, и

  • 0b1000_0000_0000_0000_0000_0000_0000_0000

Следующие шестнадцатеричные, восьмеричные и двоичные литералы представляют десятичное значение -1:

  • 0xffff_ffff,

  • 0377_7777_7777, и

  • 0b1111_1111_1111_1111_1111_1111_1111_1111

Ошибка компиляции, если шестнадцатеричный, восьмеричный или двоичный int литерал не помещается в 32 бита.

Наибольшая десятичная литерал типа long равна 9223372036854775808L (263).

Все десятичные литералы от 0L до 9223372036854775807L могут встречаться в любом месте, где может стоять long литерал. Десятичная литерал 9223372036854775808L может встречаться только как операнд унарного минуса - (§15.15.4).

Ошибка компиляции, если десятичная литерал 9223372036854775808L встречается где-либо, кроме как операндом унарного минуса; или если десятичная литерал типа long больше, чем 9223372036854775808L (263).

Наибольшие положительные шестнадцатеричные, восьмеричные и двоичные литералы типа long, каждый из которых представляет десятичное значение 9223372036854775807L (263-1), соответственно:

  • 0x7fff_ffff_ffff_ffffL,

  • 07_7777_7777_7777_7777_7777L, и

  • 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L

Наименьшие отрицательные шестнадцатеричные, восьмеричные и двоичные литералы типа long, каждый из которых представляет десятичное значение -9223372036854775808L (-263), соответственно:

  • 0x8000_0000_0000_0000L,

  • 010_0000_0000_0000_0000_0000L, и

  • 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L

Следующие шестнадцатеричные, восьмеричные и двоичные литералы представляют десятичное значение -1L:

  • 0xffff_ffff_ffff_ffffL,

  • 017_7777_7777_7777_7777_7777L, и

  • 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L

Ошибка компиляции, если шестнадцатеричный, восьмеричный или двоичный long литерал не помещается в 64 бита.

Примеры int литералов:

0    2    0372    0xDada_Cafe    1996    0x00_FF__00_FF

Примеры long литералов:

0l    0777L    0x100000000L    2_147_483_648L    0xC0B0L

3.10.2. Числовые литералы с плавающей точкой

Числовой литерал с плавающей точкой состоит из следующих частей: целая часть, десятичная или шестнадцатеричная точка (представленная символом ASCII точки), дробная часть, показатель степени и суффикс типа.

Числовой литерал с плавающей точкой может быть представлен в десятичной (основание 10) или шестнадцатеричной (основание 16) форме.

Для десятичных числовых литералов с плавающей точкой требуется хотя бы одна цифра (в целой или дробной части) и либо десятичная точка, либо показатель степени, либо суффикс типа float. Все остальные части являются необязательными. Показатель степени, если он присутствует, обозначается символами ASCII e или E, за которыми следует необязательно со знаком целое число.

Для шестнадцатеричных числовых литералов с плавающей точкой требуется хотя бы одна цифра (в целой или дробной части), показатель степени является обязательным, а суффикс типа float — необязательным. Показатель степени обозначается символами ASCII p или P, за которыми следует необязательно со знаком целое число.

Подчеркивания разрешены в качестве разделителей между цифрами, обозначающими целую часть, между цифрами, обозначающими дробную часть, и между цифрами, обозначающими показатель степени.

FloatingPointLiteral:
DecimalFloatingPointLiteral
HexadecimalFloatingPointLiteral
DecimalFloatingPointLiteral:
Digits . [Digits] [ExponentPart] [FloatTypeSuffix]
. Digits [ExponentPart] [FloatTypeSuffix]
Digits ExponentPart [FloatTypeSuffix]
Digits [ExponentPart] FloatTypeSuffix
ExponentPart:
ExponentIndicator SignedInteger
ExponentIndicator:
(один из)
e E
SignedInteger:
[Sign] Digits
Sign:
(один из)
+ -
FloatTypeSuffix:
(один из)
f F d D
HexadecimalFloatingPointLiteral:
HexSignificand BinaryExponent [FloatTypeSuffix]
HexSignificand:
HexNumeral [.]
0 x [HexDigits] . HexDigits
0 X [HexDigits] . HexDigits
BinaryExponent:
BinaryExponentIndicator SignedInteger
BinaryExponentIndicator:
(один из)
p P

Числовой литерал с плавающей точкой имеет тип float, если он имеет суффикс из символов ASCII F или f; в противном случае его тип равен double, и он может необязательно иметь суффикс из символов ASCII D или d.

Элементы типов float и double представляют собой те значения, которые могут быть представлены в формате чисел с плавающей точкой IEEE 754 binary32 и IEEE 754 binary64 соответственно (§4.2.3).

Подробное описание правильного преобразования входной строковой представления числа с плавающей точкой в внутреннее представление IEEE 754 с плавающей точкой приведено для методов valueOf класса Float и класса Double пакета java.lang.

Наибольшие и наименьшие положительные литералы типа float следующие:

  • Наибольшее положительное конечное значение float численно равно (2 - 2-23) ⋅ 2127.

    Наиболее короткий десятичный литерал, который округляется до этого значения, равен 3.4028235e38f.

    Шестнадцатеричный литерал для этого значения равен 0x1.fffffeP+127f.

  • Наименьшее положительное конечное ненулевое значение float численно равно 2-149.

    Наиболее короткий десятичный литерал, который округляется до этого значения, равен 1.4e-45f.

    Два шестнадцатеричных литерала для этого значения равны 0x0.000002P-126f и 0x0.0_0000_0000_0001P-1022.

Наибольшие и наименьшие положительные литералы типа double следующие:

  • Наибольшее положительное конечное значение double численно равно (2 - 2-52) ⋅ 21023.

    Наиболее короткий десятичный литерал, который округляется до этого значения, равен 1.7976931348623157e308.

    Шестнадцатеричный литерал для этого значения равен 0x1.f_ffff_ffff_ffffP+1023.

  • Наименьшее положительное конечное ненулевое значение double численно равно 2-1074.

    Наиболее короткий десятичный литерал, который округляется до этого значения, равен 4.9e-324.

    Два шестнадцатеричных литерала для этого значения равны 0x0.0_0000_0000_0001P-1022 и 0x1.0P-1074.

Если ненулевой числовой литерал с плавающей точкой слишком большой, так что при округленном преобразовании в его внутреннее представление он становится бесконечностью IEEE 754, это является ошибкой на этапе компиляции.

Программа может представлять бесконечности без возникновения ошибки на этапе компиляции, используя константные выражения, такие как 1f/0f или -1d/0d, или используя предопределенные константы POSITIVE_INFINITY и NEGATIVE_INFINITY классов Float и Double.

Если ненулевой числовой литерал с плавающей точкой слишком мал, так что при округленном преобразовании в его внутреннее представление он становится нулём, это является ошибкой на этапе компиляции.

Ошибка на этапе компиляции не возникает, если ненулевой числовой литерал с плавающей точкой имеет малое значение, которое при округленном преобразовании в его внутреннее представление становится ненулевым числом с пониженной точностью.

Предопределённые константы, представляющие значения «не число», определены в классах Float и Double как Float.NaN и Double.NaN.

Примеры числовых литералов с плавающей точкой:

1e1f    2.f    .3f    0f    3.14f    6.022137e+23f

Примеры числовых литералов с плавающей точкой:

1e1    2.    .3    0.0    3.14    1e-9d    1e137

3.10.3. Булевы литералы

Тип boolean имеет два значения, представленные булевыми литералами true и false, образованными из символов ASCII.

BooleanLiteral:
(один из)
true false

Булевый литерал всегда имеет тип boolean (§4.2.5).

3.10.4. Литералы символов

Литерал символа записывается как символ или последовательность управляющих символов (§3.10.7), заключенные в одиночные ASCII кавычки. (Символ одиночной кавычки, или апострофа, это \u0027.)

CharacterLiteral:
' SingleCharacter '
' EscapeSequence '
SingleCharacter:
InputCharacter но не ' или \

Литерал символа всегда имеет тип char (§4.2.1).

Содержимое литерала символа — это SingleCharacter или EscapeSequence, следующие за открывающей '.

Ошибка компиляции, если символ, следующий за содержимым, не является '.

Ошибка компиляции, если разделитель строки (§3.4) появляется после открывающей ' и перед закрывающей '.

Символы CR и LF никогда не являются InputCharacter; каждый из них распознается как LineTerminator, поэтому не может появляться в литерале символа, даже в последовательности управляющих символов \ LineTerminator.

Символ, представленный литералом символа — это содержимое литерала символа со всеми последовательностями управляющих символов, интерпретированных, как если бы они были выполнены String.translateEscapes на содержимом.

Литералы символов могут представлять только единицы кодировки UTF-16 (§3.1), т.е. они ограничены значениями от \u0000 до \uffff. Дополнительные символы должны быть представлены либо как пара суррогатов в последовательности char, либо как целое число, в зависимости от API, с которым они используются.

Ниже приведены примеры литералов символов:

  • 'a'

  • '%'

  • '\t'

  • '\\'

  • '\''

  • '\u03a9'

  • '\uFFFF'

  • '\177'

  • '™'

Поскольку эскапированные Unicode-последовательности обрабатываются очень рано, неверно писать '\u000a' для литерала символа, значение которого является символом новой строки (LF); эскапированная Unicode-последовательность \u000a преобразуется в фактический символ новой строки на этапе 1 (§3.3), а символ новой строки становится LineTerminator на этапе 2 (§3.4), поэтому литерал символа недействителен на этапе 3. Вместо этого следует использовать последовательность управляющих символов '\n'. Аналогично, неверно писать '\u000d' для литерала символа, значение которого является символом возврата каретки (CR). Вместо этого следует использовать '\r'. Наконец, невозможно написать '\u0027' для литерала символа, содержащего апостроф (').

В языках C и C++ литерал символа может содержать представления более чем одного символа, но значение такого литерала символа определяется реализацией. В языке программирования Java литерал символа всегда представляет ровно один символ.

3.10.5. Литералы строк

Литерал строки состоит из нуля или более символов, заключенных в двойные кавычки. Символы, такие как новые строки, могут быть представлены с помощью последовательностей управляющих символов (§3.10.7).

StringLiteral:
" {StringCharacter} "
StringCharacter:
InputCharacter но не " или \
EscapeSequence

Литерал строки всегда имеет тип String (§4.3.3).

Содержимое литерала строки — это последовательность символов, которая начинается сразу после открывающей " и заканчивается непосредственно перед соответствующей закрывающей ".

Ошибка компиляции, если разделитель строки (§3.4) появляется после открывающей " и перед соответствующей закрывающей ".

Символы CR и LF никогда не являются InputCharacter; каждый из них распознается как LineTerminator, поэтому не может появляться в литерале строки, даже в последовательности управляющих символов \ LineTerminator.

Строка, представленная литералом строки, — это содержимое литерала строки с интерпретированными всеми последовательностями управляющих символов, как если бы они были выполнены String.translateEscapes на содержимом.

Ниже приведены примеры литералов строк:

""                    // the empty string
"\""                  // a string containing " alone
"This is a string"    // a string containing 16 characters
"This is a " +        // actually a string-valued constant expression,
    "two-line string"    // formed from two string literals

Поскольку эскапированные Unicode-последовательности обрабатываются очень рано, неверно писать "\u000a" для литерала строки, содержащего один символ новой строки (LF); эскапированная Unicode-последовательность \u000a преобразуется в фактический символ новой строки на этапе 1 (§3.3), а символ новой строки становится LineTerminator на этапе 2 (§3.4), поэтому литерал строки недействителен на этапе 3. Вместо этого следует использовать последовательность управляющих символов "\n". Аналогично, неверно писать "\u000d" для литерала строки, содержащего один символ возврата каретки (CR). Вместо этого следует использовать "\r". Наконец, невозможно написать "\u0022" для литерала строки, содержащего двойную кавычку (").

Длинный литерал строки всегда можно разбить на более короткие части и записать как (возможно, заключенное в скобки) выражение, использующее оператор конкатенации строк + (§15.18.1).

Во время выполнения литерал строки является ссылкой на экземпляр класса String (§4.3.3), который обозначает строку, представленную литералом строки.

Кроме того, литерал строки всегда ссылается на тот же экземпляр класса String. Это потому, что литералы строк — или, более обобщенно, строки, которые являются значениями константных выражений (§15.29) — «интернируются» для совместного использования уникальных экземпляров, как если бы они были выполнены методом String.intern (§12.5).

Пример 3.10.5-1. Литералы строк

Программа, состоящая из единицы компиляции (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.println(hello == "Hello");
        System.out.println(Other.hello == hello);
        System.out.println(other.Other.hello == hello);
        System.out.println(hello == ("Hel"+"lo"));
        System.out.println(hello == ("Hel"+lo));
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

и единица компиляции:

package other;
public class Other { public static String hello = "Hello"; }

выводит:

true
true
true
true
false
true

Этот пример иллюстрирует шесть пунктов:

  • Литералы строк в одном классе и пакете представляют ссылки на один и тот же объект String (§4.3.1).

  • Литералы строк в разных классах в одном пакете представляют ссылки на один и тот же объект String.

  • Литералы строк в разных классах в разных пакетах также представляют ссылки на один и тот же объект String.

  • Строки, конкатенированные из константных выражений (§15.29), вычисляются во время компиляции и затем обрабатываются как если бы они были литералами.

  • Строки, вычисленные путем конкатенации во время выполнения, создаются заново и, следовательно, различны.

  • Результат явного интернирования вычисленной строки — тот же объект String, что и любой существующий литерал строки с таким же содержимым.


3.10.6. Блоки текста

Блок текста состоит из нуля или более символов, заключенных в открывающие и закрывающие разделители. Символы могут быть представлены с помощью последовательностей управляющих символов (§3.10.7), но символы новой строки и двойной кавычки, которые должны быть представлены с помощью последовательностей управляющих символов в строковой литерале (§3.10.5), могут быть представлены непосредственно в блоке текста.

TextBlock:
" " " {TextBlockWhiteSpace} РазделительСтрок {СимволБлокаТекста} " " "
TextBlockWhiteSpace:
Пробел но не РазделительСтрок
TextBlockCharacter:
ВводнойСимвол но не \
ПоследовательностьУправляющихСимволов
РазделительСтрок

Следующие продукции из §3.3, §3.4 и §3.6 приведены здесь для удобства:

WhiteSpace:
символ ASCII SP, также известный как "пробел"
символ ASCII HT, также известный как "горизонтальная табуляция"
символ ASCII FF, также известный как "форма подачи"
РазделительСтрок
LineTerminator:
символ ASCII LF, также известный как "новая строка"
символ ASCII CR, также известный как "возврат каретки"
символ ASCII CR, за которым следует символ ASCII LF
InputCharacter:
UnicodeВводнойСимвол кроме CR и LF
UnicodeInputCharacter:
UnicodeEscape
RawInputCharacter
UnicodeEscape:
\ UnicodeMarker ШестнадцатеричнаяЦифра ШестнадцатеричнаяЦифра ШестнадцатеричнаяЦифра ШестнадцатеричнаяЦифра
RawInputCharacter:
любой символ Unicode

Блок текста всегда имеет тип String (§4.3.3).

Открывающий разделитель — это последовательность, начинающаяся с трёх символов двойной кавычки ("""), продолжающаяся нулём или более символами пробела, табуляции и подачи формы, и заканчивающаяся разделителем строки.

Закрывающий разделитель — это последовательность из трёх символов двойной кавычки.

Содержание блока текста — это последовательность символов, начинающаяся сразу после разделителя строки открывающего разделителя и заканчивающаяся перед первым символом двойной кавычки закрывающего разделителя.

В отличие от строковой литералы (§3.10.5), появление разделителя строки в содержимом блока текста не является ошибкой компиляции.

Пример 3.10.6-1. Блоки текста

При необходимости многострочных строк блок текста обычно более читабелен, чем конкатенация строковых литералов. Например, сравните эти альтернативные представления фрагмента HTML:


String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;

Ниже приведены примеры блоков текста:

class Test {
    public static void main(String[] args) {
        // The six characters w i n t e r
        String season = """
                        winter""";

        // The seven characters w i n t e r LF
        String period = """
                        winter
                        """;

        // The ten characters H i , SP " B o b " LF
        String greeting = """
                          Hi, "Bob"
                          """;

        // The eleven characters H i , LF SP " B o b " LF
        String salutation = """
                            Hi,
                             "Bob"
                            """;        

        // The empty string (zero length)
        String empty = """
                       """;      

        // The two characters " LF
        String quote = """
                       "
                       """; 

        // The two characters \ LF
        String backslash = """
                           \\
                           """;  
    }
}


Использование последовательностей управляющих символов \n и \" для представления символа новой строки и символа двойной кавычки соответственно допускается в блоке текста, хотя обычно это не требуется. Исключением является случай, когда встречаются три последовательных символа двойной кавычки, которые не предназначены для закрывающего разделителя """ — в этом случае необходимо экранировать как минимум один символ двойной кавычки, чтобы избежать имитации закрывающего разделителя.

Пример 3.10.6-2. Последовательности управляющих символов в блоках текста

В следующей программе значение переменной story было бы менее читабельным, если бы отдельные символы двойной кавычки были экранированы:

class Story1 {
    public static void main(String[] args) {
        String story = """
            "When I use a word," Humpty Dumpty said,
            in rather a scornful tone, "it means just what I
            choose it to mean - neither more nor less."
            "The question is," said Alice, "whether you
            can make words mean so many different things."
            "The question is," said Humpty Dumpty,
            "which is to be master - that's all."
        """;
    }
}

Если программа изменена таким образом, чтобы поместить закрывающий разделитель в последней строке содержимого, возникает ошибка, поскольку первые три последовательных символа двойной кавычки в последней строке преобразуются (§3.2) в закрывающий разделитель """, и, таким образом, остаётся лишний символ двойной кавычки:

class Story2 {
    public static void main(String[] args) {
        String story = """
            "When I use a word," Humpty Dumpty said,
            in rather a scornful tone, "it means just what I
            choose it to mean - neither more nor less."
            "The question is," said Alice, "whether you
            can make words mean so many different things."
            "The question is," said Humpty Dumpty,
            "which is to be master - that's all."""";  // error
    }
}

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

class Story3 {
    public static void main(String[] args) {
        String story = """
            "When I use a word," Humpty Dumpty said,
            in rather a scornful tone, "it means just what I
            choose it to mean - neither more nor less."
            "The question is," said Alice, "whether you
            can make words mean so many different things."
            "The question is," said Humpty Dumpty,
            "which is to be master - that's all.\"""";  // OK
    }
}

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

class Code {
    public static void main(String[] args) {
        String text = """
            The quick brown fox jumps over the lazy dog
        """;

        String code =
            """
            String text = \"""
                The quick brown fox jumps over the lazy dog
            \""";
            """;
    }
}

Строка, представленная блоком текста, — это не буквальная последовательность символов в содержимом. Вместо этого строка, представленная блоком текста, является результатом применения следующих преобразований к содержимому в указанном порядке:

  1. Разделители строк нормализуются до символа ASCII LF следующим образом:

    • Символ ASCII CR, за которым следует символ ASCII LF, преобразуется в символ ASCII LF.

    • Символ ASCII CR преобразуется в символ ASCII LF.

  2. Случайные пробелы удаляются, как если бы они выполнялись String.stripIndent на символах, полученных на шаге 1.

  3. Последовательности управляющих символов интерпретируются, как если бы они выполнялись String.translateEscapes на символах, полученных на шаге 2.

Когда в этом спецификации говорится, что блок текста содержит определённый символ или последовательность символов, или что определённый символ или последовательность символов в блоке текста, это означает, что строка, представленная блоком текста (в отличие от буквальной последовательности символов в содержимом), содержит этот символ или последовательность символов.

Пример 3.10.6-3. Порядок преобразований содержимого блока текста

Интерпретация последовательностей управляющих символов в последнюю очередь позволяет программистам использовать \n, \f и \r для вертикального форматирования строки без влияния на нормализацию разделителей строк и использовать \b и \t для горизонтального форматирования строки без влияния на удаление случайных пробелов. Например, рассмотрите этот блок текста, в котором упоминается последовательность управляющих символов \r (CR):


String html = """
              <html>\r
                  <body>\r
                      <p>Hello, world</p>\r
                  </body>\r
              </html>\r
              """;

Последовательности управляющих символов \r интерпретируются не ранее, чем после нормализации разделителей строк до LF. Используя Unicode-экранирование для визуализации LF (\u000A) и CR (\u000D), и используя | для визуализации левого отступа, строка, представленная блоком текста, имеет вид:


|<html>\u000D\u000A
|    <body>\u000D\u000A
|        <p>Hello, world</p>\u000D\u000A
|    </body>\u000D\u000A
|</html>\u000D\u000A


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

Кроме того, блок текста всегда ссылается на один и тот же экземпляр класса String. Это происходит потому, что строки, представленные блоками текста, или, более обще, строки, являющиеся значениями константных выражений (§15.29), «интернируются», чтобы иметь уникальные экземпляры, как если бы выполнялся метод String.intern (§12.5).

Пример 3.10.6-4. Блоки текста оцениваются как String

Блоки текста могут использоваться там, где разрешено выражение типа String, например, в конкатенации строк (§15.18.1), в вызове методов экземпляров String и в аннотациях с элементами String:


System.out.println("ab" + """
                          cde
                          """);

String cde = """
             abcde""".substring(2);

String math = """
              1+1 equals \
              """ + String.valueOf(2);

@Preconditions("""
    rate > 0 &&
    rate <= MAX_REFRESH_RATE
""")
public void setRefreshRate(int rate) { ... }


3.10.7. Последовательности экранирования

В литералах символов, строковых литералах и блоках текста (§3.10.4, §3.10.5, §3.10.6) последовательности экранирования позволяют представлять некоторые неграфические символы без использования Unicode-экранирования (§3.3), а также символы одиночной кавычки, двойной кавычки и обратной косой черты.

EscapeSequence:
\ b (возврат на один символ назад BS, Unicode \u0008)
\ s (пробел SP, Unicode \u0020)
\ t (горизонтальная табуляция HT, Unicode \u0009)
\ n (новая строка LF, Unicode \u000a)
\ f (форма подачи FF, Unicode \u000c)
\ r (возврат каретки CR, Unicode \u000d)
\ LineTerminator (продолжение строки, без представления Unicode)
\ " (двойная кавычка ", Unicode \u0022)
\ ' (одинарная кавычка ', Unicode \u0027)
\ \ (обратная косая черта \, Unicode \u005c)
OctalEscape (восьмеричное значение, Unicode \u0000 по \u00ff)
OctalEscape:
\ OctalDigit
\ OctalDigit OctalDigit
\ ZeroToThree OctalDigit OctalDigit
OctalDigit:
(один из)
0 1 2 3 4 5 6 7
ZeroToThree:
(один из)
0 1 2 3

Производство OctalDigit выше взято из §3.10.1. Восьмеричные экранирования предоставляются для совместимости с C, но могут выражать только значения Unicode \u0000 по \u00FF, поэтому обычно предпочтительнее использовать Unicode-экранирование.

Ошибка компиляции, если символ, следующий за обратной косой чертой в последовательности экранирования, не является LineTerminator или ASCII b, s, t, n, f, r, ", ', \, 0, 1, 2, 3, 4, 5, 6 или 7.

Последовательность экранирования в содержимом литерала символа, строкового литерала или блока текста интерпретируется заменой его \ и последующего символа(ов) одним символом, обозначаемым Unicode-экранированием в грамматике EscapeSequence. Последовательность экранирования продолжения строки не имеет соответствующего Unicode-экранирования, поэтому интерпретируется заменой её ничем.

Последовательность экранирования продолжения строки может появляться в блоке текста, но не может появляться в литерале символа или строковом литерале, так как каждый из них запрещает LineTerminator.

3.10.8. Литерал null

Тип null имеет одно значение — нулевую ссылку, представленную литералом null null, образованным из ASCII-символов.

NullLiteral:
null

Литерал null всегда относится к типу null (§4.1).

3.11. Разделители

Двенадцать токенов, образованных из ASCII-символов, являются разделителями (знаками препинания).

Separator:
(один из)
(   )   {   }   [   ]   ;   ,   .   ...   @   ::

3.12. Операторы

38 токенов, образованных из ASCII-символов, являются операторами.

Operator:
(один из)
=   >   <   !   ~   ?   :   ->
==  >=  <=  !=  &&  ||  ++  --
+   -   *   /   &   |   ^   %   <<   >>   >>>
+=  -=  *=  /=  &=  |=  ^=  %=  <<=  >>=  >>>=

© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API