«Безопасный язык» говорили они, «четкая спецификация» говорили они, «Java не даст вам выстрелить себе в ногу» и прочее в таком духе. Реальность же оказалась куда веселее официальной документации и мнений экспертов.
«JVM темна и полна ужасов». (ц)

Все началось как обычно:
копаясь в проектах на Github в поисках всякого интересного и необычного, наткнулся на подборку
лютейшей дичипримеров особенного кода, с любовью отобранного неизвестным индийским студентом.
Часть из этих замечательных примеров оказалась реализована на.. Java.
Что удивило, поскольку язык не славится undefined behavior и вообще редко используется для диких программистких трюков.
Так что я решил разобрать самые отбитые интересные примеры, благо в описаниях присутствовали ссылки на обсуждение конкретной «аномалии» и описание логики работы.
Вот как описывает подборку сам автор:
A Nonsense Collection of Disgusting Codes
Here we are talking about creepiest of the most creepy codes. Programs, behave so strange, that they will twist your brain. Snippets, so small, that you won’t believe their functionality. And codes, so cryptic, that even the top coders will think of going back to the college.
Особенно порадовал этот абзац:
Never try this type of code in a real life software project; readability and maintainability should be the main concern there.
Видимо автор догадывается что ситуации бывают разные, в том числе допускающие появление подобной дичи в реальных проектах.
Дичь первая: веселый "Hello world"
Запуск этого примера с выводом можно наблюдать на заглавной картинке статьи, исходный код выглядит так:
public class hello_world{
public static void main (String[] args) {
for(long l=4946144450195624l; l>0; l>>=5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
}
}
Начало обсуждения сего замечательного кода на StackOverflow порадовало разрывом

К счастью не все обитатели SO окончательно деградировали, поэтому достаточно быстро появилось и адекватное объяснение происходящего:
You are getting a result which happens to be char representation of below values
А приложенный фрагмент окончательно раскрыл все карты:
104 -> h
101 -> e
108 -> l
108 -> l
111 -> o
32 -> (space)
119 -> w
111 -> o
114 -> r
108 -> l
100 -> d
Думаю погружаться глубже и объяснять детали уже не надо — и так все понятно.
Хотя на разбор процесса упаковки всех этих чисел в одно я бы посмотрел с интересом.
Дичь вторая: магическое кеширование
Оригинальная статья, описывающая эту проблему в деталях находится тут.
А пока разберем код — отличный пример side‑effect, когда сначала во имя добра и справедливости добавляют универсальный функционал, а затем кусают локти в безуспешных попытках обуздать полет мысли пользователей:
import java.lang.reflect.Field;
import java.util.Random;
public class crazy_jvm {
public static void main(String[] args) throws Exception {
justKidding();
for(int i=0; i<10; i++){
System.out.println((Integer) i);
}
}
private static void justKidding() throws Exception{
// extract the IntegerCache through reflection
Field field = Class.forName("java.lang.Integer$IntegerCache").getDeclaredField("cache");
field.setAccessible(true);
Integer[] cache = (Integer[]) field.get("java.lang.Integer$IntegerCache");
// rewrite the Integer cache
for (int i=0; i<cache.length; i++){
cache[i] = new Integer(new Random().nextInt());
}
}
}
Что тут происходит:
с помощью Reflection API — того самого универсального функционала, добавленного во имя добра и справедливости происходит нехорошее действо над другим универсальным фукнционалом — встроенного в JVM кеша для простых чисел.
Как известно добрыми намерениями выстелена дорога в Ад и пример выше — яркое тому подтверждение.
В результате работы этого кода, программист получает психологическую травму происходит незаметное изменение логики работы — вместо последовательного счетчика цикла отображается «отравленный» кеш с подмененным содержимым:

Добавлю, что абсолютно точно не я предложил эту идею:
реализовать еще один метод, который бы возвращал кеш чисел в оригинальное состояние и затем вызывать оба метода из юнит-тестов, доводя тем самым произвольное количество разработчиков до дурки.
Пожалуйста не ссылайтесь на эту статью, когда будете такое реализовывать.
Дичь третья: прелести типизации
Прикинувшись рекрутером задам стандартный вопрос с собеседования — «объясните что делает этот код»:
class confusion{
public static void main (String[] args){
int i = (byte) + (char) - (int) + (long) - 1;
System.out.println(i);
}
}
Справитесь?
Врядли, по крайней мере ни я сам ни кто-либо из моих коллег с ходу разобраться не смогли, хотя пишем на Java второй десяток лет.
Поэтому обратимся к подсказке от автора:
So this is just a load of casts applied to unary operators applied ultimately to the 1 at the end of the line. i.e. you are casting the -1 to a long, applying the + unary operator to it (so still -1), casting as an int, applying the unary - to it (we now have a value of 1), casting as a char, applying unary + to it and finally casting as a byte before storing as (and implicitly casting to) an int. It is simply the presence of the two unary minuses that makes the result 1.
Вот тут на StackOverflow находится более развернутое обсуждение.
Да, код действительно работает и отображает цифру 1:

Ну разве Java не прелесть?
Дичь четвертая: максимальное веселье
Настоящий шедевр высокой кухни для истинных ценителей дичи, код после просмотра которого вы немедленно побежите прочесывать рабочие проекты в поисках подобных паттернов:
public class obfuscated{
/**
* Hi!
* u002au002fu0020u0070u0075u0062u006cu0069u0063u0020u0020u0020
u0073u0074u0061u0074u0069u0063u0020u0076u006fu0069u0064u0020
u006du0061u0069u006eu0028u0053u0074u0072u0069u006eu0067u0020
u005bu005du0061u0029u007bu0053u0079u0073u0074u0065u006du002e
u006fu0075u0074u002eu0070u0072u0069u006eu0074u006cu006eu0020
u0028u0022u0048u0069u0021u0022u0029u003bu007du0020u002fu002a
*/
}
И да, это действительно работает:

Теперь стоит рассказать как работает сей сказочный код.
Большой закомментированный блок с непонятными символами внутри, на самом деле вполне рабочий код на Java, но перекодированный в UTF-16.
После перекодирования обратно в ASCII он выглядит так:
*/ public
static void
main(String
[]a){System.
out.println
("Hi!");} /*
Видите закрывающий и открывающий символы комментирования?
Именно этими символами и обеспечивается скрытие и ощущение, что весь блок был закомментирован.
Стоит пояснять, что таким способом можно легко закодировать в рабочий проект например реверс-шелл?
К сожалению мне не удалось отыскать оригинал этой статьи, комментарий в шапке примера с кодом:
// Developer- Peter van der Linden (April 1, 1996), little modified.
// Intro- Prints "Hi!" in the console, looks like a big meaningless comment though.
// Details- https://community.oracle.com/blogs/forax/2006/10/16/obfuscated-java
дал только имя автора Peter van der Linden, написавшего в свое время достаточно известную книгу по Java:
Just Java™ 2, Fifth Edition
Released December 2001 Publisher(s): Pearson ISBN: 9780130320728
Но никаких упоминаний трюка с обфускацией на его сайте найти не удалось.
Вторая ссылка на статью из блога Oracle оказалась битой и выдает 404, но ник автора (forax) навел на одного из Java Champion по имени Remi Forax.
Хотя его Github полон разных интересных проектов (некоторые из которых я со временем точно разберу), каких-либо упоминаний трюка с обфускацией также найдено не было, увы.
Так что если кто из читателей сможет разыскать оригинал с разбором столь замечательного кода — обязательно добавлю в статью.
P.S.
Это цензурированная версия статьи, куда более вольный оригинал которой доступен в нашем блоге.
0x08 Software
Мы небольшая команда ветеранов ИТ‑индустрии, создаем и дорабатываем самое разнообразное программное обеспечение, наш софт автоматизирует бизнес‑процессы на трех континентах, в самых разных отраслях и условиях.
Оживляем давно умершее, чиним никогда не работавшее и создаем невозможное — затем рассказываем об этом в своих статьях.
Автор: alex0x08