Java Ready | Программирование
9.24K subscribers
1.45K photos
77 videos
1 file
769 links
Авторский канал по разработке на Java.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c
Download Telegram
Почему split(".") в Java не делит строку по точке?

На первый взгляд кажется, что достаточно передать в split нужный символ:
String[] parts = "app.log".split(".");


Но split принимает регулярное выражение. Точка в regex означает любой символ, поэтому такой вызов не ищет буквальную точку между app и log.

Для настоящей точки её нужно экранировать в регулярном выражении. В Java-строке обратный слеш тоже экранируется, поэтому в исходнике получается два слеша:
String[] parts = "app.log".split("\\.");
System.out.println(Arrays.toString(parts));


Теперь результатом будут app и log.

Та же ловушка есть у других специальных символов regex. Например, вертикальная черта обозначает альтернативу. Когда разделитель приходит из переменной, удобнее не вспоминать все правила экранирования:
String separator = "|";
String[] fields = line.split(Pattern.quote(separator));


Pattern.quote превращает текст разделителя в буквальный шаблон. Если пользователь выберет точку, плюс или квадратную скобку, смысл разделения не изменится.

Есть ещё нюанс в конце строки. По умолчанию split убирает пустые элементы справа:
System.out.println(Arrays.toString("a,b,".split(",")));


Вы увидите только a и b. Если формат данных считает завершающее пустое поле значимым, передайте отрицательный limit:
String[] fields = "a,b,".split(",", -1);


Здесь будет три элемента, последний из них пустая строка.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4🔥4
Почему String.format может заменить точку на запятую?

Представим, что программа сохраняет числовое значение в текстовый файл. Хочется получить 12.50, поэтому пишем:
String value = String.format("%.2f", 12.5);


Но результат без явной локали зависит от настроек среды, в которой запущена JVM. На одной машине это будет 12.50, на другой 12,50. Для интерфейса пользователя локальные правила полезны, а для машинного формата такой сюрприз может испортить импорт.

Разницу легко показать без изменения настроек компьютера:
String de = String.format(Locale.GERMANY, "%.2f", 12.5);


Здесь получится 12,50. Если строку должен читать другой сервис с точкой в качестве разделителя, укажите нейтральную локаль явно:
String stable = String.format(Locale.ROOT, "%.2f", 12.5);


Теперь получится 12.50 независимо от локали пользователя. Это важно для логов с фиксированным форматом.

Для текста, который читает человек, наоборот, можно передать нужную ему локаль:
String label = String.format(userLocale, "%.2f", 12.5);


Так отображение будет привычным для пользователя. Но если вы записываете структурированные данные, лучше использовать подходящий формат и библиотеку, например JSON-сериализатор, а не собирать документ через String.format.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤4🔥3
Проверяем обновления через ETag в Java!

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

В этой задаче:
• добавляем If-None-Match
• используем кеш при ответе 304
• обновляем данные и ETag при ответе 200


Так можно сократить передачу неизменившихся данных. Для полноценного кеша дополнительно понадобятся правила хранения и отдельные записи для разных URL.

👉 Java Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3🔥2
Делаем простой CSV exporter на Java!

Иногда нужно быстро выгрузить данные в CSV. Например, список пользователей, отчёт или результат диагностики.

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

Начнём с модели:
record User(int id, String name, String email) {}


Данные могут выглядеть так:
var users = List.of(
new User(1, "Alice", "alice@mail.test"),
new User(2, "Bob, Jr.", "bob@mail.test")
);


Теперь сделаем функцию экранирования. Если значение содержит запятую, кавычку или перенос строки, оборачиваем его в кавычки:
static String csv(String value) {
if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
return "\"" + value.replace("\"", "\"\"") + "\"";
}

return value;
}


Для обычных значений функция вернёт строку как есть. Для Bob, Jr. получится корректное CSV-значение в кавычках.

Соберём строку пользователя:
static String row(User user) {
return String.join(",",
String.valueOf(user.id()),
csv(user.name()),
csv(user.email())
);
}


Теперь можно собрать весь файл. Сначала добавим заголовок:
var lines = new ArrayList<String>();
lines.add("id,name,email");


После этого добавим строки данных:
for (User user : users) {
lines.add(row(user));
}


И запишем результат в файл:
Files.write(
Path.of("users.csv"),
lines,
StandardCharsets.UTF_8
);


На выходе получится корректный CSV:
id,name,email
1,Alice,alice@mail.test
2,"Bob, Jr.",bob@mail.test


Такую заготовку можно использовать для небольших экспортов без внешних библиотек. Если появляются вложенные структуры, разные форматы дат, большие файлы или сложные правила, лучше взять полноценную CSV-библиотеку.

👉 Java Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3🔥3
Как поддерживать один плагин для Paper и Velocity?

Paper запускает игровой сервер, а Velocity распределяет подключения игроков между серверами.

В статье разбирают:
• как вынести общую логику в отдельный модуль
• зачем платформам нужны собственные адаптеры
• как собрать варианты плагина через Gradle

Общее ядро не зависит от API Paper и Velocity, поэтому одинаковые функции не приходится поддерживать в двух копиях.

Продолжай читать на Habr!


👉 Java Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤2👍2
Собрала чат-бота за вечер → заработала 10 000₽.

Просто представь, кто-то стоит в очереди на маршрутку в 8 утра чтобы успеть на “любимую” работу.

