Следующий код производит вывод "Hello World!" (нет, на самом деле, попробуйте).
public static void main(String... args) {
// The comment below is not a typo.
// \u000d System.out.println("Hello World!");
}
Причина этого заключается в том, что компилятор Java анализирует символ Unicode \u000d
как новую строку и преобразуется в:
public static void main(String... args) {
// The comment below is not a typo.
//
System.out.println("Hello World!");
}
Это приводит к тому, что комментарий «исполняется».
Поскольку это можно использовать для «сокрытия» вредоносного кода или всего, что может представить злой программист, почему это разрешено в комментариях ?
Почему это разрешено спецификацией Java?
Ответы:
Unicode-декодирование происходит перед любым другим лексическим переводом. Основное преимущество этого состоит в том, что он делает переход между ASCII и любой другой кодировкой тривиальным. Вам даже не нужно выяснять, где комментарии начинаются и заканчиваются!
Как указано в разделе 3.3 JLS, это позволяет любому инструменту на основе ASCII обрабатывать исходные файлы:
Это дает фундаментальную гарантию независимости платформы (независимости поддерживаемых наборов символов), которая всегда была ключевой целью для платформы Java.
Возможность написать любой символ Unicode в любом месте файла - это удобная функция, особенно важная в комментариях при документировании кода на нелатинских языках. Тот факт, что он может вмешиваться в семантику такими тонкими способами, является лишь (неудачным) побочным эффектом.
Есть много ошибок на эту тему, и Java Puzzlers Джошуа Блоха и Нила Гафтера включили следующий вариант:
(Эта программа оказывается простой программой "Hello World".)
В решении головоломки, они указывают на следующее:
Источник: Java: Выполнение кода в комментариях ?!
источник
\u000d
а часть после него должна иметь подсветку кода.// C:\user\...
что приводит к ошибке компиляции, поскольку\user
не является допустимой escape-последовательностью Unicode.\u000d
подсвечивается частично. После нажатия Ctrl + Shift + F символ заменяется новой строкой, а остальная строка\u002A/
должен закончить комментарий.Поскольку это еще не решено, здесь поясняется, почему перевод экранирования Unicode происходит перед любой другой обработкой исходного кода:
Идея заключалась в том, что он позволяет переводить исходный код Java без потерь между различными кодировками символов. Сегодня широко распространена поддержка Unicode, и это не выглядит проблемой, но тогда разработчику из западной страны было нелегко получить исходный код от своего азиатского коллеги, содержащий азиатские символы, внести некоторые изменения ( включая компиляцию и тестирование) и отправку результата обратно, все без ущерба для чего-либо.
Таким образом, исходный код Java может быть написан в любой кодировке и допускает широкий диапазон символов в пределах идентификаторов, символов и
String
литералов и комментариев. Затем, чтобы передать его без потерь, все символы, не поддерживаемые целевой кодировкой, заменяются их экранированием Unicode.Это обратимый процесс, и интересным моментом является то, что перевод может быть выполнен инструментом, которому не нужно ничего знать о синтаксисе исходного кода Java, поскольку правило перевода не зависит от него. Это работает, поскольку перевод их фактических символов Unicode внутри компилятора происходит независимо от синтаксиса исходного кода Java. Это означает, что вы можете выполнить произвольное количество шагов перевода в обоих направлениях, не меняя смысла исходного кода.
Это причина еще одной странной особенности, о которой даже не упоминалось:
\uuuuuuxxxx
синтаксис:Когда инструмент перевода экранирует символы и встречает последовательность, которая уже является экранированной последовательностью, он должен вставить
u
в последовательность дополнительный символ , преобразовав\ucafe
в\uucafe
. Значение не меняется, но при преобразовании в другом направлении инструмент должен просто удалить одинu
и заменить только последовательности, содержащие одинu
, их символами Юникода. Таким образом, даже экранированные символы Юникода сохраняются в своем первоначальном виде при конвертации назад и вперед. Я думаю, никто никогда не использовал эту функцию ...источник
native2ascii
что, кажется, не использует\uu...xxxx
синтаксис,native2ascii
было предназначено, чтобы помочь подготовить пакеты ресурсов, преобразовав их в iso-latin-1, так какProperties.load
было исправлено только чтение latin-1. И там, правила разные, нет\uuu…
синтаксиса и нет ранней стадии обработки. В файлах свойств,property=multi\u000aline
действительно так же, какproperty=multi\nline
. (Вопреки фразе «использование выходов Unicode, как определено в разделе 3.3 спецификации языка Java ™» документации)\u
экранирование для генерации символов в диапазоне U + 0000–007F. (Все такие символы могут быть изначально представлены всеми национальными кодировками, которые были актуальны в 1990-х годах - ну, может быть, за исключением некоторых управляющих символов, но вам все равно они не нужны для написания Java.)Я собираюсь совершенно безрезультатно добавить это, просто потому, что я не могу с собой поделать, и я еще не видел, чтобы это было сделано, что вопрос недействителен, поскольку содержит скрытую предпосылку, которая неверна, а именно, что комментарий!
В Java исходный код \ u000d во всех отношениях эквивалентен символу ASCII CR. Это конец строки, простой и понятный, где бы он ни происходил. Форматирование в вопросе вводит в заблуждение, то, что фактически соответствует этой последовательности символов:
ИМХО, поэтому самый правильный ответ: код выполняется, потому что его нет в комментарии; это на следующей строке. «Выполнение кода в комментариях» не разрешено в Java, как и следовало ожидать.
Большая часть путаницы проистекает из того факта, что подсветки синтаксиса и IDE не достаточно сложны, чтобы принять во внимание эту ситуацию. Они либо вообще не обрабатывают экранирование Юникода, либо делают это после анализа кода, а не до того, как это
javac
делается.источник
\u000d
Побег заканчивается комментарий , потому что\u
побеги равномерно преобразуются в соответствующие символы Unicode , прежде чем программа лексемы. Вы могли бы также использовать\u0057\u0057
вместо того,//
чтобы начать комментарий.Это ошибка в вашей IDE, которая должна синтаксически выделять строку, чтобы было ясно, что
\u000d
комментарий заканчивается.Это также ошибка дизайна в языке. Это не может быть исправлено сейчас, потому что это сломало бы программы, которые зависят от него.
\u
escape-коды должны быть либо преобразованы компилятором в соответствующий символ Unicode только в тех случаях, когда это «имеет смысл» (строковые литералы и идентификаторы, и, вероятно, нигде больше), либо им должно быть запрещено генерировать символы в диапазоне U + 0000–007F. , или оба. Любая из этих семантик помешала бы завершению комментария\u000d
экранированием, не вмешиваясь в случаи, когда\u
экранирования полезны - обратите внимание, что это включает использование\u
экранирования внутри комментариев в качестве способа кодирования комментариев в нелатинском скрипте, потому что текстовый редактор может иметь более широкое представление о том, где\u
побеги важнее, чем компилятор. (Я не знаю ни о каком редакторе или IDE, которые будут отображать\u
экранирование как соответствующие символы в любом контексте.)Существует аналогичная ошибка проектирования в семействе C, 1 когда обратная косая черта обрабатывается до определения границ комментариев, например,
Я привожу это, чтобы проиллюстрировать, что бывает легко совершить эту конкретную ошибку проектирования, и не понимаю, что это ошибка, пока не стало слишком поздно ее исправлять, если вы привыкли думать о токенизации и разбирать способ, которым думают программисты компилятора о токенизации и разборе. По сути, если вы уже определили свою формальную грамматику, а затем кто-то придумает синтаксический особый случай - триграфы, обратную косую черту, кодирование произвольных символов Unicode в исходных файлах, ограниченных ASCII, что угодно, - что нужно вставить в код, проще добавьте проход преобразования перед токенизатором, чем переопределить токенизатор, чтобы обратить внимание на то, где имеет смысл использовать этот особый случай.
1 Для педантов: я знаю, что этот аспект C был на 100% преднамеренным, с обоснованием - я не придумываю это - что это позволило бы вам механически втиснуть код произвольно длинными линиями в перфокарты. Это было все еще неправильное дизайнерское решение.
источник
\u
С учетом сказанного, я думаю, что выбор этапа обработки для был менее абсурдным, чем решение последовать примеру C в использовании начальных нулей для восьмеричной записи. Хотя восьмеричные нотации иногда полезны, я еще не слышал, чтобы кто-либо формулировал аргумент, почему ведущий ноль является хорошим способом его обозначения.\u
как преобразованием перед токенизацией, если бы было запрещено создавать символы в диапазоне U + 0000..U + 007F. Именно сочетание «это работает везде» и «это псевдоним ASCII-символов с синтаксической значимостью» превращает его из неловкого в прямое и неправильное.//
Это был намеренный выбор дизайна, который восходит к первоначальному дизайну Java.
Тем людям, которые спрашивают «кто хочет, чтобы Unicode избегал комментариев в комментариях?», Я предполагаю, что это люди, чей родной язык использует латинский набор символов. Другими словами, в первоначальном дизайне Java заложено, что люди могут использовать произвольные символы Unicode везде, где это разрешено в программе Java, чаще всего в комментариях и строках.
Возможно, в программах (таких как IDE), используемых для просмотра исходного текста, есть недостаток, заключающийся в том, что такие программы не могут интерпретировать экранирование Unicode и отображать соответствующий глиф.
источник
Я согласен с @zwol, что это ошибка дизайна; но я еще более критично отношусь к этому.
\u
escape полезен в строковых и символьных литералах; и это единственное место, где оно должно существовать. С ним нужно обращаться так же, как и с другими побегами\n
; и"\u000A"
должно означать точно"\n"
.В
\uxxxx
комментариях нет абсолютно никакого смысла - никто не может это прочитать.Точно так же нет смысла использовать
\uxxxx
в другой части программы. Единственное исключение, вероятно, в общедоступных API, которые принудительно содержат некоторые не-ascii-символы - что мы видели в последний раз?У дизайнеров были свои причины в 1995 году, но спустя 20 лет это, кажется, неправильный выбор.
(вопрос к читателям - почему этот вопрос продолжает получать новые голоса? Этот вопрос связан где-то популярно?)
источник
int \u5431
когда можно сделатьint 整
UTF-8
в 1995 году не было широкой поддержки). Вам просто нужно вызвать один метод, и вы не хотите устанавливать пакет поддержки азиатского языка вашей операционной системы (вспомните, девяностые годы) для этого единственного метода…Единственные люди, которые могут ответить, почему экранирование Unicode было реализовано так, как они были, - это люди, которые написали спецификацию.
Возможная причина этого заключается в том, что было желание разрешить весь BMP как возможные символы исходного кода Java. Это представляет проблему, хотя:
Это невероятно сложно, когда Юникоду удается избежать столкновения: он создает целый набор новых правил лексера.
Самый простой выход состоит в том, чтобы выполнить лексирование в два этапа: сначала найдите и замените все экранированные символы Юникода символом, который он представляет, а затем проанализируйте полученный документ, как если бы экранированные символы Юникода не существовали.
Плюсом этого является то, что его легко указать, поэтому он упрощает спецификацию и легко реализуется.
Недостатком является, ну, ваш пример.
источник