Что такое String Pool? 🤓
Ответ:
String Pool (пул строк) — это специальная область в Heap (куче) памяти JVM, предназначенная для хранения уникальных литералов строк.
Когда создается строковый литерал (например, String s = "hello";), JVM ищет такую же строку в пуле. Если находит, то возвращает ссылку на существующий объект, если нет — создает новый объект в пуле. Это позволяет экономить память.
Оператор new String("hello") всегда создает новый объект в куче вне пула, даже если такая строка уже есть в пуле.
#собеседование
Ответ:
String Pool (пул строк)
Когда создается строковый литерал (например, String s = "hello";), JVM ищет такую же строку в пуле. Если находит, то возвращает ссылку на существующий объект, если нет — создает новый объект в пуле. Это позволяет экономить память.
Оператор new String("hello") всегда создает новый объект в куче вне пула, даже если такая строка уже есть в пуле.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История IT-технологий сегодня — 03 Февраля
ℹ️ Кто родился в этот день
Не нашел(
🌐 Знаковые события
1966 — АМС «Луна-9», запущенная 31 января, впервые в мире осуществила посадку на поверхность Луны в районе Океана Бурь и передала первую лунную фотопанораму.
#Biography #Birth_Date #Events #03февраля
Не нашел(
1966 — АМС «Луна-9», запущенная 31 января, впервые в мире осуществила посадку на поверхность Луны в районе Океана Бурь и передала первую лунную фотопанораму.
#Biography #Birth_Date #Events #03февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
[Совет по Java #001]
Тема: Всегда переопределяйте equals() и hashCode() вместе, особенно для сущностей, которые будут храниться в HashSet или использоваться как ключи в HashMap.
Проблема: Нарушение ключевого контракта (contract) между методами equals() и hashCode().
Согласно спецификации Java, если два объекта равны согласно методу equals(Object), то вызов метода hashCode() для каждого из них должен возвращать одно и то же целочисленное значение.
Обратное утверждение не является обязательным: объекты с одинаковым хэш-кодом могут быть не равны (коллизия). Если разработчик переопределяет только equals(), основываясь, например, на внутренних полях объекта, но оставляет исходный hashCode() (который обычно возвращает уникальный код на основе адреса в памяти), то такие объекты, будучи логически равными, будут иметь разные хэш-коды. Это приведет к катастрофическому нарушению работы хэш-коллекций (HashSet, HashMap, ConcurrentHashMap).
Объекты не будут найдены в коллекции, дубликаты будут добавляться в HashSet, а операции с HashMap дадут непредсказуемые и ошибочные результаты. Такие ошибки трудно отлаживать, так как поведение становится недетерминированным.
Решение: Всегда совместное переопределение обоих методов, гарантирующее соблюдение их общего контракта. Реализация должна основываться на одном и том же наборе значимых (significant) полей объекта. Для вычисления хэш-кода рекомендуется использовать алгоритм, дающий хорошее распределение.
Объяснение: Метод Objects.hash(Object...) предоставляет удобную и эффективную реализацию хэш-функции, основанную на содержимом переданных полей.
Важно, что в вычислении участвуют те же поля (id и name), которые используются в equals(). Класс объявлен как final, чтобы избежать наследования и потенциального нарушения симметричности контракта equals() (проблема сравнения объекта родительского класса с объектом дочернего). Использование final полей делает класс неизменяемым (immutable), что дополнительно гарантирует стабильность хэш-кода во времени — ключевое требование для корректной работы в качестве ключа в хэш-таблицах. Нарушение этого требования (изменение поля, участвующего в hashCode(), после помещения объекта в коллекцию) приведет к потере объекта в структуре данных.
#Java #советы
Тема: Всегда переопределяйте equals() и hashCode() вместе, особенно для сущностей, которые будут храниться в HashSet или использоваться как ключи в HashMap.
Проблема: Нарушение ключевого контракта (contract) между методами equals() и hashCode().
Согласно спецификации Java, если два объекта равны согласно методу equals(Object), то вызов метода hashCode() для каждого из них должен возвращать одно и то же целочисленное значение.
Обратное утверждение не является обязательным: объекты с одинаковым хэш-кодом могут быть не равны (коллизия). Если разработчик переопределяет только equals(), основываясь, например, на внутренних полях объекта, но оставляет исходный hashCode() (который обычно возвращает уникальный код на основе адреса в памяти), то такие объекты, будучи логически равными, будут иметь разные хэш-коды. Это приведет к катастрофическому нарушению работы хэш-коллекций (HashSet, HashMap, ConcurrentHashMap).
Объекты не будут найдены в коллекции, дубликаты будут добавляться в HashSet, а операции с HashMap дадут непредсказуемые и ошибочные результаты. Такие ошибки трудно отлаживать, так как поведение становится недетерминированным.
Решение: Всегда совместное переопределение обоих методов, гарантирующее соблюдение их общего контракта. Реализация должна основываться на одном и том же наборе значимых (significant) полей объекта. Для вычисления хэш-кода рекомендуется использовать алгоритм, дающий хорошее распределение.
import java.util.Objects;
public final class Entity {
private final Long id;
private final String name;
public Entity(Long id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true; // Проверка на идентичность ссылок
if (o == null || getClass() != o.getClass()) return false; // Проверка класса
Entity entity = (Entity) o;
// Сравнение по значимым полям. Используем Objects.equals() для null-safe сравнения.
return Objects.equals(id, entity.id) &&
Objects.equals(name, entity.name);
}
@Override
public int hashCode() {
// Используем хэш-функцию на основе тех же полей, что и в equals()
return Objects.hash(id, name);
}
}
Объяснение: Метод Objects.hash(Object...) предоставляет удобную и эффективную реализацию хэш-функции, основанную на содержимом переданных полей.
Важно, что в вычислении участвуют те же поля (id и name), которые используются в equals(). Класс объявлен как final, чтобы избежать наследования и потенциального нарушения симметричности контракта equals() (проблема сравнения объекта родительского класса с объектом дочернего). Использование final полей делает класс неизменяемым (immutable), что дополнительно гарантирует стабильность хэш-кода во времени — ключевое требование для корректной работы в качестве ключа в хэш-таблицах. Нарушение этого требования (изменение поля, участвующего в hashCode(), после помещения объекта в коллекцию) приведет к потере объекта в структуре данных.
#Java #советы
👍3🔥2🆒1
Что выведет код?
#Tasks
import java.util.HashSet;
public class Task030226 {
public static void main(String[] args) {
HashSet<Item030226> set = new HashSet<>();
Item030226 item1 = new Item030226(1, "apple");
Item030226 item2 = new Item030226(1, "apple");
set.add(item1);
item1.id = 2;
set.add(item2);
System.out.println(set.size());
System.out.println(set.contains(item1));
System.out.println(set.contains(item2));
}
static class Item030226 {
int id;
String name;
Item030226(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item030226 item = (Item030226) o;
return id == item.id && name.equals(item.name);
}
@Override
public int hashCode() {
return id + name.hashCode();
}
}
}
#Tasks
👍1
👍1
Что такое конструктор? Можно ли наследовать конструктор? 🤓
Ответ:
Конструктор — это специальный метод класса, который вызывается при создании нового объекта.
Его имя должно совпадать с именем класса, и у него нет возвращаемого типа. Конструкторы не наследуются.
При создании объекта класса-потомка всегда вызывается конструктор его родительского класса (явно через super(...) или неявно, вызывается конструктор по умолчанию super()).
Если в родительском классе нет конструктора по умолчанию, в классе-потомке обязательно нужно явно вызвать один из существующих.
#собеседование
Ответ:
Конструктор
Его имя должно совпадать с именем класса, и у него нет возвращаемого типа. Конструкторы не наследуются.
При создании объекта класса-потомка всегда вызывается конструктор его родительского класса (явно через super(...) или неявно, вызывается конструктор по умолчанию super()).
Если в родительском классе нет конструктора по умолчанию, в классе-потомке обязательно нужно явно вызвать один из существующих.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История IT-технологий сегодня — 04 Февраля
ℹ️ Кто родился в этот день
Кеннет Лейн (Кен) То́мпсон (англ. Kenneth Lane Thompson; род. 4 февраля 1943) — американский программист; один из создателей UNIX и языка B, предшественника C; работал над файловыми системами, процессами, утилитами и общим дизайном UNIX, а позже участвовал в создании языка Go. Его работа задала стандарты для современных ОС (процессы, файлы, пайпы, текстовые утилиты), что прямой фундамент почти всей современной серверной IT и интернета.
🌐 Знаковые события
2004 — запуск “Thefacebook” Марком Цукербергом в общежитии Гарварда: первоначально как соцсеть для студентов, позже выросшую в Facebook; это сильно повлияло на социальные сети, рекламу, big data, рекомендательные системы и вообще на то, как интернет устроен социально и коммерчески.
#Biography #Birth_Date #Events #04февраля
Кеннет Лейн (Кен) То́мпсон (англ. Kenneth Lane Thompson; род. 4 февраля 1943) — американский программист; один из создателей UNIX и языка B, предшественника C; работал над файловыми системами, процессами, утилитами и общим дизайном UNIX, а позже участвовал в создании языка Go. Его работа задала стандарты для современных ОС (процессы, файлы, пайпы, текстовые утилиты), что прямой фундамент почти всей современной серверной IT и интернета.
2004 — запуск “Thefacebook” Марком Цукербергом в общежитии Гарварда: первоначально как соцсеть для студентов, позже выросшую в Facebook; это сильно повлияло на социальные сети, рекламу, big data, рекомендательные системы и вообще на то, как интернет устроен социально и коммерчески.
#Biography #Birth_Date #Events #04февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Please open Telegram to view this post
VIEW IN TELEGRAM
Хабр
State-first архитектура: поиск другого способа управления бизнес-логикой
За последние годы разработчики в распределённых системах почти решили инфраструктурные проблемы: масштабирование, деплой, отказоустойчивость. Ценой этого прогресса стал экспоненциальный рост сложности...
🍾8🔥4 1
Раздел 8. Stream API и функциональный стиль
Глава 1: Философский фундамент. От шагов к преобразованиям
Императивный код — машина Тьюринга в миниатюре
Императивная парадигма программирования восходит к самым глубоким основаниям вычислительной теории. Когда вы пишете код в императивном стиле, вы буквально эмулируете работу машины Тьюринга — абстрактного вычислительного устройства, состоящего из бесконечной ленты и считывающей головки, которая перемещается по этой ленте, изменяя состояние ячеек согласно заложенным инструкциям. Ваши переменные — это ячейки памяти. Ваши операторы присваивания — это запись на ленту. Ваши циклы и условные переходы — это движение головки от одной ячейки к другой.
Рассмотрим типичную задачу: необходимо из списка заказов отфильтровать выполненные, вычислить общую сумму и вернуть топ-3 заказа по стоимости.
В императивной реализации мы начинаем с пустого состояния и последовательно его трансформируем:
Здесь каждая строчка — это шаг.
Шаг первый: создать пустой список.
Шаг второй: пройтись по исходным данным.
Шаг третий: проверить условие.
Шаг четвёртый: добавить в список.
Мы не описываем что хотим получить — мы диктуем как это получить, командуя машиной на каждом этапе.
Переменные-состояния в этом коде играют роль оперативной памяти машины Тьюринга. completedOrders — это наша лента, куда мы записываем промежуточные результаты. Переменная i в цикле — это позиция головки. Каждое присваивание — запись в ячейку. Каждое чтение — позиционирование головки.
Паттерны императивной обработки
Линейный проход — самая фундаментальная структура. Мы последовательно обрабатываем элементы от первого до последнего, поддерживая текущий индекс или итератор. Этот паттерн универсален: он работает для любой коллекции, не требует дополнительной памяти для структуры данных (только для результатов), позволяет легко внедрить логику раннего выхода.
Ранний выход (break, return) — мощный инструмент оптимизации императивного кода. Он позволяет избежать лишних вычислений, как только достигнуто желаемое состояние. В функциональном программировании аналогичное поведение достигается иначе — через ленивые вычисления и короткое замыкание операций, но в императивном коде это явная инструкция процессору прекратить исполнение текущего блока.
Условное накопление комбинирует проход с фильтрацией. На каждой итерации мы проверяем предикат — логическое условие, определяющее, должна ли текущая ячейка ленты участвовать в формировании результата.
Это порождает разветвлённую логику внутри цикла:
#Java #для_новичков #beginner #stream_api #imperative
Глава 1: Философский фундамент. От шагов к преобразованиям
Императивный код — машина Тьюринга в миниатюре
Императивная парадигма программирования восходит к самым глубоким основаниям вычислительной теории. Когда вы пишете код в императивном стиле, вы буквально эмулируете работу машины Тьюринга — абстрактного вычислительного устройства, состоящего из бесконечной ленты и считывающей головки, которая перемещается по этой ленте, изменяя состояние ячеек согласно заложенным инструкциям. Ваши переменные — это ячейки памяти. Ваши операторы присваивания — это запись на ленту. Ваши циклы и условные переходы — это движение головки от одной ячейки к другой.
Рассмотрим типичную задачу: необходимо из списка заказов отфильтровать выполненные, вычислить общую сумму и вернуть топ-3 заказа по стоимости.
В императивной реализации мы начинаем с пустого состояния и последовательно его трансформируем:
public List<Order> getTopThreeCompletedOrders(List<Order> orders) {
// Инициализация временного состояния
List<Order> completedOrders = new ArrayList<>();
// Линейный проход с условным накоплением
for (Order order : orders) {
if (order.getStatus() == Status.COMPLETED) {
completedOrders.add(order);
}
}
// Сортировка через изменение состояния (in-place или новая коллекция)
completedOrders.sort((a, b) -> b.getAmount().compareTo(a.getAmount()));
// Извлечение подмножества с проверкой границ
List<Order> result = new ArrayList<>();
int limit = Math.min(3, completedOrders.size());
for (int i = 0; i < limit; i++) {
result.add(completedOrders.get(i));
}
return result;
}Здесь каждая строчка — это шаг.
Шаг первый: создать пустой список.
Шаг второй: пройтись по исходным данным.
Шаг третий: проверить условие.
Шаг четвёртый: добавить в список.
Мы не описываем что хотим получить — мы диктуем как это получить, командуя машиной на каждом этапе.
Переменные-состояния в этом коде играют роль оперативной памяти машины Тьюринга. completedOrders — это наша лента, куда мы записываем промежуточные результаты. Переменная i в цикле — это позиция головки. Каждое присваивание — запись в ячейку. Каждое чтение — позиционирование головки.
Паттерны императивной обработки
Линейный проход — самая фундаментальная структура. Мы последовательно обрабатываем элементы от первого до последнего, поддерживая текущий индекс или итератор. Этот паттерн универсален: он работает для любой коллекции, не требует дополнительной памяти для структуры данных (только для результатов), позволяет легко внедрить логику раннего выхода.
// Линейный поиск с ранним выходом
User foundUser = null;
for (User user : users) {
if (user.getId().equals(targetId)) {
foundUser = user; // Нашли — запомнили состояние
break; // Прервали ленту — головка остановлена
}
}
Ранний выход (break, return) — мощный инструмент оптимизации императивного кода. Он позволяет избежать лишних вычислений, как только достигнуто желаемое состояние. В функциональном программировании аналогичное поведение достигается иначе — через ленивые вычисления и короткое замыкание операций, но в императивном коде это явная инструкция процессору прекратить исполнение текущего блока.
Условное накопление комбинирует проход с фильтрацией. На каждой итерации мы проверяем предикат — логическое условие, определяющее, должна ли текущая ячейка ленты участвовать в формировании результата.
Это порождает разветвлённую логику внутри цикла:
List<Transaction> validTransactions = new ArrayList<>();
BigDecimal totalRisk = BigDecimal.ZERO;
for (Transaction tx : transactions) {
// Вложенные условия — сложное состояние машины
if (tx.getAmount().compareTo(THRESHOLD) > 0) {
if (tx.getTimestamp().isAfter(cutoffDate)) {
if (!tx.isFlagged()) {
validTransactions.add(tx);
totalRisk = totalRisk.add(tx.calculateRisk());
}
}
}
}
#Java #для_новичков #beginner #stream_api #imperative
👍4
Здесь мы наблюдаем эскалацию сложности состояния: несколько переменных (validTransactions, totalRisk) изменяются синхронно, и их консистентность поддерживается программистом вручную. Если забыть обновить totalRisk при добавлении транзакции — получим рассогласование состояний, баг, который сложно отследить, потому что он проявляется не сразу, а в конечном результате вычислений.
Вложенные циклы — квинтэссенция императивной вычислительной мощи и одновременно её проклятие. Когда данные имеют иерархическую структуру (категории, содержащие товары, содержащие отзывы), мы запускаем машину Тьюринга внутри машины Тьюринга. Внешняя лента — категории.
Для каждой позиции внешней ленты мы разворачиваем внутреннюю ленту — товары этой категории.
Каждый уровень вложенности добавляет измерение в пространство состояний. Теперь у нас три индекса (позиции трёх головок на трёх лентах), три контекста выполнения, и логика раннего выхода становится запутанной: break прерывает только внутренний цикл, для выхода из всех требуется флаг или исключение, что ещё больше усложняет состояние.
Сила императивного подхода
Полный контроль — вот что делает императивный код незаменимым в определённых контекстах. Когда вы пишете цикл вручную, вы контролируете каждую микрооперацию: когда именно происходит инкремент индекса, в каком порядке проверяются условия, какие объекты создаются, какие переиспользуются.
Оптимизация аллокаций — яркий пример. В примере с заказами выше мы создали два промежуточных списка: completedOrders и result.
Опытный разработчик может соптимизировать это до одного прохода с ограниченной очередью (Bounded Priority Queue), избегая хранения всех выполненных заказов в памяти:
Этот код сложнее для понимания, но он использует O(1) дополнительной памяти (константный размер кучи) вместо O(n) для хранения всех выполненных заказов. В императивном стиле такая оптимизация достижима, потому что мы управляем состоянием явно.
Прерывание выполнения в любой точке — ещё одно преимущество.
Предположим, мы ищем первый элемент, удовлетворяющий сложному условию, и должны выполнить постобработку при нахождении:
Никаких дополнительных проверок, никакого Optional, никакого пропагирования состояния "найдено/не найдено" наружу — просто остановка машины в нужный момент.
#Java #для_новичков #beginner #stream_api #imperative
Вложенные циклы — квинтэссенция императивной вычислительной мощи и одновременно её проклятие. Когда данные имеют иерархическую структуру (категории, содержащие товары, содержащие отзывы), мы запускаем машину Тьюринга внутри машины Тьюринга. Внешняя лента — категории.
Для каждой позиции внешней ленты мы разворачиваем внутреннюю ленту — товары этой категории.
// O(n × m × k) — полиномиальная сложность
List<Review> allRecentReviews = new ArrayList<>();
for (Category category : categories) {
for (Product product : category.getProducts()) {
for (Review review : product.getReviews()) {
if (review.getDate().isAfter(lastMonth)) {
allRecentReviews.add(review);
}
}
}
}
Каждый уровень вложенности добавляет измерение в пространство состояний. Теперь у нас три индекса (позиции трёх головок на трёх лентах), три контекста выполнения, и логика раннего выхода становится запутанной: break прерывает только внутренний цикл, для выхода из всех требуется флаг или исключение, что ещё больше усложняет состояние.
Сила императивного подхода
Полный контроль — вот что делает императивный код незаменимым в определённых контекстах. Когда вы пишете цикл вручную, вы контролируете каждую микрооперацию: когда именно происходит инкремент индекса, в каком порядке проверяются условия, какие объекты создаются, какие переиспользуются.
Оптимизация аллокаций — яркий пример. В примере с заказами выше мы создали два промежуточных списка: completedOrders и result.
Опытный разработчик может соптимизировать это до одного прохода с ограниченной очередью (Bounded Priority Queue), избегая хранения всех выполненных заказов в памяти:
public List<Order> getTopThreeOptimized(List<Order> orders) {
// Очередь с обратным порядком (минимальная сумма на вершине)
PriorityQueue<Order> minHeap = new PriorityQueue<>(
Comparator.comparing(Order::getAmount)
);
for (Order order : orders) {
if (order.getStatus() == Status.COMPLETED) {
if (minHeap.size() < 3) {
minHeap.offer(order);
} else if (order.getAmount().compareTo(minHeap.peek().getAmount()) > 0) {
minHeap.poll(); // Удаляем минимальный из топ-3
minHeap.offer(order);
}
}
}
// Преобразование в список и сортировка по убыванию
List<Order> result = new ArrayList<>(minHeap);
result.sort(Comparator.comparing(Order::getAmount).reversed());
return result;
}Этот код сложнее для понимания, но он использует O(1) дополнительной памяти (константный размер кучи) вместо O(n) для хранения всех выполненных заказов. В императивном стиле такая оптимизация достижима, потому что мы управляем состоянием явно.
Прерывание выполнения в любой точке — ещё одно преимущество.
Предположим, мы ищем первый элемент, удовлетворяющий сложному условию, и должны выполнить постобработку при нахождении:
for (Item item : items) {
if (complexValidation(item)) {
processFoundItem(item); // Постобработка
return result; // Мгновенный выход из метода
}
}Никаких дополнительных проверок, никакого Optional, никакого пропагирования состояния "найдено/не найдено" наружу — просто остановка машины в нужный момент.
#Java #для_новичков #beginner #stream_api #imperative
👍3
Слабость: связанность и состояние как источник ошибок
Высокая связанность (coupling) кода и бизнес-логики проявляется в том, что алгоритмическая механика неотделима от семантики предметной области. Когда вы читаете императивный цикл, вы видите if (order.getStatus() == Status.COMPLETED) вперемешку с i++ и list.add(). Бизнес-правило "учитывать только выполненные заказы" склеено с технической деталью "добавлять в ArrayList".
Это затрудняет тестирование. Вы не можете протестировать логику фильтрации отдельно от логики накопления. Вы не можете легко заменить ArrayList на LinkedList или на потоковый вывод без модификации всего метода. Вы не можете повторно использовать этот цикл для другой операции — скажем, подсчёта количества вместо сбора в список — без копирования и модификации кода.
Но главный враг императивного кода — изменяемое состояние. Каждая переменная, которая может быть перезаписана, — потенциальная точка отказа. Рассмотрим многопоточный контекст: если два потока одновременно выполняют накопление в общий список, получаем race condition (состояние гонки) — классический конкурентный баг, когда результат зависит от недетерминированного порядка выполнения операций. Даже в однопоточном коде состояние требует умственной нагрузки: отслеживая, какое значение находится в переменной на каждой строке, вы вынуждены эмулировать выполнение программы в голове.
Состояние размножается. Переменная completedOrders требует переменной i для итерации. i требует проверки границ. Проверка границ требует вычисления size(). Вычисление размера требует консистентности структуры данных. Если внутри цикла случайно модифицировать коллекцию, по которой итерируемся (например, удалить элемент), индексы съезжают, и мы получаем ConcurrentModificationException или пропуск элементов.
Вложенные циклы с состоянием особенно опасны. Легко перепутать индексы i, j, k, обратиться к products.get(i) вместо products.get(j), и компилятор не подскажет — типы совпадают, индексы валидны, логика сломана.
Возвращаясь к машине Тьюринга: императивный код заставляет вас думать как машина. Вы управляете лентой, головкой, таблицей переходов. Но программирование — это искусство абстракции, и следующий шаг эволюции — подняться над механикой вычислений к декларативному описанию преобразований, где фокус смещается с "как" на "что".
#Java #для_новичков #beginner #stream_api #imperative
Высокая связанность (coupling) кода и бизнес-логики проявляется в том, что алгоритмическая механика неотделима от семантики предметной области. Когда вы читаете императивный цикл, вы видите if (order.getStatus() == Status.COMPLETED) вперемешку с i++ и list.add(). Бизнес-правило "учитывать только выполненные заказы" склеено с технической деталью "добавлять в ArrayList".
Это затрудняет тестирование. Вы не можете протестировать логику фильтрации отдельно от логики накопления. Вы не можете легко заменить ArrayList на LinkedList или на потоковый вывод без модификации всего метода. Вы не можете повторно использовать этот цикл для другой операции — скажем, подсчёта количества вместо сбора в список — без копирования и модификации кода.
Но главный враг императивного кода — изменяемое состояние. Каждая переменная, которая может быть перезаписана, — потенциальная точка отказа. Рассмотрим многопоточный контекст: если два потока одновременно выполняют накопление в общий список, получаем race condition (состояние гонки) — классический конкурентный баг, когда результат зависит от недетерминированного порядка выполнения операций. Даже в однопоточном коде состояние требует умственной нагрузки: отслеживая, какое значение находится в переменной на каждой строке, вы вынуждены эмулировать выполнение программы в голове.
Состояние размножается. Переменная completedOrders требует переменной i для итерации. i требует проверки границ. Проверка границ требует вычисления size(). Вычисление размера требует консистентности структуры данных. Если внутри цикла случайно модифицировать коллекцию, по которой итерируемся (например, удалить элемент), индексы съезжают, и мы получаем ConcurrentModificationException или пропуск элементов.
Вложенные циклы с состоянием особенно опасны. Легко перепутать индексы i, j, k, обратиться к products.get(i) вместо products.get(j), и компилятор не подскажет — типы совпадают, индексы валидны, логика сломана.
Возвращаясь к машине Тьюринга: императивный код заставляет вас думать как машина. Вы управляете лентой, головкой, таблицей переходов. Но программирование — это искусство абстракции, и следующий шаг эволюции — подняться над механикой вычислений к декларативному описанию преобразований, где фокус смещается с "как" на "что".
#Java #для_новичков #beginner #stream_api #imperative
👍3
Что выведет код?
#Tasks
public class Task040226 {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
int i = 0;
arr[i] = i = 2;
System.out.println(arr[0] + " " + arr[2]);
}
}#Tasks
👍2
👍2🔥2
В чем разница между абстрактным классом и интерфейсом до Java 8 и после? 🤓
Ответ:
До Java 8: Абстрактный класс мог иметь поля, конструкторы, методы с реализацией и без. Интерфейс мог содержать только константы (public static final) и абстрактные методы.
С Java 8 интерфейс может содержать default методы (с реализацией) и static методы. С Java 9 добавились private методы в интерфейсах.
Ключевое отличие: класс может наследовать только один абстрактный класс, но реализовать много интерфейсов.
Абстрактный класс описывает сущность ("is-a"), интерфейс — поведение ("can-do").
#собеседование
Ответ:
До Java 8:
С Java 8 интерфейс может содержать default методы (с реализацией) и static методы. С Java 9 добавились private методы в интерфейсах.
Ключевое отличие: класс может наследовать только один абстрактный класс, но реализовать много интерфейсов.
Абстрактный класс описывает сущность ("is-a"), интерфейс — поведение ("can-do").
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История IT-технологий сегодня — 05 Февраля
ℹ️ Кто родился в этот день
Никого не нашел, вот просто для интереса:
Сэр Ха́йрем Сти́венс Ма́ксим (иногда Мэ́ксим, англ. Hiram Stevens Maxim, 5 февраля 1840 — 24 ноября 1916) — американо-британский изобретатель и оружейник, создатель одного из самых знаменитых пулемётов — пулемёта Максима.
🌐 Знаковые события
1909 — бельгийский химик Лео Бакеланд сообщил о полученном им материале, который он назвал «бакелитом». Данный материал был первым синтетическим реактопластом — пластиком, который не размягчался при высокой температуре.
1972 — Hewlett-Packard выпускает HP‑35, первый научный карманный калькулятор: он смог заменить логарифмическую линейку, стал важным инструментом инженеров и программистов и показал, как вычислительная техника переезжает «в карман» пользователя.
#Biography #Birth_Date #Events #05февраля
Никого не нашел, вот просто для интереса:
Сэр Ха́йрем Сти́венс Ма́ксим (иногда Мэ́ксим, англ. Hiram Stevens Maxim, 5 февраля 1840 — 24 ноября 1916) — американо-британский изобретатель и оружейник, создатель одного из самых знаменитых пулемётов — пулемёта Максима.
1909 — бельгийский химик Лео Бакеланд сообщил о полученном им материале, который он назвал «бакелитом». Данный материал был первым синтетическим реактопластом — пластиком, который не размягчался при высокой температуре.
1972 — Hewlett-Packard выпускает HP‑35, первый научный карманный калькулятор: он смог заменить логарифмическую линейку, стал важным инструментом инженеров и программистов и показал, как вычислительная техника переезжает «в карман» пользователя.
#Biography #Birth_Date #Events #05февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
[Совет по Java #002]
Тема: Используйте StringBuilder для конкатенации строк в циклах. String в цикле создает множество объектов и убивает производительность.
Проблема: Использование оператора + или метода concat() для конкатенации строк внутри циклов приводит к катастрофическим потерям производительности и излишнему потреблению памяти.
Причина кроется в неизменяемости (immutability) объектов класса String. Каждая операция конкатенации с участием String не модифицирует существующий объект, а создает совершенно новый, так как содержимое исходных строк не может быть изменено.
Внутри цикла это порождает квадратичную временную сложность O(n²). Для каждой итерации создается новый строковый объект, содержимое предыдущего результата копируется в него, а затем добавляется новая часть.
Старые объекты становятся мусором, что увеличивает нагрузку на сборщик мусора (Garbage Collector). Производительность падает экспоненциально с увеличением количества итераций и длины строк.
Решение: Для любого множественного (в цикле) или сложного построения строк необходимо использовать классы StringBuilder (в однопоточных сценариях) или его синхронизированную версию StringBuffer. Эти классы являются изменяемыми (mutable). Они работают с внутренним массивом символов (char array), который динамически расширяется при необходимости. Операции добавления (append) модифицируют этот внутренний буфер, не создавая новый объект на каждой итерации.
Это снижает сложность до линейной O(n) и сводит к минимуму создание промежуточных объектов.
Объяснение: Ключевое преимущество StringBuilder — работа с изменяемым внутренним состоянием.
Конструктор может принимать начальную емкость (capacity), что позволяет избежать затратных операций копирования массива при его расширении, если примерная длина итоговой строки известна заранее.
Важно отметить, что для однократной конкатенации вне циклов компилятор Java сам оптимизирует код, заменяя + на StringBuilder. Однако эта оптимизация не распространяется на циклы — внутри цикла компилятор создает новый экземпляр StringBuilder на каждой итерации, что также неэффективно.
Для конкатенации элементов коллекций с разделителем наилучшим и наиболее выразительным выбором является статический метод String.join(), который внутри использует оптимизированную логику.
#Java #советы
Тема: Используйте StringBuilder для конкатенации строк в циклах. String в цикле создает множество объектов и убивает производительность.
Проблема: Использование оператора + или метода concat() для конкатенации строк внутри циклов приводит к катастрофическим потерям производительности и излишнему потреблению памяти.
Причина кроется в неизменяемости (immutability) объектов класса String. Каждая операция конкатенации с участием String не модифицирует существующий объект, а создает совершенно новый, так как содержимое исходных строк не может быть изменено.
Внутри цикла это порождает квадратичную временную сложность O(n²). Для каждой итерации создается новый строковый объект, содержимое предыдущего результата копируется в него, а затем добавляется новая часть.
Старые объекты становятся мусором, что увеличивает нагрузку на сборщик мусора (Garbage Collector). Производительность падает экспоненциально с увеличением количества итераций и длины строк.
Решение: Для любого множественного (в цикле) или сложного построения строк необходимо использовать классы StringBuilder (в однопоточных сценариях) или его синхронизированную версию StringBuffer. Эти классы являются изменяемыми (mutable). Они работают с внутренним массивом символов (char array), который динамически расширяется при необходимости. Операции добавления (append) модифицируют этот внутренний буфер, не создавая новый объект на каждой итерации.
Это снижает сложность до линейной O(n) и сводит к минимуму создание промежуточных объектов.
public class StringConcatExample {
public static void main(String[] args) {
String[] words = {"Это", "пример", "неэффективной", "конкатенации"};
//Антипаттерн: Каждый "+" создает новый String-объект.
String badResult = "";
for (String word : words) {
badResult = badResult + word + " "; // Новый объект на каждом шаге!
}
System.out.println(badResult);
//Решение: Использование StringBuilder.
StringBuilder sb = new StringBuilder(); // Можно задать начальную capacity для оптимизации.
for (String word : words) {
sb.append(word).append(" "); // Работа с внутренним буфером.
}
String goodResult = sb.toString().trim(); // Создание финального String происходит лишь один раз.
System.out.println(goodResult);
// Для простого однострочного соединения коллекций предпочтительнее String.join()
String bestResult = String.join(" ", words); // Читаемо и эффективно.
System.out.println(bestResult);
}
}Объяснение: Ключевое преимущество StringBuilder — работа с изменяемым внутренним состоянием.
Конструктор может принимать начальную емкость (capacity), что позволяет избежать затратных операций копирования массива при его расширении, если примерная длина итоговой строки известна заранее.
Важно отметить, что для однократной конкатенации вне циклов компилятор Java сам оптимизирует код, заменяя + на StringBuilder. Однако эта оптимизация не распространяется на циклы — внутри цикла компилятор создает новый экземпляр StringBuilder на каждой итерации, что также неэффективно.
Для конкатенации элементов коллекций с разделителем наилучшим и наиболее выразительным выбором является статический метод String.join(), который внутри использует оптимизированную логику.
#Java #советы
👍6
Что выведет код?
#Tasks
public class Task050226 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("12345");
sb.delete(2, 4);
sb.insert(2, "xyz");
sb.replace(1, 3, "ab");
System.out.println(sb);
}
}#Tasks
👍2
👍4
Java for Beginner
Очередная статья на Хабре. Не для новичков, но для размышления. Жду Ваших оценок 🙂 Ну и накидайте лайков или что там вместо них ☺️ #Хабр
Ну блин лайкосов зажали…
Уйду я от вас….
Уйду я от вас….
👍8👾2
Что такое переменные, методы и классы обертки (Wrapper classes)? Зачем они нужны? 🤓
Ответ:
Классы-обертки (например, Integer, Double, Boolean) — это объектные представления примитивных типов данных.
Они нужны:
1) для хранения примитивов в коллекциях, которые работают только с объектами (Object);
2) для использования утильных методов (преобразование в строку, из строки, сравнение);
3) для поддержки механизма автоупаковки (autoboxing) и распаковки (unboxing), которые автоматически преобразуют примитивы в объекты и обратно. Они являются immutable и final.
#собеседование
Ответ:
Классы-обертки (например, Integer, Double, Boolean)
Они нужны:
1) для хранения примитивов в коллекциях, которые работают только с объектами (Object);
2) для использования утильных методов (преобразование в строку, из строки, сравнение);
3) для поддержки механизма автоупаковки (autoboxing) и распаковки (unboxing), которые автоматически преобразуют примитивы в объекты и обратно. Они являются immutable и final.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
История IT-технологий сегодня — 06 Февраля
ℹ️ Кто родился в этот день
Оркут Бююккёктен (родился 6 февраля 1975 г.) — турецкий инженер-программист, разработавший социальные сети Club Nexus, inCircle и Orkut . Ранее он работал менеджером по продуктам в Google. После перехода в Google использовал своё «20% времени», чтобы сделать новую соцсеть — так появилась Orkut; сервис назвали в его честь, домен orkut.com тоже изначально принадлежал ему. Orkut стал особенно популярен в Бразилии и Индии, набрал свыше 100 млн пользователей, был одним из первых крупных конкурентов Facebook, но Google закрыл его 30 сентября 2014 года.
🌐 Знаковые события
1900 — русский учёный Александр Попов находился на станции беспроводной связи на острове Кутсало (осуществлял настройку аппаратуры) при передаче телеграфного сообщения на остров Гогланд (командиру ледокола «Ермак») об унесённых на льдине рыбаках.
1959 — американский учёный и инженер Джек Килби работавший в Texas Instruments (TI), получает патент (U.S. Patent 3,138,743) на изобретение интегральной схемы. В 2000 году, за это изобретение, он стал лауреатом Нобелевской премии по физике.
#Biography #Birth_Date #Events #06февраля
Оркут Бююккёктен (родился 6 февраля 1975 г.) — турецкий инженер-программист, разработавший социальные сети Club Nexus, inCircle и Orkut . Ранее он работал менеджером по продуктам в Google. После перехода в Google использовал своё «20% времени», чтобы сделать новую соцсеть — так появилась Orkut; сервис назвали в его честь, домен orkut.com тоже изначально принадлежал ему. Orkut стал особенно популярен в Бразилии и Индии, набрал свыше 100 млн пользователей, был одним из первых крупных конкурентов Facebook, но Google закрыл его 30 сентября 2014 года.
1900 — русский учёный Александр Попов находился на станции беспроводной связи на острове Кутсало (осуществлял настройку аппаратуры) при передаче телеграфного сообщения на остров Гогланд (командиру ледокола «Ермак») об унесённых на льдине рыбаках.
1959 — американский учёный и инженер Джек Килби работавший в Texas Instruments (TI), получает патент (U.S. Patent 3,138,743) на изобретение интегральной схемы. В 2000 году, за это изобретение, он стал лауреатом Нобелевской премии по физике.
#Biography #Birth_Date #Events #06февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4