А Алёна в тепле и уюте…
собирает бизнесу чат-ботов со своего ноутбука. Без привязки к месту и графику получает европейскую зарплату.

Без сложных задач.
Без многолетнего обучения.
Без дорогого ноутбука.

Работа проста:
Берёшь клиента → Собираешь бота по шаблону → Наставник всё проверяет → Сдаёшь работу и получаешь деньги.

Новички без опыта обычно выходят на 50–80 тыс ₽ в месяц,
и за год легко вырастают до 200 тыс ₽ и выше.

Работаешь в удобное тебе время, поэтому легко совмещать с текущей работой, учёбой, декретом…

👉 Пройди бесплатное обучение, и ты поймёшь, что это не просто слова

Бесплатных мест осталось 9.
👎10😁1
Почему нельзя просто проигнорировать InterruptedException?

Поток может ждать завершения задачи, блокировки или таймера. Другой поток вправе попросить его остановиться через interrupt. Например, worker делает паузу между проверками:
while (!Thread.currentThread().isInterrupted()) {
processNext();
Thread.sleep(1000);
}


Если вызвать interrupt во время sleep, метод выбросит InterruptedException. При этом флаг прерывания будет очищен. Это важный момент: после перехвата исключения простая проверка isInterrupted уже не увидит запрос на остановку.

Такой обработчик теряет сигнал:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("continue");
}


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

Если метод может передать исключение выше, оставь его в сигнатуре:
void pause() throws InterruptedException {
Thread.sleep(1000);
}


Тогда решение об остановке примет вызывающий уровень. А если исключение приходится перехватить внутри Runnable или другого метода без throws, восстанови флаг и заверши работу:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}


Восстановленный флаг увидит внешний код, который проверяет состояние потока.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3❤2
🔴В 2025 году Россияне заработали 5,3 млрд рублей на арбитраже криптовалют

По данным Минфин 32% граждан покинули своё место работы поскольку уже в первый месяц смогли заработать 250 000 рублей, а через 2-3 месяца с легкостью вышли на семизначные числа.

Самое смешное, что лишь 8% россиян что-то слышали об этой профессии хотя все инструкции бесплатно лежат на канале Арбитраж Криптовалют

Там объясняют, как имея телефон и доступ в интернет заработать свои первые $3000. Причём разжёвывают так доступно, что поймёт даже ребенок. Кстати, на канале сегодня вышла новая рабочая связка!

Ссылка для входа — https://t.me/+gL6CZ_b93PBhZGM6
Please open Telegram to view this post
VIEW IN TELEGRAM
👎7😁2
Почему equals у массивов в Java не сравнивает содержимое?

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

Но массивы не переопределяют equals для содержимого. Обычный equals у массива работает почти как сравнение ссылок.

Например, два массива с одинаковыми числами не будут равны через equals:
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};

System.out.println(a.equals(b));


Результат будет false, потому что это два разных объекта в памяти.

Для одномерных массивов нужно использовать Arrays.equals:
System.out.println(Arrays.equals(a, b));


Теперь Java сравнит элементы по порядку и вернёт true.

С массивами объектов логика такая же:
String[] left = {"java", "ready"};
String[] right = {"java", "ready"};

System.out.println(Arrays.equals(left, right));


Но для вложенных массивов Arrays.equals уже недостаточно. Он сравнит внутренние массивы как объекты.

Для двумерных структур нужен deepEquals:
int[][] x = {{1, 2}, {3, 4}};
int[][] y = {{1, 2}, {3, 4}};

System.out.println(Arrays.deepEquals(x, y));


То же самое касается вывода. Обычный toString у массива покажет служебное имя, а не элементы.

Для нормального вывода используют Arrays.toString:
System.out.println(Arrays.toString(a));


Если массив вложенный, нужен deepToString:
System.out.println(Arrays.deepToString(x));


Массивы в Java стоит сравнивать и выводить через Arrays. Иначе код может выглядеть логично, но проверять совсем не то.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤3👍3
Шпаргалка по загрузчикам классов в Java!

На картинке показаны Bootstrap, Platform, Application и пользовательский ClassLoader. Для каждого отмечено, какие классы он обычно загружает и как связана их видимость.

Сохрани, чтобы не потерять!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤3👍3
Знали, что Map.getOrDefault может вызвать загрузку, даже когда ключ уже есть?

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

Поэтому дорогой вызов справа выполнится заранее:
User user = users.getOrDefault(id, loadUser(id));


Даже если users уже содержит id, loadUser(id) успеет выполниться. Если это запрос к базе, сеть или вычисление, приложение потратит время зря.

Для ленивого вычисления значения подойдёт computeIfAbsent:
User user = users.computeIfAbsent(id, this::loadUser);


Функция загрузки вызывается только тогда, когда ключ отсутствует или связан с null. Возвращённое ненулевое значение добавляется в map и дальше берётся из неё.

Проверь и семантику отсутствующего результата. Если загрузчик вернёт null, запись не появится, и следующий вызов снова попробует загрузить пользователя:
User user = users.computeIfAbsent(id, key -> findUser(key).orElse(null));


Ещё один нюанс касается конкурентного доступа. Для параллельных потоков выбирай подходящую concurrent map и не помещай в функцию долгую блокирующую работу без понимания гарантий конкретной реализации.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍3❤2