Уголок сельского джависта
СтатистикаПишу на Java и Kotlin, иногда полную ерунду, люто топлю за Spring и чистую архитектуру. Консультирую. Чат: https://t.me/shurik_codes_group
- Последний пост
- 23 июл.
- Последнее чтение
- 15 авг.
- Постов за неделю
- 0
- Всего постов
- 22
- Тип
- открытый
- Язык
- русский
- Категория
- Технологии (по похожим)
- В каталоге с
- 14 авг.
- 1/24сутки в ленте
- 1 046
- 1/48двое суток
- 1 198
- 1/72трое суток
- 1 292
Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.
Посты
⏰📆 JSR 310: Instant Класс java.time.Instant описывает точное время и работает с метками времени в формате UTC. Для большинства технических задач, например, хранения, логгирования и аудита, Instant подходит лучше всего. 📖 Читать на сайте 📖 Читать в VK 📖 Читать в Telegra.ph 📖 Читать в Дзене #java #jsr310
⏰📆 JSR 310: Источники времени Прежде чем переходить к более подробному описанию классов, описывающих метки времени, неплохо было бы упомянуть источники времени, хотя на практике они используются не так часто. Источники времени представлены интерфейсом InstantSource и его основной реализацией — Clock. 📖 Читать на сайте 📖 Читать в VK 📖 Читать в Telegra.ph #java #jsr310
⏰📆 Введение в JSR 310: Дата и время в Java Для работы с датой и временем в стандартной библиотеке Java существует два набора API: старый, включающий в себя такие классы, как Date и Calendar, и новый, опирающийся на стандарт ISO 8601 и включающий в себя классы из пакета java.time. В настоящее время разработчиками платформы Java рекомендуется использование новых API для работы с датой и временем. 📖 Читать на сайте 📖 Читать в VK 📖 Читать в Telegra.ph 📖 Читать в Дзене #java #jsr310
🎄Друзья! Я поздравляю всех вас с наступающим новым 2026 годом! Искренне желаю вам успехов и удачи во всех ваших начинаниях, чтобы все поставленные цели были достигнуты, профессионального роста и всего самого-самого наилучшего! Да, в этом году времени на блог у меня не было, к сожалению, но я очень надеюсь на то, что в наступающем году это самое время и мотивация будут. Всем добра!
без подписи
🐈⬛ Про пет-проекты В затянувшемся творческом перерыве решил смахнуть цифровую пыль с одной из своих самых любимых видеоигр - Elite: Dangerous, чтобы докачать наконец-то ранг исследователя до Элиты (каково же было моё удивление, когда я узнал, что рангов Элиты теперь 6) и вернуться в обитаемый пузырь, а то последние два года у меня длится очередная кругосветка. Процесс исследования звёздных систем требует времени, особенно, если есть желание отлететь куда подальше от обитаемого пузыря. А чтобы исследование звёздной системы проходило быстрее, нужно знать, какие планеты в системе представляют наибольший интерес, какое вознаграждение можно получить за систему, и сколько вообще исследовательских данных накоплено. Игра предоставляет далеко не всю информацию, зато пишет очень подробный лог внутри-игровых событий в формате JSON. И вот тут у меня созрела идея написать очередной пет-проект - систему сбора и анализа логов. Пара-тройка вечеров и данные из логов попадают в базу данных. Хотел ещё добраться до веб-интерфейса на основе Vue/Vuetify, но в итоге остановился на Grafana, для текущих задач вполне достаточно. И в процессе написания этого мини-проекта пришло понимание того, что для пет-проекта важно, чтобы разработчик был реальным пользователем, а сам пет-проект решал реальные задачи, пусть и в виртуальном мире. С точки зрения опыта разработки такой пет-проект будет намного важнее и полезнее, чем тот, в котором собраны все возможные технические решения, но не решающие каких-либо задач. P.S.: в открытый доступ проект, возможно, когда-нибудь попадёт Прикладываю виртуальный подорожник к Elite: Dangerous, а то и на выходных не получалось подключиться, и со вчерашнего дня какие-то проблемы на стороне AWS (заменили SRE и DevOPS-инженеров на AI-агентов и прилегли).
✅ Гибкие конструкторы JDK 25 привнесёт ещё одно небольшое, но важное, на мой взгляд, изменение в язык программирования Java - гибкие конструкторы (JEP 513). Цель изменения - позволить выполнять действия в конструкторе класса-наследника до вызова конструктора родительского класса. Представим себе достаточно банальную ситуацию: нам нужно создать экземпляр некоторого класса-наследника, но в процессе создания нужно провалидировать переданные в аргументах данные. До Java 25 сделать это можно только после вызова конструктора родительского класса: class Vehicle { int wheels; Vehicle(int wheels) { this.wheels = wheels; } } class Car extends Vehicle { Car(int wheels) { super(wheels); // Всякие Reliant-ы и Morgan Super 3 - // это трайки и автоциклы if (wheels < 4) { throw new IllegalArgumentException(); } } } Проблема такого кода заключается в том, что в конструкторе родительского класса может находиться какая-то ресурсоёмкая логика, которая будет выполнена зря. Да, валидацию можно вынести в дополнительные методы класса, но выглядеть это будет не всегда красиво и удобно: class Vehicle { int wheels; Vehicle(int wheels) { this.wheels = wheels; } } class Car extends Vehicle { Car(int wheels) { super(wheelsMustBeAtLeastFour(wheels)); } static int wheelsMustBeAtLeastFour(int wheels) { if (wheels < 4) { throw new IllegalArgumentException(); } return wheels; } } Это будет так, даже если попытаться шаблонизировать код: class Assertions { static <T, E extends RuntimeException> T require(T value, Predicate<T> predicate, Supplier<E> exceptionSupplier) { if (predicate.test(value)) { return value; } throw exceptionSupplier.get(); } } class Vehicle { int wheels; Vehicle(int wheels) { this.wheels = wheels; } } class Car extends Vehicle { Car(int wheels) { super(Assertions.require(wheels, v -> v >= 4, IllegalArgumentException::new)); } } С Java 25 необходимые манипуляции с аргументами можно выполнять до вызова конструктора родительского класса: class Vehicle { int wheels; Vehicle(int wheels) { this.wheels = wheels; } } class Car extends Vehicle { Car(int wheels) { if (wheels < 4) { throw new IllegalArgumentException(); } super(wheelsMustBeAtLeastFour(wheels)); } } На мой взгляд, выглядит красивее и проще. Но не стоит забывать о том, что до вызова конструктора класса-родителя нельзя обращаться к this, методам и инициализированным свойствам текущего экземпляра. Как вам нововведение? #jdk25 #java
✅ Сборщики стримов Одним из наиболее важных нововведений, на мой взгляд, в стандартной библиотеке в грядущем релизе JDK 25 станут сборщики стримов (JEP 485 Stream Gatherers). Да, стабильными они стали ещё в JDK 24, но для себя я рассматриваю только LTS-релизы. Сборщики стримов описываются типизированным интерфейсом Gatherer<T, A, R>, где T - тип входящего элемента стрима, R - тип выходящего элемента, а A - потенциально изменяемое состояние сборщика. Внутри Gatherer используются четыре компонента: - Supplier<A> initializer - инициализатор состояния (если оно используется) - Integrator<A, T, R> integrator - интегратор, обрабатывающий полученный элемент, опционально использующий состояние. Так же существует жадный интегратор - Integrator.Greedy, который принимает все элементы стрима и передаёт элементы нисходящему стриму только, если тот их может принять. Жадные интеграторы не должны порождать бесконечные стримы. - BinaryOperator<A> - функция, принимающая два промежуточных состояния и объединяющая их - BiConsumer<A, Downstream<? super R>> finisher - функция, принимающая последнее промежуточное состояние и нисходящий стрим (даунстрим - стрим, который получается после применения сборщика) Создать сборщик стрима можно при помощи метода Gatherer.of (для сборщиков без состояния) или Gatherer.ofSequential (для сборщиков с состоянием), передав в качестве аргумента, как минимум, функцию-интегратор или все вышеуказанные компоненты. Состояние позволяет реализовывать логику, опирающуюся на ранее обработанные элементы стрима, например исключение дублирующихся элементов из стрима. Для применения сборщика к стриму нужно передать его в промежуточном метода Stream.gather(). Ниже приведён пример сборщика, который для каждого элемента вычисляет сумму всех предыдущих элементов и передаёт её в нисходящий стрим: // выведет 1, 3, 7, 12, 18 IO.println( Stream.of(1, 2, 4, 5, 6) .gather(Gatherer.ofSequential(AtomicInteger::new, Gatherer.Integrator.ofGreedy( (state, in, downstream) -> downstream.push(state.addAndGet(in))))) .map(String::valueOf) .collect(Collectors.joining(", ")) ); При помощи сборщиков можно реализовывать и стандартные операции, но с большим контролем, например комбинируя, map() и filter(): Stream.of(1, 2, 3, 4, 5) // Оставит только чётные числа и преобразует их в строку в бинарном формате .gather(Gatherer.of( (_, num, downstream) -> { if (num % 2 == 0) { return !downstream.isRejecting() && downstream.push(Integer.toBinaryString(num)); } else { return !downstream.isRejecting(); } })) .forEach(IO::println); /* Выведет 10 100 */ Кроме этого в стандартную библиотеку добавлен класс Gatherers, предоставляющий стандартные сборщики: - Gatherers.fold - сборщик, реализующий свёртывание элементов стрима в один - Gatherers.scan - сборщик, реализующий сканирование префикса - инкрементное накопление, когда каждый следующий элемент содержит данные предыдущих - Gatherers.mapConcurrent - сборщик, реализующий конкурентное преобразование элементов стрима - Gatherers.windowFixed - сборщик, агрегирующий элементы стрима в окна фиксированного размера - Gatherers.windowSliding - сборщик, агрегирующий элементы стрима в плавающие окна фиксированного размера Вполне вероятно, что стандартные промежуточные операции в будущем могут быть переписаны на сборщики стримов. Как вам нововведение? #jdk25 #java
🎉 Всех причастных с праздником! Добивайтесь профессиональных высот, не оставайтесь на достигнутом и поменьше багов вам!
✅ Переменные с областью действия Ещё одно интересное нововведение, которое ждёт нас в грядущем релизе JDK 25 — переменные с областью действия (JEP 506). Наверняка многие сталкивались с ситуацией, когда нужно передать какую-то информацию между двумя компонентами, но при этом по каким-то причинам не хочется добавлять новые аргументы в методы или свойства в классы. Зачастую такая задача решалась при помощи ThreadLocal - локальных переменных потока. В одном месте данные добавлялись в локальные переменные потока, а в другом - извлекались. Но у локальных переменных потоков есть свои недостатки и достаточно существенные. И именно недостатки ThreadLocal побудили разработчиков JDK придумать новый инструмент, позволяющий объявлять переменные в некоторой области действия. Переменные с областью действия находились в разработке с JDK 20 и к релизу JDK 25 стали стабильными. Переменные с областью действия описываются типизированным классом ScopedValue<T>, где тип соответствует типу данных переменной. Данный класс предоставляет следующие методы: - newInstance() — метод класса для создания переменной с областью действия - where() — метод класса для присвоения значения и получения его носителя - get() — для получения значения переменной для текущей области действия - isBound() — для проверки наличия присвоенного значения - orElse() — для получения текущего значения или альтернативного - orElseThrow() — для получения текущего значения или выбрасывания исключения Носитель значения описывается классом ScopedValue.Carrier и предоставляет следующие методы: - get() — для получения значения для текущей области действия - where() — для присвоения нового значения и получения дочернего носителя Пример использования переменных с областью действия: // Создание новой строковой переменной с областью действия ScopedValue<String> scopedValue = ScopedValue.newInstance(); // Присваивание значения и получение носителя ScopedValue.Carrier carrier = ScopedValue.where(scopedValue, "foo"); // Выполнение кода с передачей переменной carrier.run(() -> { // Здесь значение переменной - foo // Выведет foo IO.println(scopedValue.get()); // Присваивание значения и получение нового носителя ScopedValue.where(scopedValue, "bar").run(() -> { // А здесь значение переменной - bar // Выведет bar IO.println(scopedValue.get()); }); // В этой области действия значение осталось прежним - foo // Выведет foo IO.println(scopedValue.get()); }); // К текущему значению можно получить доступ через носитель // Выведет foo IO.println(carrier.get()); // Так делать нельзя, так как для текущей области действия // значение не назначено, и будет выброшено // исключение NoSuchElementException scopedValue.get(); #jdk25 #java
✅ Импорт модулей в Java 25 Ещё одним нововведением в языке программирования Java в грядущем релизе JDK 25 является импорт модулей (JEP 511). Ещё в JDK 9 была реализована система модулей платформы Java (JPMS - Java Platform Module System), а классы стандартной библиотеки были распределены между различными модулями. Однако это никак не повлияло на написание кода, а вся работа с JPMS сводилась к файлу module-info.java. Теперь же с 25 версии языка программирования Java появится возможность импорта не только отдельных классов или классов, находящихся в одном пакете, а всех публичных классов определённого модуля. В следующем примере кода импортируется четыре класса из пакета java.util и его подпакетов: package example; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; class Application { static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0; static final Function<Integer, String> INT_TO_STRING = Object::toString; static void main() { String numbers = List.of(1, 2, 3, 4, 5, 6) .stream() .filter(IS_EVEN) .map(INT_TO_STRING) .collect(Collectors.joining(", ")); } } Вместо этого можно импортировать модуль java.base, который экспортирует вышеуказанные пакеты. Для этого нужно использовать специальный оператор import module: package example; import module java.base; class Application { static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0; static final Function<Integer, String> INT_TO_STRING = Object::toString; static void main() { String numbers = List.of(1, 2, 3, 4, 5, 6) .stream() .filter(IS_EVEN) .map(INT_TO_STRING) .collect(Collectors.joining(", ")); } } С полным списком экспортируемых пакетов модулем java.base можно ознакомиться в документации. Однако, стоит помнить, что разные модули могут экспортировать классы с одним и тем же названием. Так, например, java.base и java.desktop экспортируют класс List. В этом случае нужно конкретизировать, какой класс нужно импортировать: package example; // экспортирует java.util.List import module java.base; // экспортирует java.awt.List import module java.desktop; import java.util.List; class Application { static void main() { List.of(1, 2, 3, 4); } } Обратите своё внимание на то, что при импорте модулей не требуется использовать JPMS в проекте и настраивать файл module-info.java. #jdk25 #java
✅ Компактные файлы исходных кодов в Java 25 С релизом JDK 25, который произойдёт уже на следующей неделе - 16 сентября, стабильными станут компактные файлы исходных кодов и главные методы (JEP 512), которые были в разработке начиная с JDK 21. Все мы знаем, что любая программа, написанная на языке программирования Java, начинает выполнение с класса с main-методом, который в общих чертах имеет следующую форму: class Application { public static void main(String[] args) { System.out.println("Hello world!"); } } И у людей, только начинающих изучение языка программирования Java, возникает сразу много вопросов: "А что такое класс и зачем он нужен?", "Зачем все эти public static void?" и т.д. И ведь во многих других языках программирования для обычного "Hello world" действительно нужно меньше действий. Вот пример на Kotlin: fun main() { println("Hello world!") } А вот пример на Rust: fn main() { println!("Hello world!"); } С релизом Java 25 можно будет убрать класс, модификаторы public и static, а так же аргументы: void main() { IO.println("Hello world!"); } В этом случае class и package объявляются неявно, и если файл Application.java с main-методом находится в пакете example, то описанный выше код будет эквивалентен следующему: package example; public class Application { public static void main(String[] args) { System.out.println("Hello world!"); } } Кроме этого в компактных файлах исходных кодов автоматически импортируются все публичные классы из пакетов, экспортируемых из модуля java.base, что значительно упрощает написание простых программ: void main() { Function<Integer, String> intToStringFunction = Object::toString; Predicate<Integer> isEvenPredicate = i -> i % 2 == 0; List<String> numbers = Stream.of(1,2,3,4,5,6) .filter(isEvenPredicate) .map(intToStringFunction) .toList(); IO.println("Numbers are: " + numbers);// Numbers are: [2, 4, 6] } Так же компактный файл исходных кодов кроме метода main может содержать другие методы и свойства класса и экземпляра: static final String NUMBERS_ARE = "Numbers are: "; void main() { Function<Integer, String> intToStringFunction = Object::toString; List<String> numbers = Stream.of(1, 2, 3, 4, 5, 6) .filter(this::isEven) .map(intToStringFunction) .toList(); IO.println(NUMBERS_ARE + numbers);// Numbers are: [2, 4, 6] } private boolean isEven(Integer number) { return number % 2 == 0; } Но вместе с этим изменилась и минимальная сигнатура полноценного класса с main-методом, и теперь она выглядит следующим образом: package example; class Application { static void main() { } } Как вам нововведение? #jdk25
Java: Виртуальные потоки Одним из наиболее значимых нововведений в Java 21 стала стабильная поддержка виртуальных потоков. Предлагаю в этой статье разобраться с тем, почему нам нужны виртуальные потоки, как их использовать и какие подводные камни могут нас ожидать при их применении. 📖 Читать в блоге 📖 Читать в VK 📖 Читать на Telegra.ph #java 💬 Обсудить в Telegram: https://t.me/+UFAkw187WstX0wqy Поддержать: 💵 На карту T: https://www.tinkoff.ru/cf/4PEOiVCZQuS 💵 В VK https://vk.com/donut/shurik.codes 💵 В Boosty https://boosty.to/akosarev/donate 💵 ЮМани 5599 0050 8286 9475
Ровно 13 лет назад, 9 апреля 2012 года, я переступил порог компании "Maxim Technology" (@ooo_technology), будучи зелёным джуном. Когда я шёл на собеседование, ожидал увидеть типичный ИТ-отдел компании провинциального уровня с десятком айтишников в свитерах и очках, а увидел просторный, залитый светом офис с удобными и современными рабочими местами и коллектив, состоящий из пары десятков профессионалов, частью которого мне посчастливилось стать. Тогда я ещё был веб-разработчиком (да, PHP, MySQL, HTML, JavaScript, CSS и прочие радости веба), но уже с опытом разработки на Java 6 со Spring Framework 3, Java EE и прочими томкатами. Кто бы мог подумать, но за прошедшие годы из джуна я вырос до техлида, освоил и внедрил в разработку огромное количество инструментов. Решённых задач уже не счесть, да и не важно это. Было ли желание уйти? Было. Были ли причины уйти? Были. Но моя лень сделала своё дело, и всех своих целей я добился не меняя место работы. Спасибо компании за предоставленные возможности и коллегам за невероятно интересный рабочий процесс.
Друзья, всем снова привет! Моим коллегам из федеральной сети фитнес-клубов "Брайтфит" нужен в команду Java-разработчик уровня middle+. Брайтфит - это больше 200 тыс. клиентов в год, 2 Страны, 14 городов, 34 фитнес клуба, больше 1 тыс. тренеров-партнеров. За подробностями обращаться к @Viacheslav_st1.
Всех причастных с праздником! #404
Друзья, всем привет! Мне в команду в компании @ooo_technology нужен толковый разработчик на Kotlin уровня middle или senior. Ну или джавист, готовый погрузиться в Kotlin. Задачи интересные, связанные с телефонией, работать можно удалённо или в офисе, подробнее тут: https://spb.hh.ru/vacancy/117519653?hhtmFrom=employer_vacancies
▶️ Основы Apache Kafka Информационные системы в процессе своей работы порождают большое количество событий, которые могут быть связаны как с самой системой, так и с реализуемой предметной областью. Но созданные события нужно доставить до получателей - других компонентов информационной системы, и в этом случае на помощь приходят брокеры очередей сообщений и стриминговые системы. В этом ролике речь пойдёт о самой популярной системе стриминга событий - Apache Kafka и будут описаны следующие темы: - Краткое описание стримов данных - Терминология Kafka - Локальный запуск Apache Kafka - Использование утилит CLI для отправки и получения сообщений - Отправка и получение сообщений в проекте на Java ▶️ Смотреть в VK ▶️ Смотреть в Rutube ▶️ Смотреть в Дзене ▶️ Смотреть на Платформе ▶️ Смотреть на YouTube ▶️ Смотреть в Boosty ▶️ Смотреть в NUUM #kafka #java 💬 Обсудить в Telegram: https://t.me/+UFAkw187WstX0wqy Поддержать: 💵 На карту T: https://www.tinkoff.ru/cf/4PEOiVCZQuS 💵 В VK https://vk.com/donut/shurik.codes 💵 В Boosty https://boosty.to/akosarev/donate 💵 ЮМани 5599 0050 8286 9475
Заждались? Уже сегодня)
📖 Получение сообщений из Kafka Получение сообщений из Kafka на первый взгляд выглядит достаточно просто, но на деле обладает большим количеством нюансов, связанных с членством получателя в группе, её перебалансировкой, получением ранее отправленных сообщений, фиксацией смещений и семантиками доставки. Все эти вопросы я постарался осветить в этой статье. 📖 Читать на сайте 📖 Читать в VK 📖 Читать в Telegraph #kafka 💬 Обсудить в Telegram: https://t.me/+UFAkw187WstX0wqy Поддержать: 💵 На карту T: https://www.tinkoff.ru/cf/4PEOiVCZQuS 💵 В VK https://vk.com/donut/shurik.codes 💵 В Boosty https://boosty.to/akosarev/donate 💵 ЮМани 5599 0050 8286 9475