
- Главная
- Каталог
- Интернет технологии
- Java | Фишки и трюки
Java | Фишки и трюки
Аудитория канала - начинающие или опытные Java программисты. Канал о разработке приложений на Java, в том числе написание бэкенд и web-приложений. Рассматриваются фишки и трюки при программировании на Java.
Статистика канала
NullPointerException (NPE) был похож на игру в «Сапёра».
Вы получали ошибку: NullPointerException at com.app.Service.process(Service.java:45).
Смотрим в код на строку 45:
person.getAddress().getCountry().getIsoCode().toLowerCase();{}
null?
person?
Address?
Country?
IsoCode?
Приходилось разбивать строку на части или запускать дебаггер, чтобы понять, где именно произошел взрыв.
💡 Как это работает сейчас (Java 14+):
Java научилась анализировать байт-код в момент падения и формировать информативное сообщение.
Теперь в логах вы увидите:
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "Country.getIsoCode()" because the return value of "Address.getCountry()" is null{}
🗣 Java прямым текстом говорит:
«Я не могу вызвать getIsoCode(), потому что метод getCountry() вернул null».
Эта фича включена по умолчанию в современных версиях Java. Мелочь, а как приятно!HashMap: Когда список превращается в дерево
Все знают, что HashMap работает на основе хеширования. Мы кладем ключ, вычисляется хеш, и элемент попадает в определенную ячейку (bucket) массива.
Но что происходит, если у многих ключей совпадает хеш (коллизия)?
🐢 До Java 8 (Эпоха LinkedList):
Все элементы с одинаковым хешем выстраивались в простой связный список (LinkedList) внутри одной ячейки.
Если у вас плохой алгоритм хеширования, поиск элемента в такой мапе превращался из мгновенного O(1) в медленный перебор списка O(n). Это могло «повесить» сервер.
⚡️С Java 8 (Эпоха Red-Black Tree):
Разработчики JDK внедрили механизм Treeification (одеревенение).
Как только в одной ячейке (bucket) скапливается слишком много элементов, Java автоматически превращает этот простой список в Красно-Черное дерево (Red-Black Tree).
Магия чисел:
Порог 8 (TREEIFY_THRESHOLD): Если в цепочке становится 8 или больше элементов - список превращается в дерево.
Порог 6 (UNTREEIFY_THRESHOLD): Если при удалении элементов их становится меньше 6 - дерево разжалуется обратно в список (чтобы экономить память, так как дерево занимает больше места).
🌳 Как HashMap сравнивает ключи в дереве (Tree Bin)
Когда корзина превращается в Красно-Черное дерево, Java нужна сортировка, чтобы работала навигация (бинарный поиск). Но ключи могут быть любыми объектами.
Вот иерархия проверок, которую использует HashMap, чтобы понять, «больше» ключ или «меньше» текущего узла:
1️⃣ Сравнение хешей (hash):
Даже если ключи попали в одну корзину (индекс массива), их полные 32-битные хеши могут отличаться.
Пример: У нас корзина №5. Туда попал ключ с хешем 5 и ключ с хешем 21 (при размере массива 16: 5 % 16 = 5, 21 % 16 = 5).
HashMap видит: 5 < 21. Ага, значит, идем влево или вправо. Это самая частая проверка.
2️⃣ Интерфейс Comparable:
Если полные хеши абсолютно одинаковы (это редкая коллизия), Java проверяет: «А реализует ли ключ интерфейс Comparable?».
Если да (например, ключи это String или Integer), то используется их метод compareTo(). Это позволяет идеально рассортировать элементы внутри дерева.
3️⃣ Последний шанс (tieBreakOrder):
Если хеши одинаковы и ключи НЕ реализуют Comparable (или сравнение не удалось), Java использует System.identityHashCode() (адрес в памяти) или имя класса, чтобы хоть как-то их упорядочить.
Это «аварийный» метод, просто чтобы дерево оставалось деревом и не ломалась структура.
🚀 Почему это быстрее?
В обычном списке (LinkedList) вы вызываете equals() для каждого элемента (100 элементов = 100 проверок).
В дереве (Red-Black Tree), используя правила выше (хеш > comparable > identity), мы на каждом шаге отсекаем половину вариантов.
Мы смотрим на корень: наш хеш меньше? Идем влево. (Правая ветка со всеми ее элементами сразу отбрасывается). Смотрим следующий узел: больше? Идем вправо.
В итоге, даже если в корзине 1000 элементов, в списке мы бы сделали 1000 вызовов equals. А в дереве мы спустимся по веткам примерно за 10 шагов (log2(1000) ≈ 10).
Итог:
equals() всё равно вызывается, но только когда мы уже нашли кандидата (или идем по пути с абсолютно идентичными хешами). Мы не "опрашиваем" всех соседей подряд.
try {
int number = Integer.parseInt(input);
} catch (NumberFormatException e) { // <-- Зачем нам 'e'?
// Мы и так знаем, что это не число, детали ошибки нам не важны
System.out.println("Это не число!");
}{}
Или в циклах:
for (var s : list) { // <-- Нам нужно просто посчитать количество, 's' не нужна
count++;
}{}
✅ Как стало с Java 22 (Unnamed Variables):
Теперь можно использовать символ подчеркивания _. Это сигнал компилятору: "Здесь должна быть переменная, но я не собираюсь её использовать".
try {
int number = Integer.parseInt(input);
} catch (NumberFormatException _) { // Красота!
System.out.println("Это не число!");
}{}
Или в паттерн-матчинге (для instanceof и switch), если нам важен только тип, а не само значение:
if (obj instanceof String _) {
System.out.println("Да, это строка (но читать её я не буду)");
}{}
🔥 Почему это круто?
1️⃣ Чистота намерений: Читая код, другой разработчик сразу понимает: эта переменная игнорируется намеренно, а не по ошибке.
2️⃣ Спокойствие IDE: Анализаторы кода больше не спамят предупреждениями "Unused variable".
3️⃣ Меньше когнитивной нагрузки: Не нужно придумывать имена вроде ignored, unused или dummy.
var list = List.of("A", "B", "C");
// Чтобы взять последний элемент:
String last = list.get(list.size() - 1);
// А если это Set? (LinkedHashSet)
// Приходилось использовать итератор или перегонять в список... 😱
String lastInSet = set.iterator()... // Ой, всё, лень писать.{}
Это было неудобно, нечитаемо и чревато ошибками (привет, -1).
🥳 Java 21 (Sequenced Collections):
В Java наконец-то добавили общий интерфейс для всех коллекций, у которых есть порядок элементов — SequencedCollection.
Теперь у List, Deque, SortedSet и LinkedHashSet появились единые методы:
list.getFirst(); // Взять первый
list.getLast(); // Взять последний (Наконец-то!)
list.addFirst("Z"); // Добавить в начало
list.addLast("X"); // Добавить в конец
list.reversed(); // Получить представление коллекции в обратном порядке{}
🔥 Почему это круто?
1️⃣ Единый стандарт. Раньше у Deque были методы getFirst, у List — get(0), у SortedSet — first(). Теперь везде одинаково.
2️⃣ Работает с Set. Теперь можно легко взять первый или последний элемент из LinkedHashSet или TreeSet, не прибегая к итераторам.
3️⃣ Безопасность типов. Метод reversed() возвращает «живое» представление. Изменения в нем отразятся на оригинале (для мутабельных коллекций).
Вроде мелочь, а код становится намного чище.public, от него может наследоваться кто угодно.
Если делаете final, от него не может наследоваться никто.
А что, если я хочу, чтобы от моего класса Payment могли наследоваться только CashPayment и CardPayment, и больше никто?
До Java 17 приходилось придумывать костыли с пакетами. Теперь у нас есть Sealed Classes (Запечатанные классы).
sealed и через permits перечисляете, кому "можно".
public sealed interface Shape permits Circle, Square, Rectangle {
// Общие методы
}{}
Теперь Java (и компилятор) гарантирует: никаких других фигур, кроме этих трех, в программе существовать не может.
Circle, Square...) должны выбрать свою судьбу и указать один из модификаторов:
1️⃣ final — от меня наследовать нельзя (конец цепочки).
2️⃣ sealed — я тоже строгий, вот мой список наследников.
3️⃣ non-sealed — ладно, от меня можно наследовать всем (открываем шлюз).
🔥 Зачем это нужно? (Главная фишка)
Это идеально работает в связке с новым Switch.
Поскольку компилятор точно знает все возможные варианты наследников, он не потребует от вас ветку default!
String result = switch (shape) {
case Circle c -> "Это круг радиусом " + c.radius();
case Square s -> "Это квадрат";
case Rectangle r -> "Это прямоугольник";
// default не нужен! Java знает, что других фигур нет.
};{}
Это делает моделирование бизнес-логики (статусы заказов, типы ошибок) невероятно надежным. Если вы добавите новую фигуру, код перестанет компилироваться, пока вы не обработаете её в свитче.Map с двумя значениями. Но Java требовала от вас целый ритуал.
🐢 Как это было раньше (Java 8 и старее):
// Для списка:
List<String> list = Arrays.asList("a", "b", "c");
// Вроде ок, но этот список можно менять (set), но нельзя менять размер (add/remove).
// Для Map — вообще ужас:
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map = Collections.unmodifiableMap(map); // Если хотим защитить от изменений{}
🚀 Как это делается с Java 9:
List<String> list = List.of("a", "b", "c");
Map<String, Integer> map = Map.of("one", 1, "two", 2);
Set<String> set = Set.of("a", "b", "c");{}
Преимущества:
1️⃣ Неизменяемость (Immutability): Эти коллекции нельзя менять. Попытка сделать .add() или .put() сразу выбросит UnsupportedOperationException. Это делает код безопаснее (особенно в многопоточности).
2️⃣ Лаконичность: Map создается в одну строку (до 10 пар ключ-значение можно писать через запятую, дальше — через Map.ofEntries).
3️⃣ Никаких null: Если вы попытаетесь положить null в List.of или Map.of, вы сразу получите ошибку. Java приучает нас не использовать null в коллекциях.
💡 Лайфхак для Java 16+:
Если вы работаете со стримами, забудьте про .collect(Collectors.toList()).
Теперь можно писать просто:
List<String> result = stream.filter(s -> s.length() > 3).toList();{}
Обратите внимание: toList() возвращает неизменяемый список, в отличие от коллектора.Отзывы канала
всего 11 отзывов
- Добавлен: Сначала новые
- Добавлен: Сначала старые
- Оценка: По убыванию
- Оценка: По возрастанию
Каталог Телеграм-каналов для нативных размещений
Java | Фишки и трюки — это Telegam канал в категории «Интернет технологии», который предлагает эффективные форматы для размещения рекламных постов в Телеграмме. Количество подписчиков канала в 7.2K и качественный контент помогают брендам привлекать внимание аудитории и увеличивать охват. Рейтинг канала составляет 20.4, количество отзывов – 11, со средней оценкой 5.0.
Вы можете запустить рекламную кампанию через сервис Telega.in, выбрав удобный формат размещения. Платформа обеспечивает прозрачные условия сотрудничества и предоставляет детальную аналитику. Стоимость размещения составляет 2237.76 ₽, а за 85 выполненных заявок канал зарекомендовал себя как надежный партнер для рекламы в TG. Размещайте интеграции уже сегодня и привлекайте новых клиентов вместе с Telega.in!
Вы снова сможете добавить каналы в корзину из каталога
Комментарий