Снова показываю как вести разработку «голыми руками» — без IDE, документации и даже интернета. На этот раз с помощью «пользовательской» Ubuntu Linux и OpenJDK.

Поскольку современные разработчики постоянно жалуются на завышенные требования технических интервью вообще и на мою «дурную практику» написания кода от руки в частности — показываю на личном примере как все это работает.
Жертвам «слабой памяти» посвящается.
Заодно узнаете как можно вести разработку на Java хоть в чистом поле — в самолете, в поезде или на закрытом объекте, без подключения к интернету и документации.
Видео
На этот раз для большего угара помимо статьи было записано и видео, где показан весь процесс «полевой разработки» на Java, с одним только JDK.
Также ролик можно посмотреть на VK Video, Youtube и Платформе.
Тестовое окружение
Для большей чистоты эксперимента был взят Live-образ Ubuntu Desktop 24.04.3 LTS, записан на флешку, флешка вставлена в один из рабочих ноутбуков, который затем с нее был загружен.
Таким образом получилась чистая система, без средств разработки и с отключенной сетью.
Из инструментов будет лишь текстовый редактор и JDK.
И все.
Что будем писать
Самое простое что можно написать в таких полевых условиях — реверс-шелл HTTP-сервер. На самом деле написать можно много чего, особенно если посмотреть в каталог demo внутри OpenJDK:

Здесь и далее скриншоты из другой системы (Manjaro), чтобы не заморачиваться с их перебрасыванием из Live-системы и добавлением в статью.
Тем не менее на видео все описываемые в статье шаги и весь код вбиваются каноничным способом — полностью вручную, на чистой системе, загруженной с Live USB.
Демо
Упомянутый выше каталог demo содержит набор довольно серьезных примеров проектов, которых вам вполне хватит для начальной стадии изучения или в качестве основы для какого-нибудь прототипа, особенно если никаких других инструментов и интернета — нет.
Так выглядит демо-проект Notepad, реализующий простейший текстовый редактор:

Так выглядит демо Metalworks, с простейшей реализацией мульти-оконной системы (MDI):

Напоминаю, что вся эта благодать находится внутри стандартной поставки любой версии JDK, начиная с незапамятных времен 8й версии.
Все демо-проекты содержат исходный код в архивах
src.zipи собираются без внешних зависимостей.
К сожалению каталог с демо иногда вырезается ментейнерами дистрибутивов линукса ради экономии места. И переносится в отдельный пакет, который пользователи разумеется забывают установить.
Ручная разработка
В ролике в записи показано как автор вводит и запускает в работу примерно такой код:
// разумеется я не помню названий абсолютно всех
// импортируемых классов, поэтому тут стоит '*'
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class MyWebServer {
static void handle(Socket s) {
// метод getId() устарел, поэтому его использование в
// последних версиях JDK выдает предупреждение
System.out.println("Thread: %d"
.formatted(Thread.currentThread().getId()));
// самое сложное место, которое удалось повторить на записи
// далеко не с первой попытки
try(PrintWriter out = new PrintWriter(s.getOutputStream());
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));) {
// поскольку используется чтение и запись строк а не байт - читаем
// строку целиком, т.е. до символа \n
String l = in.readLine();
// тут просто показываем в консоль
System.out.println(l);
// этим простым способом читаем только строку запроса,
// которая идет первой, пропустив все заголовки
// \r\n (пустая строка) - признак завершения запроса
while (l==null || l.isEmpty() || "\r\n".equals(in.readLine()));
// тут мы 'в лоб' сравниваем строку HTTP-запроса целиком
// так она выглядит до работы парсера
if ("GET /test HTTP/1.1".equals(l)) {
// поскольку мы реагируем только на один url '/test'
// формируем ниже статичный ответ
String data = "Hello from alex0x08 at "+ new Date();
// так выглядят стандартные поля ответа в 'raw' виде, без обработки
out.println("HTTP/1.1 200 OK");
// 'close' дает указание браузеру разорвать соединение
// с сервером сразу после получения данных
out.println("Connection: close");
// поскольку мы отдаем строку - ставим MIME тип 'text/plain'
out.println("Content-Type: text/plain");
// опционально отдаем размер данных
out.println("Content-Length: " + data.length());
// пустая строка - признак начала блока с данными
out.println();
// отдаем сами данные
out.println(data);
} else {
// во всех остальных случаях формируем ответ 404
out.println("HTTP/1.1 404 Not Found");
out.println("Connection: close");
out.println();
}
// нужно обязательно вызывать поскольку PrintWriter кеширует данные
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
// в любом случае закрываем клиентский сокет
try {s.close();} catch (Exception ee) {}
}
}
// стартовый метод приложения
public static void main(String[] args) throws Exception {
System.out.println("Starting..");
// тоже сложное место, которое было непросто ввести по памяти
ExecutorService p = Executors.newFixedThreadPool(10);
// создание 'серверного' сокета, который будет прослушивать
// указанный порт
// поскольку хост не указан - будут прослушиваться все (0.0.0.0)
ServerSocket ss = new ServerSocket(8089);
// бесконечный цикл, который нужен тк метод accept() - блокирующий
// и выход из него произойдет после получения входящего подключения
while (true) {
// получен клиентский сокет
Socket s = ss.accept();
// запуск асинхронной обработки
p.execute(() -> handle(s));
}
}
}
Комментариев в версии кода, который был показан на записи разумеется нет, они были добавлены уже после — для большего понимания.
Этот код реализует простейший многопоточный веб-сервер на Java, который отвечает лишь на один URL /test и отдает заранее заданную строку с датой.
Как видите даже столь небольшого кода достаточно чтобы можно было подключиться из современного браузера Chrome:

Компиляция выполняется как и в записи всего одной командой:
javac -cp . MyWebServer.java
После чего появится один единственный .class файл c совпадающим именем, поскольку пакеты не использовались, для запуска достаточно указать в качестве classpath текущий каталог:
java -cp . MyWebServer
Но это все лирика и понты.
Когда кончается память
Разумеется невозможно запомнить абсолютно все и рано или поздно вы столкнетесь с названием метода или класса, которые надо где-то подсмотреть.
Автор при записи видео столкнулся с таким в двух местах:
длинные классы-обертки над потоками (stream) сокета и сложное название статичного метода, создающего экземпляр ExecutorService.
И то и другое получилось правильно ввести далеко не с первой попытки.
Возвращаясь к ситуации когда нет доступа к интернету и полноценной среды разработки, зато на машине есть JDK — показываю что можно сделать в этом непростом случае.
Невероятно но факт:
подсмотреть названия системных классов и методов можно.. в самом JDK!
Вот это поворот!
В последних версиях JDK появилась интересная утилита jimage, которая находится в каталоге bin (там же где и главные бинарники java и javac).
С помощью этой штуки можно легко посмотреть полные названия всех системных классов:

Правда знание полного имени класса не всегда помогает, поскольку в JDK много вложенных системных классов, которые по идее вызывать снаружи не надо.
Команда для запуска:
jimage list $JAVA_HOME/lib/modules |less
где переменная JAVA_HOME указывает на каталог с установленной JDK:

Так вы увидите названия всех системных классов, но что делать с методами?
Вытаскиваем сигнатуры методов
Тут тоже есть решение, поскольку эта же утилита позволяет распаковывать jmod-файлы в которых находятся системные .class-файлы JDK.
jimage extract --dir=/opt/src/tmp $JAVA_HOME/lib/modules
А еще одна утилита javap позволяет посмотреть метаданные .class-файла, в том числе сигнатуры всех методов:
cd /opt/src/tmp/jre
javap java.base/java/nio/Bits.class
Так выглядит результат:

