Что такое this и super? 🤓
Ответ:
this — это ссылка на текущий объект.
Используется для разрешения конфликтов имен (параметр метода с тем же именем, что и поле класса: this.field = field), для вызова одного конструктора из другого (this()), или передачи текущего объекта в другой метод.
super — это ссылка на объект родительского класса.
Используется для доступа к полям и методам родителя, скрытым переопределением, и для вызова конструктора родителя (super()) — это должен быть первый оператор в конструкторе потомка.
#собеседование
Ответ:
this
Используется для разрешения конфликтов имен (параметр метода с тем же именем, что и поле класса: this.field = field), для вызова одного конструктора из другого (this()), или передачи текущего объекта в другой метод.
super — это ссылка на объект родительского класса.
Используется для доступа к полям и методам родителя, скрытым переопределением, и для вызова конструктора родителя (super()) — это должен быть первый оператор в конструкторе потомка.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 31 марта
ℹ️ Кто родился в этот день
Ка́рло Ру́ббиа (итал. Carlo Rubbia; род. 31 марта 1934, Гориция, Италия) — итальянский физик, лауреат Нобелевской премии по физике в 1984 году, совместно с Симоном ван дер Мером, «за решающий вклад в большой проект, осуществление которого привело к открытию квантов поля W и Z — переносчиков слабого взаимодействия».
Синъити́ро Томона́га (яп. 朝永 振一郎 Томонага Синъитиро:, 31 марта 1906, Токио — 8 июля 1979, там же) — японский физик-теоретик, известный разработкой базовых положений квантовой электродинамики, лауреат Нобелевской премии по физике 1965 года. Президент Научного совета Японии (1963—1969). Член Японской академии наук (1951), иностранный член Национальной академии наук США (1965)[4], Академии наук СССР (1971).
🌐 Знаковые события
1901 — выпуск первого автомобиля с названием Mercedes.
2005 — группой астрономов во главе с профессором Майклом Брауном была открыта карликовая планета Макемаке.
#Biography #Birth_Date #Events #31марта
Ка́рло Ру́ббиа (итал. Carlo Rubbia; род. 31 марта 1934, Гориция, Италия) — итальянский физик, лауреат Нобелевской премии по физике в 1984 году, совместно с Симоном ван дер Мером, «за решающий вклад в большой проект, осуществление которого привело к открытию квантов поля W и Z — переносчиков слабого взаимодействия».
Синъити́ро Томона́га (яп. 朝永 振一郎 Томонага Синъитиро:, 31 марта 1906, Токио — 8 июля 1979, там же) — японский физик-теоретик, известный разработкой базовых положений квантовой электродинамики, лауреат Нобелевской премии по физике 1965 года. Президент Научного совета Японии (1963—1969). Член Японской академии наук (1951), иностранный член Национальной академии наук США (1965)[4], Академии наук СССР (1971).
1901 — выпуск первого автомобиля с названием Mercedes.
2005 — группой астрономов во главе с профессором Майклом Брауном была открыта карликовая планета Макемаке.
#Biography #Birth_Date #Events #31марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 8. Stream API и функциональный стиль в Java
Глава 7: Stream API в экосистеме Java. Интеграционные паттерны
Рефакторинг legacy-кода в проекте «Библиотека»
Представьте, что в проекте «Библиотека» накопился такой метод — подсчёт статистики по авторам с множеством условий:
Характеристики кода:
40+ строк в одном методе
4 уровня вложенности в пике
Смешение фильтрации, трансформации и агрегации
Два прохода: основной цикл + пост-обработка
#Java #для_новичков #beginner #stream_api #практика
Глава 7: Stream API в экосистеме Java. Интеграционные паттерны
Рефакторинг legacy-кода в проекте «Библиотека»
Представьте, что в проекте «Библиотека» накопился такой метод — подсчёт статистики по авторам с множеством условий:
public Map<String, AuthorStats> calculateAuthorStatsLegacy(List<Book> books, int minYear) {
Map<String, AuthorStats> result = new HashMap<>();
for (Book book : books) {
// Пропускаем старые книги
if (book.getYear() < minYear) {
continue;
}
// Пропускаем без жанров
if (book.getGenres().isEmpty()) {
continue;
}
// Пропускаем, если нет цены
Double price = book.getPrice();
if (price == null || price <= 0) {
continue;
}
String author = book.getAuthor();
AuthorStats stats = result.get(author);
if (stats == null) {
stats = new AuthorStats();
stats.firstBookYear = book.getYear();
result.put(author, stats);
}
// Обновляем статистику
stats.bookCount++;
stats.totalPages += book.getPages();
stats.totalPrice += price;
// Обновляем самую старую книгу
if (book.getYear() < stats.firstBookYear) {
stats.firstBookYear = book.getYear();
}
// Собираем уникальные жанры
for (String genre : book.getGenres()) {
if (!stats.genres.contains(genre)) {
stats.genres.add(genre);
}
}
}
// Пост-обработка: считаем среднее
for (AuthorStats stats : result.values()) {
stats.averagePrice = stats.totalPrice / stats.bookCount;
}
return result;
}
static class AuthorStats {
int bookCount;
int totalPages;
double totalPrice;
double averagePrice;
int firstBookYear;
Set<String> genres = new HashSet<>();
}Характеристики кода:
40+ строк в одном методе
4 уровня вложенности в пике
Смешение фильтрации, трансформации и агрегации
Два прохода: основной цикл + пост-обработка
#Java #для_новичков #beginner #stream_api #практика
👍4
Мутация состояния на каждом шагу
Шаг 1: Выделить источник данных в stream
Не меняем логику — только оборачиваем в stream:
Что достигли: Начали привыкать к синтаксису. Никаких функциональных преимуществ ещё нет — это подготовка.
Шаг 2: Заменить простые фильтры
Выносим continue как предикаты filter. Каждый фильтр — отдельная проверка:
Улучшения:
Фильтры вынесены, читаются как декларативные условия
Заменили if (stats == null) на computeIfAbsent
Жанры теперь через forEach
Проблема осталась: Сложная логика агрегации всё ещё внутри forEach с мутацией.
#Java #для_новичков #beginner #stream_api #практика
Шаг 1: Выделить источник данных в stream
Не меняем логику — только оборачиваем в stream:
public Map<String, AuthorStats> calculateAuthorStatsStep1(List<Book> books, int minYear) {
Map<String, AuthorStats> result = new HashMap<>();
books.stream().forEach(book -> { // Заменили for-each на forEach
// ... весь прежний код без изменений ...
});
// пост-обработка
result.values().forEach(stats ->
stats.averagePrice = stats.totalPrice / stats.bookCount
);
return result;
}Что достигли: Начали привыкать к синтаксису. Никаких функциональных преимуществ ещё нет — это подготовка.
Шаг 2: Заменить простые фильтры
Выносим continue как предикаты filter. Каждый фильтр — отдельная проверка:
public Map<String, AuthorStats> calculateAuthorStatsStep2(List<Book> books, int minYear) {
Map<String, AuthorStats> result = new HashMap<>();
books.stream()
.filter(book -> book.getYear() >= minYear) // было: if (year < minYear) continue
.filter(book -> !book.getGenres().isEmpty()) // было: if (genres.isEmpty()) continue
.filter(book -> { // было: if (price == null || price <= 0) continue
Double price = book.getPrice();
return price != null && price > 0;
})
.forEach(book -> {
// ... урезанный цикл без фильтров ...
String author = book.getAuthor();
AuthorStats stats = result.computeIfAbsent(author, k -> {
AuthorStats s = new AuthorStats();
s.firstBookYear = book.getYear();
return s;
});
stats.bookCount++;
stats.totalPages += book.getPages();
stats.totalPrice += book.getPrice();
if (book.getYear() < stats.firstBookYear) {
stats.firstBookYear = book.getYear();
}
book.getGenres().forEach(genre -> stats.genres.add(genre));
});
result.values().forEach(stats ->
stats.averagePrice = stats.totalPrice / stats.bookCount
);
return result;
}Улучшения:
Фильтры вынесены, читаются как декларативные условия
Заменили if (stats == null) на computeIfAbsent
Жанры теперь через forEach
Проблема осталась: Сложная логика агрегации всё ещё внутри forEach с мутацией.
#Java #для_новичков #beginner #stream_api #практика
👍4
Шаг 3: Вынести преобразование в map
Выделяем «чистые» данные для агрегации — промежуточный объект:
Улучшения:
Разделили фильтрацию, группировку и агрегацию
Каждый этап — отдельный, тестируемый блок
Убрали мутацию внутри потока
Новая проблема: Два прохода по данным, избыточные промежуточные структуры.
Шаг 4: Сложная агрегация через collect
Объединяем всё в один проход с кастомным коллектором:
Достигнуто:
Один проход по данным
Нет промежуточных структур
Потенциально parallelizable
Цена: 30 строк сложного кода вместо 40 строк простого. Стало ли читаемее?
#Java #для_новичков #beginner #stream_api #практика
Выделяем «чистые» данные для агрегации — промежуточный объект:
// Вспомогательный record для промежуточных данных
record BookData(String author, int year, int pages, double price, List<String> genres) {}
public Map<String, AuthorStats> calculateAuthorStatsStep3(List<Book> books, int minYear) {
Map<String, List<BookData>> grouped = books.stream()
.filter(book -> book.getYear() >= minYear)
.filter(book -> !book.getGenres().isEmpty())
.filter(book -> book.getPrice() != null && book.getPrice() > 0)
.map(book -> new BookData(
book.getAuthor(),
book.getYear(),
book.getPages(),
book.getPrice(),
book.getGenres()
))
.collect(Collectors.groupingBy(BookData::author));
// Отдельный проход для агрегации
Map<String, AuthorStats> result = new HashMap<>();
grouped.forEach((author, dataList) -> {
AuthorStats stats = new AuthorStats();
stats.bookCount = dataList.size();
stats.totalPages = dataList.stream().mapToInt(BookData::pages).sum();
stats.totalPrice = dataList.stream().mapToDouble(BookData::price).sum();
stats.averagePrice = stats.totalPrice / stats.bookCount;
stats.firstBookYear = dataList.stream()
.mapToInt(BookData::year)
.min()
.orElse(0);
stats.genres = dataList.stream()
.flatMap(d -> d.genres().stream())
.collect(Collectors.toSet());
result.put(author, stats);
});
return result;
}
Улучшения:
Разделили фильтрацию, группировку и агрегацию
Каждый этап — отдельный, тестируемый блок
Убрали мутацию внутри потока
Новая проблема: Два прохода по данным, избыточные промежуточные структуры.
Шаг 4: Сложная агрегация через collect
Объединяем всё в один проход с кастомным коллектором:
public Map<String, AuthorStats> calculateAuthorStatsStep4(List<Book> books, int minYear) {
return books.stream()
.filter(book -> book.getYear() >= minYear)
.filter(book -> !book.getGenres().isEmpty())
.filter(book -> book.getPrice() != null && book.getPrice() > 0)
.collect(Collectors.groupingBy(
Book::getAuthor,
Collector.of(
AuthorStats::new, // supplier
(stats, book) -> { // accumulator
if (stats.bookCount == 0) {
stats.firstBookYear = book.getYear();
} else {
stats.firstBookYear = Math.min(stats.firstBookYear, book.getYear());
}
stats.bookCount++;
stats.totalPages += book.getPages();
stats.totalPrice += book.getPrice();
stats.genres.addAll(book.getGenres());
},
(left, right) -> { // combiner для parallelStream
if (left.bookCount == 0) return right;
if (right.bookCount == 0) return left;
AuthorStats merged = new AuthorStats();
merged.bookCount = left.bookCount + right.bookCount;
merged.totalPages = left.totalPages + right.totalPages;
merged.totalPrice = left.totalPrice + right.totalPrice;
merged.firstBookYear = Math.min(left.firstBookYear, right.firstBookYear);
merged.genres.addAll(left.genres);
merged.genres.addAll(right.genres);
return merged;
},
stats -> { // finisher
stats.averagePrice = stats.totalPrice / stats.bookCount;
return stats;
}
)
));
}Достигнуто:
Один проход по данным
Нет промежуточных структур
Потенциально parallelizable
Цена: 30 строк сложного кода вместо 40 строк простого. Стало ли читаемее?
#Java #для_новичков #beginner #stream_api #практика
👍4
Шаг 5: Точка остановки — прагматичный компромисс
Вернёмся к шагу 3 и доработаем его до читаемого состояния без избыточной сложности:
Почему это лучше шага 4:
Методы isValidForStats и aggregateStats можно тестировать отдельно
Нет 30-строчного лямбда-ада в коллекторе
Junior-разработчик поймёт код за 5 минут
Легко добавить логирование или метрики в промежуточные точки
Критерии остановки рефакторинга
Остановитесь, если:
Код стал медленнее без причины
Появились неочевидные побочные эффекты
Теряется отладочная информация
В исходном коде можно было поставить breakpoint на любой continue. В сложном коллекторе — только внутри лямбд.
Команда не владеет паттернами
Если 3 из 5 разработчиков не знают Collector.of — код неподдерживаем.
Практические задания
Задача 1: рефакторинг с остановкой
В проекте «Библиотека» найдите метод с циклом, содержащим:
2+ условия continue
мутацию аккумулятора
вложенные циклы
Проведите рефакторинг до шага 3 (группировка + явная агрегация). Остановитесь. Обоснуйте выбор.
Задача 2: сравнение читаемости
Покажите шаг 4 (полный коллектор) коллеге, не знакомому с Stream API. Засеките время, за которое он поймёт логику. Повторите с шагом 5. Зафиксируйте разницу.
Задача 3: добавление функциональности
К обоим вариантам (шаг 4 и шаг 5) добавьте требование: «пропускать авторов с менее чем 3 книгами».
В каком варианте изменение проще? В каком меньше риск регрессии?
Задача 4: документация компромисса (звёздочка)
Создайте в проекте docs/stream-guidelines.md с правилами:
когда использовать Stream
когда остановиться
примеры «хорошего», «плохого» и «достаточного» кода из вашей кодовой базы
#Java #для_новичков #beginner #stream_api #практика
Вернёмся к шагу 3 и доработаем его до читаемого состояния без избыточной сложности:
public Map<String, AuthorStats> calculateAuthorStatsPragmatic(List<Book> books, int minYear) {
// Предварительная фильтрация — ясная и тестируемая
List<Book> validBooks = books.stream()
.filter(this::isValidForStats)
.filter(book -> book.getYear() >= minYear)
.collect(Collectors.toList());
// Группировка — стандартная операция
Map<String, List<Book>> byAuthor = validBooks.stream()
.collect(Collectors.groupingBy(Book::getAuthor));
// Агрегация — явный цикл с понятной логикой
Map<String, AuthorStats> result = new HashMap<>();
for (Map.Entry<String, List<Book>> entry : byAuthor.entrySet()) {
result.put(entry.getKey(), aggregateStats(entry.getValue()));
}
return result;
}
private boolean isValidForStats(Book book) {
return !book.getGenres().isEmpty()
&& book.getPrice() != null
&& book.getPrice() > 0;
}
private AuthorStats aggregateStats(List<Book> authorBooks) {
AuthorStats stats = new AuthorStats();
stats.bookCount = authorBooks.size();
stats.totalPages = authorBooks.stream()
.mapToInt(Book::getPages)
.sum();
stats.totalPrice = authorBooks.stream()
.mapToDouble(Book::getPrice)
.sum();
stats.averagePrice = stats.totalPrice / stats.bookCount;
stats.firstBookYear = authorBooks.stream()
.mapToInt(Book::getYear)
.min()
.orElse(0);
stats.genres = authorBooks.stream()
.flatMap(b -> b.getGenres().stream())
.collect(Collectors.toSet());
return stats;
}Почему это лучше шага 4:
Методы isValidForStats и aggregateStats можно тестировать отдельно
Нет 30-строчного лямбда-ада в коллекторе
Junior-разработчик поймёт код за 5 минут
Легко добавить логирование или метрики в промежуточные точки
Критерии остановки рефакторинга
Остановитесь, если:
Код стал медленнее без причины
Появились неочевидные побочные эффекты
// Опасно: параллельный stream с непотокобезопасным accumulators
.collect(Collectors.groupingByConcurrent(
Book::getAuthor,
Collector.of(
() -> new AuthorStats(), // ОК — новый для каждого
(stats, book) -> stats.genres.addAll(...), // Опасно! HashSet не thread-safe
...
)
))
Теряется отладочная информация
В исходном коде можно было поставить breakpoint на любой continue. В сложном коллекторе — только внутри лямбд.
Команда не владеет паттернами
Если 3 из 5 разработчиков не знают Collector.of — код неподдерживаем.
Практические задания
Задача 1: рефакторинг с остановкой
В проекте «Библиотека» найдите метод с циклом, содержащим:
2+ условия continue
мутацию аккумулятора
вложенные циклы
Проведите рефакторинг до шага 3 (группировка + явная агрегация). Остановитесь. Обоснуйте выбор.
Задача 2: сравнение читаемости
Покажите шаг 4 (полный коллектор) коллеге, не знакомому с Stream API. Засеките время, за которое он поймёт логику. Повторите с шагом 5. Зафиксируйте разницу.
Задача 3: добавление функциональности
К обоим вариантам (шаг 4 и шаг 5) добавьте требование: «пропускать авторов с менее чем 3 книгами».
В каком варианте изменение проще? В каком меньше риск регрессии?
Задача 4: документация компромисса (звёздочка)
Создайте в проекте docs/stream-guidelines.md с правилами:
когда использовать Stream
когда остановиться
примеры «хорошего», «плохого» и «достаточного» кода из вашей кодовой базы
#Java #для_новичков #beginner #stream_api #практика
👍4
Java for Beginner
Ради интереса добавил чат, пока думаю что там насобирать))) ✌️ Пишите, заодно и потестим))) 🧑💻
А вы уже тестили чат?
Я добавил звук уведомления, при свернутом браузере)) Так что теперь можно и пообщаться)
Я добавил звук уведомления, при свернутом браузере)) Так что теперь можно и пообщаться)
🔥2👍1
Что выведет код?
#Tasks
public class Task310326 {
public static void main(String[] args) {
int[] arr = new int[2];
System.out.println(arr + arr);
System.out.println(arr == arr);
System.out.println(arr.equals(arr));
System.out.println(arr.hashCode() == arr.hashCode());
}
}#Tasks
👍2
Варианты ответа:
Anonymous Quiz
0%
true false true true
23%
true true true true
0%
true false false false
77%
Ошибка компиляции
👍1
Как работает switch со строками (String)? 🤓
Ответ:
Поддержка String в switch появилась в Java 7.
В байт-коде это реализуется не как прямое сравнение строк, а с помощью вычисления хеш-кода строки и использования switch по int.
Сначала вычисляется hashCode() строки в case-метках, и по нему выбирается возможный блок. Затем внутри выбранного блока выполняется проверка equals() для подтверждения точного совпадения (на случай коллизий хешей).
Это делает switch по строкам несколько медленнее, чем по числам или enum.
#собеседование
Ответ:
В байт-коде это реализуется не как прямое сравнение строк, а с помощью вычисления хеш-кода строки и использования switch по int.
Сначала вычисляется hashCode() строки в case-метках, и по нему выбирается возможный блок. Затем внутри выбранного блока выполняется проверка equals() для подтверждения точного совпадения (на случай коллизий хешей).
Это делает switch по строкам несколько медленнее, чем по числам или enum.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологий сегодня — 1 апреля
ℹ️ Кто родился в этот день
Не нашел...(
🌐 Знаковые события
1940 — в Нью-Джерси впервые демонстрируется электронный микроскоп.
1960 — запущен спутник TIROS-1, впервые обеспечивший телевещание из космоса.
1972 — компания Intel представила микропроцессор 8008.
1976 — Стив Джобс, Стив Возняк и Рональд Уэйн основывают Apple Computer Company.
2004 — открылась почта Gmail от Google.
#Biography #Birth_Date #Events #01апреля
Не нашел...(
1940 — в Нью-Джерси впервые демонстрируется электронный микроскоп.
1960 — запущен спутник TIROS-1, впервые обеспечивший телевещание из космоса.
1972 — компания Intel представила микропроцессор 8008.
1976 — Стив Джобс, Стив Возняк и Рональд Уэйн основывают Apple Computer Company.
2004 — открылась почта Gmail от Google.
#Biography #Birth_Date #Events #01апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #021]
Тема: Использование intern() в (String.intern()) для уникальных строк может забить PermGen/Metaspace и вызвать утечку памяти.
Проблема: Метод String.intern() помещает строку в пул, управляемый JVM, и возвращает канонический экземпляр.
В ранних версиях Java пул находился в PermGen (Permanent Generation) — области памяти фиксированного размера (обычно 64-96 МБ). При интенсивном интернировании уникальных строк (например, полученных из внешних источников или сгенерированных динамически) пул быстро переполняется, вызывая OutOfMemoryError: PermGen space.
Начиная с Java 8, PermGen заменен на Metaspace (неограниченный по умолчанию, но ограниченный физической памятью). Однако проблема остается: интернированные строки никогда не собираются сборщиком мусора, если на них есть ссылки из пула. Даже после того, как исходная строка становится недоступной, интернированный экземпляр продолжает жить в пуле, что приводит к утечке памяти. Кроме того, операция intern() синхронизирована и в высококонкурентной среде становится узким местом.
Решение: Не используйте intern() для дедупликации строк, особенно если строки уникальны или их количество велико.
Для кэширования повторяющихся строк применяйте ConcurrentHashMap<String, String> или ConcurrentHashMap<String, WeakReference<String>> с возможностью ручного контроля размера.
Альтернатива — использовать String как есть, полагаясь на то, что JVM сама оптимизирует хранение литералов и констант. Для ограниченных наборов (например, коды ошибок, статусы) используйте enum. Если интернирование необходимо для экономии памяти при огромном количестве дубликатов, настройте размер StringTable через -XX:StringTableSize=N и используйте intern() только для строк с высокой степенью повторяемости.
Объяснение: Пул строк — это хэш-таблица (StringTable) фиксированного размера.
При интернировании строка добавляется в эту таблицу, и сборщик мусора не удаляет ее, даже если исходный объект больше не используется.
В Java 8+ Metaspace может расти до предела физической памяти, но при интенсивном интернировании уникальных строк рост становится бесконтрольным, и приложение падает с OutOfMemoryError: Metaspace.
#Java #советы
Тема: Использование intern() в (String.intern()) для уникальных строк может забить PermGen/Metaspace и вызвать утечку памяти.
Проблема: Метод String.intern() помещает строку в пул, управляемый JVM, и возвращает канонический экземпляр.
В ранних версиях Java пул находился в PermGen (Permanent Generation) — области памяти фиксированного размера (обычно 64-96 МБ). При интенсивном интернировании уникальных строк (например, полученных из внешних источников или сгенерированных динамически) пул быстро переполняется, вызывая OutOfMemoryError: PermGen space.
Начиная с Java 8, PermGen заменен на Metaspace (неограниченный по умолчанию, но ограниченный физической памятью). Однако проблема остается: интернированные строки никогда не собираются сборщиком мусора, если на них есть ссылки из пула. Даже после того, как исходная строка становится недоступной, интернированный экземпляр продолжает жить в пуле, что приводит к утечке памяти. Кроме того, операция intern() синхронизирована и в высококонкурентной среде становится узким местом.
Решение: Не используйте intern() для дедупликации строк, особенно если строки уникальны или их количество велико.
Для кэширования повторяющихся строк применяйте ConcurrentHashMap<String, String> или ConcurrentHashMap<String, WeakReference<String>> с возможностью ручного контроля размера.
Альтернатива — использовать String как есть, полагаясь на то, что JVM сама оптимизирует хранение литералов и констант. Для ограниченных наборов (например, коды ошибок, статусы) используйте enum. Если интернирование необходимо для экономии памяти при огромном количестве дубликатов, настройте размер StringTable через -XX:StringTableSize=N и используйте intern() только для строк с высокой степенью повторяемости.
import java.util.concurrent.ConcurrentHashMap;
public class StringInternRisk {
//Антипаттерн: интернирование всех строк
public static String dangerousIntern(String value) {
return value.intern(); // Попадает в Metaspace, никогда не освободится
}
//Решение: ручной пул с контролем
private static final ConcurrentHashMap<String, String> STRING_POOL = new ConcurrentHashMap<>();
public static String safeDeduplicate(String value) {
// putIfAbsent атомарно добавляет, если отсутствует
String existing = STRING_POOL.putIfAbsent(value, value);
return existing != null ? existing : value;
}
// Вариант с автоматической очисткой через WeakReference
private static final ConcurrentHashMap<String, WeakReference<String>> WEAK_POOL = new ConcurrentHashMap<>();
// Для ограниченных наборов — enum
public enum Status {
NEW, PROCESSING, COMPLETED;
// вместо "NEW".intern() используйте Status.NEW.name()
}
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = new String("Hello");
// Рискованно
String interned = s1.intern();
System.out.println(s1 == s2); // false
System.out.println(s1 == interned); // true (попал в пул)
// Безопасная альтернатива
String dedup = safeDeduplicate(s2);
System.out.println(s1 == dedup); // false, но equals true — корректно
// Демонстрация утечки: цикл с уникальными строками быстро заполнит Metaspace
for (int i = 0; i < 1_000_000; i++) {
String unique = Integer.toString(i);
// unique.intern(); // Раскомментировать — опасно, приведет к OOM
}
}
}
Объяснение: Пул строк — это хэш-таблица (StringTable) фиксированного размера.
При интернировании строка добавляется в эту таблицу, и сборщик мусора не удаляет ее, даже если исходный объект больше не используется.
В Java 8+ Metaspace может расти до предела физической памяти, но при интенсивном интернировании уникальных строк рост становится бесконтрольным, и приложение падает с OutOfMemoryError: Metaspace.
#Java #советы
👍4
Что выведет код?
#Tasks
import java.util.ArrayList;
import java.util.List;
public class Task010426 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10_000; i++) {
String s = new String("data_" + i);
String interned = s.intern();
list.add(interned);
}
String a = new String("data_5000");
String b = a.intern();
System.out.println(a == b);
System.out.println(a == list.get(5000));
System.out.println(b == list.get(5000));
}
}
#Tasks
👍1
Варианты ответа:
Anonymous Quiz
56%
true true true
0%
false true true
44%
false false true
0%
true false false
👍2
Что такое анонимные классы (Anonymous classes)? 🤓
Ответ:
Анонимные классы — это локальные внутренние классы без имени, которые объявляются и инстанцируются одновременно в одном выражении.
Они используются, когда нужен одноразовый объект с небольшими изменениями, чаще всего для реализации интерфейсов или расширения классов (например, Runnable, компараторы, обработчики событий).
Синтаксис: new Interface() { ... } или new Class() { ... }.
Анонимные классы имеют доступ к final или effectively final переменным окружающего контекста.
#собеседование
Ответ:
Анонимные классы
Они используются, когда нужен одноразовый объект с небольшими изменениями, чаще всего для реализации интерфейсов или расширения классов (например, Runnable, компараторы, обработчики событий).
Синтаксис: new Interface() { ... } или new Class() { ... }.
Анонимные классы имеют доступ к final или effectively final переменным окружающего контекста.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 2 апреля
ℹ️ Кто родился в этот день
Уо́лтер Пе́рси Кра́йслер (англ. Walter Percy Chrysler; 2 апреля 1875, Уомего, Канзас — 18 августа 1940, Кингс-Пойнт, Нью-Йорк) — американский автомобилестроитель, промышленник, основатель корпорации Chrysler.
🌐 Знаковые события
1966 — советский спутник «Луна-10» стал первым в мире космическим аппаратом, облетевшим Луну.
#Biography #Birth_Date #Events #02апреля
Уо́лтер Пе́рси Кра́йслер (англ. Walter Percy Chrysler; 2 апреля 1875, Уомего, Канзас — 18 августа 1940, Кингс-Пойнт, Нью-Йорк) — американский автомобилестроитель, промышленник, основатель корпорации Chrysler.
1966 — советский спутник «Луна-10» стал первым в мире космическим аппаратом, облетевшим Луну.
#Biography #Birth_Date #Events #02апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 8. Stream API и функциональный стиль в Java
Глава 8: За пределами коллекций. Бесконечность и I/O
Бесконечные стримы и ленивая генерация
Ранее Stream API рассматривался как инструмент обработки конечных коллекций данных. Но фундаментальная мощь потоков — в их способности моделировать бесконечные последовательности, вычисляемые по требованию. Это сдвигает фокус с "данных в памяти" к "процессу генерации", открывая паттерны для числовых последовательностей, генераторов уникальных идентификаторов, обработки потоковых источников вроде сетевых соединений.
Stream.iterate: рекурсия с состоянием
Метод Stream.iterate создаёт поток, где каждый следующий элемент вычисляется из предыдущего.
Перегрузка Java 8 принимает начальное значение и унарную функцию:
Ключевая особенность: iterate сохраняет состояние между элементами. Это делает его непараллелизуемым — каждый элемент зависит от предыдущего, разделение невозможно. Попытка вызвать .parallel() на таком потоке не даст выигрыша, а может замедлить из-за накладных расходов.
Java 9+: iterate с предикатом остановки
Перегрузка с тремя параметрами добавляет условие продолжения:
Это заменяет паттерн iterate(...).limit(n) более семантически ясным конструктом. Предикат проверяется перед генерацией каждого элемента, включая seed. Если hasNext(seed) ложно, поток пуст.
Stream.generate: чистая генерация без состояния
Stream.generate принимает Supplier — функцию без аргументов, возвращающую значение. Каждый вызов независим, что теоретически позволяет параллелизм (хотя реализация в OpenJDK не делит такие потоки эффективно).
generate идеален для стохастических или внешне определяемых последовательностей, где нет рекуррентной зависимости. Но бесконечность требует ограничения: limit, takeWhile или короткое замыкание терминальной операцией.
takeWhile и dropWhile: предикатные границы
Java 9 добавила методы takeWhile и dropWhile, критически отличные от filter по семантике:
filter проверяет каждый элемент независимо, пропуская или отбрасывая по условию
takeWhile пропускает элементы, пока предикат истинен, и останавливает поток при первом ложном
dropWhile отбрасывает элементы, пока предикат истинен, и продолжает с первого ложного
Ключевое отличие takeWhile от filter — short-circuit поведение. filter(t -> t < 25) обработал бы все элементы, включая 19, 12, 8. takeWhile останавливается, предполагая, что последовательность упорядочена и дальнейшие элементы не интересны.
Это критично для бесконечных потоков:
Без takeWhile пришлось бы использовать limit, требующий знания количества элементов заранее, или filter, не останавливающийся.
#Java #для_новичков #beginner #stream_api
Глава 8: За пределами коллекций. Бесконечность и I/O
Бесконечные стримы и ленивая генерация
Ранее Stream API рассматривался как инструмент обработки конечных коллекций данных. Но фундаментальная мощь потоков — в их способности моделировать бесконечные последовательности, вычисляемые по требованию. Это сдвигает фокус с "данных в памяти" к "процессу генерации", открывая паттерны для числовых последовательностей, генераторов уникальных идентификаторов, обработки потоковых источников вроде сетевых соединений.
Stream.iterate: рекурсия с состоянием
Метод Stream.iterate создаёт поток, где каждый следующий элемент вычисляется из предыдущего.
Перегрузка Java 8 принимает начальное значение и унарную функцию:
// Натуральные числа: 0, 1, 2, 3...
Stream<Integer> natural = Stream.iterate(0, n -> n + 1);
// Степени двойки: 1, 2, 4, 8...
Stream<Integer> powersOfTwo = Stream.iterate(1, n -> n * 2);
// Фибоначчи: пара (a, b) -> (b, a+b)
Stream<long[]> fibonacci = Stream.iterate(
new long[]{0, 1},
f -> new long[]{f[1], f[0] + f[1]}
);
Ключевая особенность: iterate сохраняет состояние между элементами. Это делает его непараллелизуемым — каждый элемент зависит от предыдущего, разделение невозможно. Попытка вызвать .parallel() на таком потоке не даст выигрыша, а может замедлить из-за накладных расходов.
Java 9+: iterate с предикатом остановки
Перегрузка с тремя параметрами добавляет условие продолжения:
// Числа от 0 до 99
Stream<Integer> limited = Stream.iterate(
0, // seed
n -> n < 100, // hasNext (предикат продолжения)
n -> n + 1 // next (функция следующего)
);
Это заменяет паттерн iterate(...).limit(n) более семантически ясным конструктом. Предикат проверяется перед генерацией каждого элемента, включая seed. Если hasNext(seed) ложно, поток пуст.
Stream.generate: чистая генерация без состояния
Stream.generate принимает Supplier — функцию без аргументов, возвращающую значение. Каждый вызов независим, что теоретически позволяет параллелизм (хотя реализация в OpenJDK не делит такие потоки эффективно).
// Постоянное значение
Stream<String> constants = Stream.generate(() -> "repeat");
// Случайные числа
Stream<Double> randoms = Stream.generate(Math::random);
// Уникальные ID через атомарный счётчик
AtomicLong idGenerator = new AtomicLong(0);
Stream<String> uniqueIds = Stream.generate(() -> "ID-" + idGenerator.incrementAndGet());
generate идеален для стохастических или внешне определяемых последовательностей, где нет рекуррентной зависимости. Но бесконечность требует ограничения: limit, takeWhile или короткое замыкание терминальной операцией.
takeWhile и dropWhile: предикатные границы
Java 9 добавила методы takeWhile и dropWhile, критически отличные от filter по семантике:
filter проверяет каждый элемент независимо, пропуская или отбрасывая по условию
takeWhile пропускает элементы, пока предикат истинен, и останавливает поток при первом ложном
dropWhile отбрасывает элементы, пока предикат истинен, и продолжает с первого ложного
// Сортированный поток температур
IntStream temperatures = IntStream.of(15, 18, 22, 25, 19, 12, 8);
// takeWhile: температура ниже 25
temperatures.takeWhile(t -> t < 25)
.forEach(System.out::println); // 15, 18, 22 — остановка на 25
// dropWhile: пропускаем прохладную погоду
temperatures.dropWhile(t -> t < 20)
.forEach(System.out::println); // 22, 25, 19, 12, 8
Ключевое отличие takeWhile от filter — short-circuit поведение. filter(t -> t < 25) обработал бы все элементы, включая 19, 12, 8. takeWhile останавливается, предполагая, что последовательность упорядочена и дальнейшие элементы не интересны.
Это критично для бесконечных потоков:
// Бесконечная последовательность, ограниченная условием
Stream.iterate(1, n -> n * 2)
.takeWhile(n -> n < 1000) // 1, 2, 4, 8, ..., 512 — остановка
.forEach(System.out::println);
Без takeWhile пришлось бы использовать limit, требующий знания количества элементов заранее, или filter, не останавливающийся.
#Java #для_новичков #beginner #stream_api
👍6
Сценарий: генерация уникальных идентификаторов
Комбинация generate с takeWhile или limit создаёт контролируемые генераторы:
Атомарные структуры (AtomicLong, ConcurrentHashMap) обеспечивают потокобезопасность при параллельном доступе, хотя generate в стандартной реализации не эффективно параллелится.
Сценарий: чтение потоковых данных
Бесконечные стримы моделируют внешние источники с неизвестным объёмом:
```
// Симуляция чтения из сокета: байты до терминатора
Stream<Byte> socketStream = Stream.generate(() -> readFromSocket())
.takeWhile(b -> b != -1); // -1 как EOF
// Обработка пакетов до специального маркера
Stream<Packet> packetStream = Stream.generate(this::readNextPacket)
.takeWhile(p -> !p.isTerminator());
Важно: такие потоки требуют управления ресурсами. Stream.generate не знает о необходимости закрыть сокет. Использование в try-with-resources или явное закрытие источника в Supplier — ответственность разработчика.
Практика: числовые последовательности с ленивостью
Сравним подходы к генерации последовательностей:
Ленивость позволяет работать с "потенциально бесконечными" последовательностями, фактически обрабатывая только необходимый минимум.
Ограничения бесконечных потоков
Параллелизм: iterate не параллелится, generate — плохо. Бесконечные потоки — последовательная абстракция.
Состояние: iterate требует небольшого состояния (предыдущий элемент), но не масштабируется. Сложное состояние в Supplier требует синхронизации.
Ресурсы: бесконечность — концептуальная. Реальные источники (сокеты, файлы) требуют закрытия. Stream API не управляет жизненным циклом внешних ресурсов автоматически.
#Java #для_новичков #beginner #stream_api
Комбинация generate с takeWhile или limit создаёт контролируемые генераторы:
// UUID с проверкой уникальности в пределах сессии
Set<String> usedIds = ConcurrentHashMap.newKeySet();
Stream<String> uniqueIds = Stream.generate(UUID::randomUUID)
.map(UUID::toString)
.filter(usedIds::add) // add возвращает false если уже есть, фильтруем дубликаты
.limit(1000); // строго 1000 уникальных
// Или с takeWhile для условной остановки
Stream<String> idsUntilPattern = Stream.generate(this::generateId)
.takeWhile(id -> !id.contains("STOP")); // Генерация до спецпаттерна
Атомарные структуры (AtomicLong, ConcurrentHashMap) обеспечивают потокобезопасность при параллельном доступе, хотя generate в стандартной реализации не эффективно параллелится.
Сценарий: чтение потоковых данных
Бесконечные стримы моделируют внешние источники с неизвестным объёмом:
```
// Симуляция чтения из сокета: байты до терминатора
Stream<Byte> socketStream = Stream.generate(() -> readFromSocket())
.takeWhile(b -> b != -1); // -1 как EOF
// Обработка пакетов до специального маркера
Stream<Packet> packetStream = Stream.generate(this::readNextPacket)
.takeWhile(p -> !p.isTerminator());
Важно: такие потоки требуют управления ресурсами. Stream.generate не знает о необходимости закрыть сокет. Использование в try-with-resources или явное закрытие источника в Supplier — ответственность разработчика.
Практика: числовые последовательности с ленивостью
Сравним подходы к генерации последовательностей:
// Плохо: немедленное создание списка
List<Integer> eager = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
if (isPrime(i)) eager.add(i);
}
// Хорошо: ленивый поток, элементы вычисляются по требованию
IntStream primes = IntStream.iterate(2, n -> n + 1)
.filter(this::isPrime)
.takeWhile(n -> n < 1000000);
// Потребление только первых 10, остальные никогда не вычислены
primes.limit(10).forEach(System.out::println);
Ленивость позволяет работать с "потенциально бесконечными" последовательностями, фактически обрабатывая только необходимый минимум.
Ограничения бесконечных потоков
Параллелизм: iterate не параллелится, generate — плохо. Бесконечные потоки — последовательная абстракция.
Состояние: iterate требует небольшого состояния (предыдущий элемент), но не масштабируется. Сложное состояние в Supplier требует синхронизации.
Ресурсы: бесконечность — концептуальная. Реальные источники (сокеты, файлы) требуют закрытия. Stream API не управляет жизненным циклом внешних ресурсов автоматически.
#Java #для_новичков #beginner #stream_api
👍5
Please open Telegram to view this post
VIEW IN TELEGRAM
🤣10
Что выведет код?
#Tasks
import java.util.stream.Stream;
public class Task020426 {
public static void main(String[] args) {
Stream<Integer> stream = Stream.iterate(0, n -> n + 1)
.filter(n -> n % 2 == 0)
.map(n -> {
System.out.print(n + " ");
return n;
})
.limit(3);
stream.forEach(n -> {});
}
}
#Tasks
👍1