12073
Присоединяйтесь к нашему каналу и погрузитесь в мир для Java-разработчика Связь: @devmangx РКН: https://clck.ru/3H4WUg
Java-совет: начиная с Java 14 можно использовать switch-выражения, чтобы писать многоветвистую логику короче и чище.
// Old way:
String season;
switch (month) {
case 12:
case 1:
case 2:
season = "Winter";
break;
case 3:
case 4:
case 5:
season = "Spring";
break;
default:
season = "Invalid";
}
// New switch expression:
String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
default -> "Invalid";
};
Совет по Java: если нужна максимально хорошая производительность в общем случае, бери HashMap как реализацию Map.
✅HashMap внутри реализован как хеш-таблица, и в среднем put(), get() и remove() работают за O(1).
🪲Он не потокобезопасный (если нужна thread-safety, используй ConcurrentHashMap).
Пример:
Map<String, Integer> ages = new HashMap<>();
// Добавляем данные
ages.put("Michael", 31);
ages.put("Lisa", 25);
ages.put("Alice", 42);
// Поиск
System.out.println("Возраст Lisa: " + ages.get("Lisa"));
// Удаление
ages.remove("Alice");
// Итерация
for (Map.Entry<String, Integer> entry : ages.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
Java tip : старайся не шарить данные между потоками.
✅Используй immutable-объекты
✅Или пусть потоки общаются сообщениями, не лезя напрямую в общий state и не мутируя его
// With immutable objects:
record Book(String title, int price) {} //Immutable
public class BookJob implements Runnable {
private final Book book;
....//constructor
@Override
public void run() {
System.out.println(book.title() + " " + book.price());
}
}
// Exchanging messages:
...
new Thread(() -> {
try {
queue.put("mess1");
} catch (InterruptedException e) {}
}).start();
...
new Thread(() -> {
try {
String mess = queue.take();
} catch (InterruptedException e) {}
}).start();
Spring Boot: на этапе разработки используй spring.main.lazy-initialization=true, чтобы ускорить старт приложения.
По умолчанию Spring Boot жадно инициализирует все бины на старте. В дев-окружении это значит:
1. Дольше стартует, особенно в больших проектах
2. Приходится ждать загрузки бинов, которые тебе прямо сейчас не нужны
Чтобы этого избежать, в application.properties поставь:
spring.main.lazy-initialization=true
👩💻 В сеть вывалилась гигантская куча курсов и книг
Держи сотни гигабайт свежих уроков, и каждую неделю мы подкидываем ещё!
• 1612 ГБ — DevOps
• 1402 ГБ — Python
• 1300 ГБ — C, C++
• 1815 ГБ — Frontend
• 1515 ГБ — Backend
• 898 ГБ — ИБ, Хакинг
• 996 ГБ — Kotlin, Swift
• 212 ГБ — JavaScript
• 315 ГБ — Flutter
• 820 ГБ — Go, PHP
• 419 ГБ — Java, Rust
• 648 ГБ — GameDev
• 517 ГБ — Windows, Linux
• 998 ГБ — Дизайн (UX/UI)
• 617 ГБ — Нейросети (ML/RL)
• 546 ГБ — БД (SQL & NoSQL)
• 687 ГБ — Аналитика данных
• 115 ГБ — QA-тестирование
Подписывайся и не плати за то, что можно получить бесплатно
IntelliJ IDEA исполняется 25 лет! 🎉
👉 Java Portal
Про модификаторы доступа в Java (public / default / protected / private)
private - только для себя
public - откуда угодно
protected - вроде бы для подклассов?
На самом деле тут всегда начинается каша, если не смотреть на вопрос "откуда и кто вызывает" по двум осям: пакет и наследование.
Чтобы нормально понять модификаторы, я подумал: пусть будет статья с моделью "якинику-ресторан".
Что такое модификаторы на самом деле:
Модификаторы - это не про "силу".
Это правила, откуда можно получить доступ.
В Java доступ всегда определяется двумя вещами:
* тот же пакет?
* есть наследование?
Модель с рестораном якинику:
* YakinikuShop: сам ресторан (родительский класс)
* LocalStaff: персонал этого же ресторана
* Manager: управляющий (дочерний класс)
* BranchStaff: персонал филиала (другой пакет + наследник)
* Customer: обычный клиент
Родительский класс: YakinikuShop.java
package shop;
public class YakinikuShop {
public int counter = 30; // Стойка/места: видно всем
int staffRoom = 10; // Комната персонала: только для своего пакета
protected int kitchen = 20; // Кухня: свой пакет или наследники
private int safeMoney = 100; // Сейф: даже управляющему напрямую нельзя
public void askManagerToOpenSafe(Object person) {
if (!(person instanceof Manager)) {
System.out.println("❌ Только управляющий может открыть сейф");
return;
}
openSafe();
}
private void openSafe() {
System.out.println("open safe: " + safeMoney);
}
}
Залетел полезняк для тех, кто готовится к собесам по Java или просто хочет быстро освежить базу.
На InterviewPrep выложили Java Notes (по сути мини-гайд/конспект) и там закрывают самые частые темы:
история Java
JDK / JRE / JVM (как это вообще устроено)
Collections
управление памятью
многопоточность
обработка исключений
типы классов
типы данных и ключевые слова
Объясни, какие паттерны проектирования Spring Boot использует внутри.
На собеседовании обычно проверяют не то, знаешь ли ты названия паттернов.
Скорее хотят понять, понимаешь ли ты, как Spring Boot архитектурно на них построен.
1. Singleton
Spring Boot бины по умолчанию singleton.
Когда ты помечаешь класс @Component, @Service или @Repository, Spring по дефолту создаёт один инстанс.
Что можно сказать:
"Все Spring-бины по умолчанию singleton, если явно не указать другой scope, например @RequestScope. Это экономит память и держит объектный граф консистентным и разделяемым."
2. Proxy
Spring AOP (аспектно-ориентированное программирование) работает через прокси.
3. Observer
Система событий Spring это Observer в чистом виде.
4. Factory
Spring везде использует фабрики.BeanFactory, ApplicationContext, FactoryBean.
Ты объявляешь бин, а Spring решает, когда и как его создать.
5. Builder
Используется в клиентах вроде RestTemplateBuilder, WebClient.Builder.
6. Template Method
Очень активно используется в JdbcTemplate, RestTemplate и т.д.
Что можно сказать:
"Мне не нужно париться про управление соединением и обработку исключений. Это делает шаблон, а я просто подставляю свою конкретную логику."
👉 Java Portal
Реализация гексагональной архитектуры на Java
В данной статье рассматривается архитектура проекта, позволяющая модульным образом интегрировать инфраструктурные фреймворки, такие как Spring, Quarkus и Micronaut, без необходимости модификации ядра предметной области (domain) или внешних API.
👉 Java Portal
В старые времена на Java самое дорогое выражение, которое я видел в проде, выглядело невинно:
→ repository.findAll()
Компилируется, тесты проходит, на QA тоже всё ок потому что данных мало.
А потом в проде прилетает реальный трафик, JVM начинает пухнуть от хипа и p99 улетает в космос. Причина простая — вызов неограниченный по объёму. Без лимитов, без пагинации, без стриминга.
показательный случай про цену подобных ошибок:
• ракета Ariane 5
• стоимость проекта — 370 млн
• в коде было преобразование 64-битного значения к 16-битному
• при ускорении значение вышло за пределы
• произошёл overflow, навигация отказала
• запуск провалился, 370 млн просто улетели
Вывод: неограниченные операции в коде — всегда риск.
Из той же категории:
• collect(toList()) на огромных стримах
• @Transactional прямо на контроллерах
• synchronized на нагруженном месте
👉 Java Portal
JetBrains вбросили загадку про IntelliJ IDEA. Судя по всему, кто-то пытается незаметно попасть на их день рождения. Команда решила устроить мини-квест: каждый день будет новая задачка, новые подсказки и в итоге надо вычислить тайного гостя вечеринки. 😂
В первом задании предлагают сопоставить сниппеты кода с версиями Java. Там набор вполне узнаваемых фич: var, streams, diamond-оператор, enum и records. Ниже варианты: Java 5, 7, 8, 11, 17 и 21.
Выглядит легко, но подана история забавно, а оформление в стиле карты-загадки.
👉 Java Portal
Что такое Throwable
Throwable в Java — это базовый класс для всех «бросаемых» аномалий. Он даёт общий функционал для работы с исключениями: стек, причину, вложенные ошибки и т. д.
С точки зрения модели: невосстанавливаемые критические состояния представлены через Error, а ошибки уровня приложения — через Exception.
То есть всё, что можно бросать (throw), наследуется от Throwable.
Коротко:
Error: обычно невосстанавливаемая и не предполагается обработка на уровне приложения
Exception: приложение может предусматривать и обрабатывать
checked: обработка контролируется компилятором
unchecked: обработка не принудительная
Иерархия выглядит примерно так:
java.lang.Object
└─ java.lang.Throwable // всё, что можно throw-нуть
├─ java.lang.Error // системные критические сбои
│ ├─ OutOfMemoryError
│ ├─ StackOverflowError
│ ├─ VirtualMachineError
│ └─ ...
└─ java.lang.Exception // исключения уровня приложения
├─ RuntimeException // unchecked
│ ├─ NullPointerException
│ ├─ IllegalArgumentException
│ ├─ IndexOutOfBoundsException
│ └─ ...
└─ IOException, SQLException (checked)
Поток обработки запроса в Spring Boot
Вопрос на собеседовании: как проходит HTTP-запрос внутри Spring Boot приложения?
Звучит просто, но на практике сложно правильно восстановить порядок и технические детали.
• клиент отправляет HTTP-запрос
• запрос сначала попадает в DispatcherServlet (Front Controller)
• DispatcherServlet через HandlerMapping ищет нужный контроллер
• контроллер занимается маппингом запроса и валидацией
• бизнес-логика выполняется в сервисном слое
• работа с базой идет через репозиторий (JPA)
• ответ идет обратно по тому же пути и сериализуется в JSON через Jackson
👉 Java Portal
Многопоточность позволяет писать так, чтобы несколько задач выполнялись параллельно внутри одного приложения.
Чтобы использовать многопоточность (то есть писать многопоточный код), нужен класс java.lang.Thread.
Стадии жизненного цикла потока:
• New: Новый поток начинает жизнь в состоянии new. Он остается там, пока программа не запустит поток.
• Runnable: После запуска новый поток становится runnable. В этом состоянии считается, что поток выполняет свою работу.
• Waiting: Иногда поток переходит в waiting, когда ему нужно дождаться выполнения задачи другим потоком.
• Timed Waiting: Поток из runnable может перейти в timed waiting на определенный промежуток времени. После истечения этого времени он снова возвращается в runnable.
• Terminated: Поток попадает в terminated после завершения задачи или принудительного завершения.
Приоритеты потоков:
Каждый Java-поток имеет приоритет, который помогает ОС решать порядок планирования потоков.
• MIN_PRIORITY (константа 1)
• NORM_PRIORITY (константа 5) (приоритет по умолчанию)
• MAX_PRIORITY (константа 10)
Базовые способы создать поток в Java:
• Реализовать интерфейс Runnable
• Наследоваться от Thread
Методы потоков:
• public void start()
Запускает поток в отдельном пути выполнения и затем вызывает run() у этого объекта Thread.
• public void run()
Если объект Thread был создан с отдельным Runnable, то run() будет вызван на этом Runnable.
• public final void setPriority(int priority)
Устанавливает приоритет потока. Возможные значения от 1 до 10.
• public void interrupt()
Прерывает поток, заставляя продолжить выполнение, даже если он был заблокирован.
• public final boolean isAlive()
Возвращает true, если поток "жив", то есть после старта и до завершения.
Это были базовые вещи. Пост длинный, но надеюсь, что что-то новое унес с собой.
👉 Java Portal
Если ты изучаешь Java или хочешь прокачать навыки, обрати внимание на JBook — это интерактивная книга по Java, доступная прямо на GitHub.
Что внутри:
🔸Подробное объяснение основ языка Java;
🔸Примеры кода с пояснениями;
🔸Упражнения для закрепления знаний;
🔸Поддержка Jupyter Notebook + Java (IJava kernel) — можно запускать код прямо в браузере
GitHub: https://github.com/qcha/JBook
👉 Java Portal
10 золотых правил чистого кода:
1. Избегай магических чисел и строк
2. Используй осмысленные, говорящие имена
3. Предпочитай ранние return вместо глубокой вложенности
4. Избегай длинных списков параметров
5. Делай функции маленькими и сфокусированными
6. Соблюдай DRY (Don’t Repeat Yourself) — не дублируй код
7. Применяй принцип KISS (Keep It Simple, Stupid) — делай проще
8. Отдавай предпочтение композиции, а не наследованию
9. Комментируй только там, где это действительно необходимо
10. Пиши хорошие сообщения к коммитам
Какие еще правила чистого кода ты бы добавил в этот список?
👉 Java Portal
Сommand completion (..) расширяет обычный code completion. Помимо автодополнения API и postfix completion, автодополнение кода теперь включает и команды. Чтобы отфильтровать список только до команд, используйте .. Список поддерживает поиск, чтобы быстрее находить нужное.
👉 Java Portal
Шпаргалка по разрядам чисел, максимальным и минимальным значениям
👉 Java Portal
Spring Boot: чтобы при сериализации в Jackson исключать любые поля, у которых значение null, можно использовать аннотацию @JsonInclude(Include.NON_NULL)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserDTO {
private Long id;
private String name;
private String email;
private String phone;
...
}
Spring Boot: используй CommandLineRunner, чтобы выполнять логику при старте приложения.
CommandLineRunner это интерфейс Spring Boot. Его можно реализовать, чтобы запускать код после того, как контекст приложения полностью инициализирован.
Некоторые сценарии, где это уместно:
Заполнение БД начальными данными
Запуск health-check’ов при старте
Бутстрап внешних сервисов/ресурсов
Пример:
@Component
public class StartupRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("Приложение запущено! Дальнейшая настройка");
}
}
В Spring Boot можно задать таймаут для graceful shutdown через настройку spring.lifecycle.timeout-per-shutdown-phase.
Graceful shutdown помогает избежать резких обрывов HTTP-запросов и преждевременных остановок потоков.
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 20s
# Сервер будет завершаться корректно (graceful).
# Даёт до 20 секунд на завершение запросов и работы бинов.
Spring Boot: если в приложении для REST-вызовов используется RestTemplate, таймауты можно централизованно настроить через RestTemplateBuilder.
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofSeconds(3))
.setReadTimeout(Duration.ofSeconds(5))
.build();
}
}
@Bean
@Qualifier("slowServiceRestTemplate")
public RestTemplate slowServiceRestTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(30))
.build();
}
@Autowired
@Qualifier("slowServiceRestTemplate")
private RestTemplate restTemplate;
Java: исключения в Java делятся на две основные категории: checked и unchecked.
Checked-исключения нужно либо перехватывать, либо объявлять.
Unchecked-исключения не нужно объявлять в throws у метода, и компилятор не требует их перехватывать, хотя при необходимости их тоже можно ловить.
// Проверяемые (checked)
try {
FileReader reader = new FileReader("file.txt"); //FileNotFoundException (проверяемое)
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// Непроверяемые (unchecked)
String nullText = null;
System.out.println(nullText.length()); //NullPointerException (непроверяемое)
Java-совет : выносите повторяющуюся логику в небольшие вспомогательные классы, но не скатывайтесь в всезнающие God-классы.
Пример helper-класса:
public class TextUtils {
public static String capitalize(String str) {
...
}
...
}public class DoThings {
public void addTask(String task) { ... }
public void saveToFile(String filename) { ... }
public void logError(String error) { ... }
...
}
Новый способ регистрировать бины программно начиная со Spring Framework 7 👇👇👇
@Configuration
@Import(ExampleRegistrar.class)
class ExampleConfiguration {
}
class ExampleRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
if (env.matchesProfiles("dev")) {
registry.registerBean(ExampleInMemoryRepository.class);
} else {
registry.registerBean(ExampleRepository.class);
}
}
}
Spring Boot: можно переопределять конфиги на рантайме без перепаковки приложения.
Spring Boot по умолчанию использует лаунчер JarLauncher, который позволяет переопределить дефолтную конфигурацию через параметр loader.path при запуске приложения.
Предположим, у вас есть Spring Boot приложение со следующими зависимостями:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
demo.message=Hello from inside the JAR
@RestController
public class HelloController {
@Value("${demo.message}")
private String message;
@GetMapping("/")
public String hello() {
return message;
}
}
java -jar springboot-propertieslauncher-demo-0.0.1-SNAPSHOT.jar
Hello from inside the JAR
demo.message=Hello from outside the JAR
java -jar springboot-propertieslauncher-demo-0.0.1-SNAPSHOT.jar --loader.path=.
Hello from outside the JAR
Spring Boot: чтобы прогнать SQL-скрипты до или после выполнения тестового метода, можно использовать аннотацию @Sql.
@SpringBootTest
@Sql("/test/products.sql")
class ProductServiceTest {
@Autowired
ProductService productService;
@Test
void findProductByName() {
Product product = productService.findByName("product1");
assertThat(product).isNotNull();
}
}
Spring Boot. С помощью @DataJpaTest можно прогонять JPA репозитории изолированно.@DataJpaTest поднимает только JPA слой без всего приложения, использует in-memory базу H2 и откатывает транзакции после каждого теста.
👉 Java Portal
Spring Boot. Чтобы Jackson создавал объекты из JSON явно и предсказуемо, можно использовать аннотацию@JsonCreator
Каждый параметр явно биндится.
Определяем конструктор с @JsonCreator:
class User {
public final String username;
public final int age;
@JsonCreator
public User(
@JsonProperty("username") String username,
@JsonProperty("age") int age
) {
this.username = username;
this.age = age;
}
}