В виде текстового блока с подсветкой синтаксиса:
class java.nio.Bits {
static final jdk.internal.misc.VM$BufferPool BUFFER_POOL;
static final int JNI_COPY_TO_ARRAY_THRESHOLD;
static final int JNI_COPY_FROM_ARRAY_THRESHOLD;
static final boolean $assertionsDisabled;
static short swap(short);
static char swap(char);
static int swap(int);
static long swap(long);
static int pageSize();
static long pageCount(long);
static boolean unaligned();
static void reserveMemory(long, long);
static void unreserveMemory(long, long);
static {};
}
Вот этого уже с запасом хватит для полевой разработки в условиях крайнего Севера.
Если у вас есть реальный, а не нарисованный опыт разработки на Java, двух этих трюков будет достаточно для работы в поезде или самолете или на чужом компьютере — в тех местах и обстоятельствах, где нет подготовленного рабочего места.
Исходники JRE
Если вам совсем повезет, в каталоге JDK/lib будет находиться файл src.zip, внутри которого будут исходники всех системных классов JRE:

«Повезет» — потому что также как и demo, этот файл часто удаляют ментейнеры дистрибутивов Linux, с переносом в отдельный пакет. Но разумеется если он присутствует, то поможет гораздо больше чем все приседания с javap.
В распакованном виде:

Внутри находится исходный код всех классов Java, используемых в JDK:
cat java.base/java/io/Bits.java |less
Так выглядит исходный код класса java.io.Bits, который мы просматривали выше с помощью javap:

Эпилог
Смысл такой «полевой разработки» — в первую очередь проверка реальных практических навыков, которые находятся в голове у программиста, а не где‑то в интернете.
К сожалению на 2025й год можно констатировать, что такие навыки являются большой редкостью и мало кто из кандидатов, которых я когда-либо собеседовал могли осилить написание хотя‑бы трети подобного кода.
Кстати в нашем Телеграм-канале выложено первое техническое видео (запись с экрана), где впервые получилось проверить всю идею.
Оригинал статьи как обычно в нашем блоге.
Комментарии (22)

novoselov
01.09.2025 09:47Для начала стоило бы посмотреть последние нововведения в JDK (включая JDK 25)
Теперь можно написать короче: без классов, без модификаторов, без аргументов, без System.out
void main() { IO.println("hello"); }И запускать просто файл без предварительной компиляции
java app.javaИли сделать shebang
#!/usr/bin/java --source 25или shebang с дополнительными параметрами
#!/usr/bin/env -S java --source 25 --class-path lib/*И запускать так (нужно только удалить разрешение .java)
chmod +x app ./appА импорт классов сделать целым модулем
import module java.base;
alex0x08 Автор
01.09.2025 09:47Рад конечно что вы
умеете гуглить ив курсе последних фич, но есть два нюанса:Если приглядеться, то можно увидеть что в статье скриншоты трех разных версий JDK (17,21 и 24), потому что в разных дистрибутивах разные версии подразумеваются под LTS. Желание использовать свежие фичи конечно похвально, но в случае «полевой разработки» вас может ожидать все что угодно — от 11й версии до последней 25й, поэтому смысла фокусироваться на именно последней 25й особого нет.
Shebang ну в данной конкретной задаче еще возможен, но и только. На отдельных классах шебанг закончится.
Наконец последнее: в условиях интервью ни про шебанг, ни про импорт модулей ни про void main() никто не знает, так что все эти новые фичи я даже не спрашиваю (

novoselov
01.09.2025 09:47Использовать это для интервью? С какой целью?
Либо вы предоставляете кандидату ссылку на онлайн сервис с поддержкой компиляции, либо даете пользоваться любым привычным IDE. Во всех остальных случаях это не отличается от написания кода от руки.
alex0x08 Автор
01.09.2025 09:47Во всех остальных случаях это не отличается от написания кода от руки.
Да, именно так и есть. Потому что я не нанимаю разработчика IDE, LLM или поисковой системы и важны только те знания, которые есть в голове а не где-то еще.

hddn
01.09.2025 09:47Было очень интересно, спасибо за статью!
Получилось актуальненко для меня - пришлось недавно написать пару скриптов на JBang, без IDE, в голом vim'е, даже без подсветки синтаксиса. На удивление получилось! :)
Конечно, ну его нафиг такие приключения, но судьба преподносит иногда невероятные вызовы. :)

Samhuawei
01.09.2025 09:47Vim подсвечивает синтаксис если правильно настроить терминал. Не подсвечивает классический vi.
Я частенько пользуюсь голой джавой на препод системе чтобы проверить правильность установки сертификатов в jre.

Masnin
01.09.2025 09:47Люди, значит, вайбокодят километрами листингов кода, а вы предлагаете от IDE в пользу нотпада оказаться)
Мне когда-то так же советовали писать на ассемблере. Круто, конечно, вот только зачем...

alex0x08 Автор
01.09.2025 09:47Люди, значит, вайбокодят километрами листингов кода, а вы предлагаете от IDE в пользу нотпада оказаться
Не то чтобы предлагаю, скорее демонстрирую что такое еще вообще возможно )
А вайбкодинг это натуральный дар небес для всех кто хоть немного понимает в нормальной разработке, поскольку такие километры треша и дичи разгребать после вайбкодеров придется десятилетиями.
И по очень хорошему рейту.

oilmonster
01.09.2025 09:47А где вы сударь в полях найдете электричество и канал связи для вашего чудо проекта написанного как попало в вакууме от руки?
И второе, сейчас все требуют опыт несколько лет коммерческой разработки, а не писанину в геологической партии, даже в морской дальней экспедиции еще потребуется навык пайки электроники, что тоже требует электричество
Ваш кейс слепой разработки сможет помочь если вам надо кодить находясь в заложниках, но это уже про способ выживания и к разработке реальных фич отношения не имеет

Mr_Qwerty
01.09.2025 09:47Нигде он не найдёт и искать не будет. Тут проблема в безудержном желании являть себя миру в целях самолюбования. Если этот персонаж научится ногами печатать, не сомневайтесь, статья не заставит себя долго ждать (как и проверка навыка на собесах)

alex0x08 Автор
01.09.2025 09:47в полях найдете электричество и канал связи
Написал же: в поезде и самолете.
сейчас все требуют опыт несколько лет коммерческой разработки
И вы требуйте, в чем проблема. "Первое, второе и компот" или как там у вас в дурке кормят.

oilmonster
01.09.2025 09:47Ваш подход называется нестареющей статикой. И со времен явления разного рода приклада для контроля версий перестал быть ценностью. Все нормальные успешные разработчики сегодня использую git или подобные системы, что за собой тащит динамику, многоавторство, сотрудничество и другими плюшками.
А с таким коллегой как вы я бы и дня не хотел бы вместе работать, уж не впечатляет зашкварный подход из 90х, накопленные наработки и гроша ломанного сегодня не стоят без сотрудничества, взаимоуважения и конкуренции за интеллект, а не за способ махать лопатой

aleksandy
01.09.2025 09:47"\r\n".equals(in.readLine())И если проверка не прошла, то что там было в той строке ты никогда не узнаешь.

alex0x08 Автор
01.09.2025 09:47Дааа, это как раз повод для разговора на собеседовании )
И нет, особой проблемы тут нет (кроме таймаута) поскольку пустая строка после полей запроса обязательна, если ее нет - HTTP запрос невалиден.

vic_1
01.09.2025 09:47Похоже на клуб мозахистов :)

Siemargl
01.09.2025 09:47Отсеиваются те, которые без интернет-советника в одном слове делают две ошибки.

alex0x08 Автор
01.09.2025 09:47По такой логике любые тренировки это мазохизм, а знания видимо должны сразу в мозг заливаться, путем копирования ага.
Lazhu
А где нордическая блондинка? )