Java for Beginner
871 subscribers
1.01K photos
275 videos
14 files
1.68K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что такое Thread.onSpinWait() (Java 9)? 🤓

Ответ:

Thread.onSpinWait()
 — метод, который подсказывает JVM и процессору, что текущий поток выполняет активное ожидание (busy-wait).

Это позволяет процессору оптимизировать энергопотребление и эффективность, например, переключить ядро в режим пониженного потребления.

Используется в низкоуровневых lock-free алгоритмах (например, ожидание в AbstractQueuedSynchronizer). Обычно вызывается внутри цикла, где ожидается некоторое условие, но блокировка неэффективна.

Улучшает производительность по сравнению с пустым циклом.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 22 июля

ℹ️ Кто родился в этот день

Па́вел О́сипович Сухо́й (бел. Павел Восіпавіч Сухі; 10 [22] июля 1895, Глубокое, Дисненский уезд, Виленская губерния, Российская империя (ныне Витебская область, Беларусь) — 15 сентября 1975МоскваСССР) — советский авиаконструктор, доктор технических наук, один из основателей советской реактивной и сверхзвуковой авиации.

Гу́став Лю́двиг Герц (нем. Gustav Ludwig Hertz; 22 июля 1887, Гамбург — 30 октября 1975, Берлин) — немецкий физик, лауреат Нобелевской премии по физике в 1925 году «за открытие законов соударения электрона с атомом» (совместно с Джеймсом Франком).

Габриэ́ль Ламе́ (фр. Gabriel Lamé; 22 июля 1795, Тур — 1 мая 1870, Париж) — французский математикмеханикфизик и инженерчлен-корреспондент Петербургской академии наук (1829), член Парижской академии наук (1843), профессор Политехнической школы (1832—1863) и Парижского университета (1848—1863). В 1820—1831 годах работал в России, в петербургском Институте Корпуса инженеров путей сообщения. Основные труды — по математической физике и теории упругости. Разработал общую теорию криволинейных координат (1833), ввёл так называемые коэффициенты Ламе (1859) и специальный класс функций (1839, функции Ламе). В честь него названы параметры Ламе в теории упругости.


🌐 Знаковые события

1961 — первый полёт первого советского экраноплана «СМ-1».

1996 — вышла компьютерная игра Quake.


#Biography #Birth_Date #Events #22июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #063]

Тема: String.getBytes() использует платформенно-зависимую кодировку.

Проблема: Метод String.getBytes() без аргументов преобразует строку в массив байт, используя системную кодировку по умолчанию, которая определяется операционной системой и настройками JVM (file.encoding).

На разных платформах это может быть CP1251 (Windows), UTF-8 (Linux), MacRoman (macOS) или другие. Это приводит к тому, что одно и то же приложение на разных машинах генерирует разные байтовые последовательности для одинаковых строк, что вызывает ошибки при передаче данных по сети, записи в файлы или взаимодействии с внешними системами. Особенно критично для символов вне ASCII (кириллица, иероглифы) — они могут быть испорчены или потеряны. Даже если сейчас всё работает, при смене окружения (например, при деплое на сервер с другой кодировкой) приложение внезапно ломается, и диагностика затруднена, так как проблема проявляется только в определённых условиях.

Решение: Всегда указывайте кодировку явно с помощью String.getBytes(Charset) или String.getBytes(String charsetName).

Предпочтительно использовать константы из StandardCharsets (Java 7+), например, StandardCharsets.UTF_8. Это гарантирует одинаковое поведение на всех платформах и делает намерения разработчика явными. Аналогичное правило применяется к конструктору new String(byte[], Charset) и другим методам преобразования между строками и байтами.

Для проектов, ориентированных на международное взаимодействие, UTF-8 является стандартом де-факто.
import java.nio.charset.StandardCharsets;
import java.io.UnsupportedEncodingException;

public class StringGetBytes {

public static void main(String[] args) throws UnsupportedEncodingException {
String text = "Привет, мир!";

//Антипаттерн: платформенно-зависимая кодировка
byte[] badBytes = text.getBytes();
System.out.println("Длина (default): " + badBytes.length);
// На Windows (CP1251) результат может быть другим, чем на Linux (UTF-8)

//Решение: явная кодировка UTF-8
byte[] goodBytes = text.getBytes(StandardCharsets.UTF_8);
System.out.println("Длина (UTF-8): " + goodBytes.length);

// Также при обратном преобразовании
String restored = new String(goodBytes, StandardCharsets.UTF_8);
System.out.println("Восстановлено: " + restored);

// Для старых версий Java (до 7) используйте строковое имя
// byte[] bytes = text.getBytes("UTF-8");
// Но StandardCharsets предпочтительнее (типобезопасно)

// Демонстрация различий: символы вне ASCII кодируются по-разному
String euro = "€"; // Символ евро
byte[] utf8 = euro.getBytes(StandardCharsets.UTF_8); // 3 байта (E2 82 AC)
byte[] win1251 = euro.getBytes("Windows-1251"); // 1 байт (80) или не поддерживается
System.out.println("UTF-8 length: " + utf8.length);
System.out.println("Windows-1251 length: " + win1251.length);
}
}


Объяснение:
 Кодировка по умолчанию определяется системным свойством file.encoding и может быть изменена при запуске JVM (например, -Dfile.encoding=UTF-8).

Однако это глобальная настройка, которая может быть неочевидна для других разработчиков, а её изменение может повлиять на другие части системы (например, чтение файлов). Явное указание кодировки делает код самодокументируемым и устойчивым к изменениям окружения. UTF-8 является предпочтительной кодировкой для обмена данными в современных системах: она поддерживает все символы Unicode, эффективна для текстов на латинице и широко используется в вебе, JSON, XML.

Использование StandardCharsets.UTF_8 исключает проверяемое исключение UnsupportedEncodingException, так как UTF-8 гарантированно поддерживается всеми реализациями JVM.


#Java #советы
👍5
Что выведет код?

public class Task220726 {
public static void main(String[] args) {
String s = "é";
System.out.println(s.getBytes().length);
}
}


#Tasks
👍2
👍2
Что такое ForkJoinPool.commonPool() и когда его использовать? 🤓

Ответ:

ForkJoinPool.commonPool() 
— это общий пул потоков для приложения (Java 8), используемый по умолчанию в параллельных стримах (parallelStream()) и CompletableFuture (в некоторых методах).

Размер по умолчанию = количество процессоров − 1.

Не рекомендуется блокировать (спать, ввод-вывод) внутри задач, выполняющихся в commonPool, так как это замедлит другие задачи.

Если ваши задачи могут блокироваться, создавайте отдельный ForkJoinPool. Используйте commonPool для коротких вычислительных задач, не блокирующих IO.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 23 июля

ℹ️ Кто родился в этот день

Влади́мир Я́ковлевич Кли́мов (1892—1962) — советский учёный в области авиационного моторостроенияконструктор авиационных двигателейгенерал-майор инженерно-авиационной службы (1944), академик АН СССР (1953). В ходе научных исследований и конструкторской работы Климовым были разработаны и внедрены специальная закрытая система жидкостного охлаждения поршневых авиационных двигателей под давлением, воздушный нагнетатель с двухскоростным приводом, усовершенствованная система газораспределения, система питания топливовоздушной смесью мощных и высокооборотных авиадвигателей, предложен ряд оригинальных решений в конструкциях ТРД. Климов внёс существенный вклад в развитие теории смазки, в решение проблем уравновешивания поршневых авиадвигателей и др. вопросов двигателестроения.


🌐 Знаковые события

1874 — Александр Николаевич Лодыгин получил привилегию № 1619 на лампу накаливания. Его изобретение было запатентовано и в нескольких европейских странах, Петербургская АН присудила ему в этом году Ломоносовскую премию, а в конце года было создано «Товарищество электрического освещения А. Н. Лодыгин и Ко».

1903 — спустя месяц после образования корпорации «Ford Motor Company» она выпустила в продажу свой первый автомобиль «модель А». В течение первого года своего существования компания произвела 1700 автомобилей. Первый автомобиль, выставленный на продажу, был описан как «наиболее совершенная машина на рынке, которую в состоянии водить даже 15-летний мальчик». Первый автомобиль был продан доктору Е. Пфеннингу (англ. Ernst Pfenning) из Чикаго, который купил машину месяц спустя после регистрации компании, к большой радости обеспокоенных акционеров, нервно наблюдавших за уменьшением банковского баланса до $223.

1980 — запуск космического корабля Союз-37, в составе его экипажа в космос отправился первый космонавт из Азиивьетнамец Фам Туан (Phạm Tuân).

2015 — с помощью телескопа «Кеплер» найдена первая экзопланета земного типа, которая находится в зоне обитаемости солнцеподобной звезды — Kepler-452 b.


#Biography #Birth_Date #Events #23июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

Глава 2. Современный NIO.2 (java.nio.file)

Files.lines() и WatchService — построчное чтение и мониторинг файловой системы

Два механизма в Java NIO.2 решают противоположные задачи: Files.lines() читает содержимое файла построчно, а WatchService наблюдает за изменениями в файловой системе без активного чтения. Первый экономит память при обработке больших текстовых файлов, второй — CPU при ожидании событий. Оба требуют строгого управления ресурсами, так как работают с нативными дескрипторами ОС, невидимыми для GC.


Files.lines(Path) — построчное чтение текстовых файлов

Метод Files.lines(Path path) возвращает Stream<String>, который лениво читает файл построчно. Каждая строка декодируется из байтов файла в символы с использованием кодировки по умолчанию (StandardCharsets.UTF_8).
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;

public class LineReader {

public void readLines(Path file) throws IOException {
// Stream<String> — ленивое чтение, строки появляются по мере обхода
try (Stream<String> lines = Files.lines(file)) {
lines.forEach(line -> System.out.println(line));
}
// BufferedReader закрыт автоматически try-with-resources
}
}

Важно: Files.lines() открывает BufferedReader под капотом, который держит файловый дескриптор открытым. Поток Stream<String> должен быть закрыт через try-with-resources или явный close(). Если поток не закрыт, дескриптор останется открытым даже после того, как Stream будет собран GC.

BufferedReader — это класс в Java, который буферизует чтение символов из потока. Вместо чтения по одному символу или байту он читает блоками (обычно 8 КБ) в буфер в памяти, а затем отдаёт символы из буфера. Это снижает количество системных вызовов read() с O(N) до O(N / buffer_size).

Files.lines(Path, Charset) — с указанием кодировки
import java.nio.charset.StandardCharsets;

public void readWithEncoding(Path file) throws IOException {
// Явное указание кодировки — обязательно для файлов не в UTF-8
try (Stream<String> lines = Files.lines(file, StandardCharsets.ISO_8859_1)) {
lines.forEach(System.out::println);
}
}

Если кодировка не указана, используется Charset.defaultCharset(), которая зависит от ОС и локали. На Linux обычно UTF-8, на Windows — Windows-1252 или CP1251. Неявное использование дефолтной кодировки — распространённый источник багов при переносе приложения между платформами.

Кодировка (charset, character encoding) — это схема сопоставления байтовых последовательностей символам текста. UTF-8 — переменная длина (1–4 байта на символ), обратно совместима с ASCII. ISO-8859-1 (Latin-1) — фиксированная 1 байт на символ, покрывает западноевропейские языки.


Ленивость и потребление памяти

Files.lines() — ленивый поток. Строка читается из файла только когда терминальная операция запрашивает следующий элемент. Это позволяет обрабатывать файлы любого размера с константным потреблением памяти:
public void processHugeFile(Path file) throws IOException {
try (Stream<String> lines = Files.lines(file)) {
// Фильтрация и подсчёт — каждая строка обрабатывается и отбрасывается
long errorCount = lines.filter(line -> line.contains("ERROR"))
.count();
System.out.println("Ошибок: " + errorCount);
// Память не зависит от размера файла — только от длины одной строки
}
}

В отличие от Files.readAllLines(), который загружает весь файл в List<String> (потенциально гигабайты в куче), Files.lines() держит в памяти только текущую строку и буфер BufferedReader (8 КБ).


WatchService — отслеживание изменений в файловой системе

WatchService — это сервис в NIO.2, который использует нативные механизмы ОС для мониторинга изменений в файловой системе. Вместо периодического опроса (polling) он работает по событийной модели: ОС уведомляет JVM, когда происходит зарегистрированное событие.

Polling (опрос) — это стратегия проверки состояния, при которой программа периодически запрашивает ресурс (например, проверяет время модификации файла). Это расходует CPU впустую. Событийная модель (event-driven) — противоположный подход, при котором программа ждёт уведомления от ОС и не потребляет ресурсы в ожидании.


Нативные механизмы ОС

WatchService в Java — это тонкая обёртка над нативными API:
Linux: inotify — система уведомлений о событиях файловой системы на уровне ядра. Каждый WatchService создаёт экземпляр inotify через системный вызов inotify_init().
macOS: FSEvents — фреймворк уведомлений о изменениях файловой системы. Работает на уровне всей ФС, а не отдельных директорий, что даёт другую семантику событий.
Windows: ReadDirectoryChangesW — WinAPI функция для асинхронного чтения изменений директории.

Важно: WatchService не гарантирует доставку всех событий и не гарантирует порядок. Некоторые события могут быть пропущены при высокой нагрузке, и события могут приходить пачками (coalescing).

Coalescing (слияние событий) — это оптимизация, при которой несколько последовательных событий над одним и тем же файлом объединяются в одно. Например, множественные быстрые записи в файл могут породить только одно событие ENTRY_MODIFY.

Создание WatchService
import java.nio.file.*;

public class WatchServiceFactory {

public WatchService createWatchService() throws IOException {
// Получаем WatchService для файловой системы по умолчанию
WatchService watchService = FileSystems.getDefault().newWatchService();
return watchService;
}
}

FileSystems.getDefault().newWatchService() создаёт нативный дескриптор inotify (Linux), регистрирует callback в FSEvents (macOS) или создаёт объект ожидания ReadDirectoryChangesW (Windows). Этот дескриптор существует в нативной памяти вне кучи JVM.

Регистрация директории
import java.nio.file.*;

public class DirectoryRegistrar {

public void registerDirectory(Path dir, WatchService watchService) throws IOException {
// Регистрируем директорию на отслеживание создания, удаления и модификации
WatchKey key = dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE, // создание файла/директории
StandardWatchEventKinds.ENTRY_DELETE, // удаление
StandardWatchEventKinds.ENTRY_MODIFY // изменение содержимого или метаданных
);

// WatchKey — это токен, идентифицирующий зарегистрированную директорию
// Его можно использовать для отмены регистрации
System.out.println("Директория зарегистрирована: " + dir);
}
}

WatchKey — это объект, представляющий регистрацию директории в WatchService. Он содержит ссылку на зарегистрированный путь и очередь событий. Когда WatchKey становится недействительным (например, директория удалена), его метод isValid() возвращает false.


#Java #для_новичков #beginner #IO #NIO #Files #Files_lines #WatchService
👍5
Ожидание событий: take() vs poll()
import java.nio.file.*;
import java.util.concurrent.TimeUnit;

public class EventPoller {

// Блокирующее ожидание: поток ждёт, пока не появится событие
public void blockingWait(WatchService watchService) throws InterruptedException {
// take() блокирует поток до появления события
// Прерывается только при InterruptedException или закрытии WatchService
WatchKey key = watchService.take();
processKey(key);
}

// Неблокирующая проверка: возвращает null, если событий нет
public void nonBlockingPoll(WatchService watchService) {
WatchKey key = watchService.poll();
if (key != null) {
processKey(key);
} else {
System.out.println("Нет новых событий");
}
}

// Ожидание с таймаутом
public void timedPoll(WatchService watchService) throws InterruptedException {
// Ждём 5 секунд, если событий нет — возвращаем null
WatchKey key = watchService.poll(5, TimeUnit.SECONDS);
if (key != null) {
processKey(key);
}
}

private void processKey(WatchKey key) {
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context(); // имя файла, вызвавшего событие

System.out.println("Событие: " + kind.name() + " для " + fileName);
}
// Сбрасываем ключ, чтобы получать новые события
boolean valid = key.reset();
if (!valid) {
System.out.println("Ключ больше не валиден — директория удалена");
}
}
}

take() — блокирующий метод, который переводит поток в состояние WAITING. Поток не потребляет CPU в этом состоянии. ОС пробуждает поток, когда приходит событие inotify/FSEvents/ReadDirectoryChangesW.
poll() — неблокирующий. Если событий нет, немедленно возвращает null. Полезен в циклах, где нужно делать другую работу между проверками.
poll(timeout, TimeUnit) — блокирует на указанное время, затем возвращает null или WatchKey.

Важно: после обработки событий необходимо вызвать key.reset(). Это сбрасывает состояние ключа и позволяет WatchService снова наполнять его событиями. Если reset() не вызван, ключ перестаёт получать новые события. Если reset() возвращает false, ключ больше не валиден (обычно потому, что директория удалена).


Обработка WatchEvent
WatchEvent<?> event = ...;

// Тип события: ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY
WatchEvent.Kind<?> kind = event.kind();

// Имя файла или директории, вызвавшей событие (относительно зарегистрированной директории)
Path context = (Path) event.context();

// Количество событий данного типа для этого файла (может быть > 1 при coalescing)
int count = event.count();


event.context()
возвращает Path, но это не абсолютный путь — это имя файла относительно зарегистрированной директории. Для получения абсолютного пути нужно резолвить через watchable() ключа:
Path dir = (Path) key.watchable();  // зарегистрированная директория
Path absolute = dir.resolve(context); // полный путь


Важные нюансы WatchService

Событие CREATE приходит до завершения записи: если процесс создаёт файл и медленно пишет в него, ENTRY_CREATE приходит сразу после появления inode. Файл может быть ещё пустым или заблокирован на запись. Решение — ожидание готовности (как в waitForFileReady()) или использование временных имён с последующим атомарным переименованием.

Событие MODIFY может приходить многократно: одна логическая операция записи может породить серию событий ENTRY_MODIFY. Приложение должно быть готово к дедупликации или игнорированию промежуточных событий.

Символические ссылки: WatchService следует по символическим ссылкам при регистрации. Если зарегистрировать ссылку, события будут приходить для целевой директории.

Рекурсивный мониторинг: WatchService не мониторит поддиректории автоматически. Для рекурсивного мониторинга нужно обходить дерево и регистрировать каждую директорию отдельно, а при появлении новой директории (событие ENTRY_CREATE для директории) регистрировать её динамически.

OVERFLOW: при слишком быстром потоке событий внутренний буфер ОС может переполниться. В этом случае приходит событие OVERFLOW, и часть событий теряется. Приложение должно обрабатывать это как сигнал к полному пересканированию директории.


Путь байтов в памяти JVM при построчном чтении

Files.lines(): путь данных

При Files.lines(file):
Метод создаёт BufferedReader — объект в куче, который оборачивает InputStreamReader, который оборачивает FileInputStream. Каждый из этих объектов — в куче. FileInputStream содержит файловый дескриптор (целое число в нативной памяти).
BufferedReader выделяет буфер char[] размером 8192 символов (по умолчанию) в Young Generation. Этот буфер переиспользуется для всех операций чтения.
При итерации по Stream<String> каждый вызов readLine() выполняет:
Чтение байтов из файла через FileInputStream.read() в буфер BufferedReader.
Декодирование байтов в символы через CharsetDecoder (для UTF-8 — переменная длина, 1–4 байта на символ).
Поиск символа перевода строки (\n, \r\n или \r).
Создание нового объекта String для строки.
Объект String создаётся в Young Generation (Eden). Если строка длинная (например, CSV-строка с тысячей символов), она может быть размещена сразу в Old Generation как humongous object (в G1 GC, если размер превышает половину региона).
После обработки строки в терминальной операции (например, count() или forEach()) ссылка на String исчезает. Объект становится мусором и собирается Minor GC. Буфер BufferedReader остаётся живым до закрытия потока.
При закрытии Stream (через try-with-resources) вызывается BufferedReader.close(), который закрывает FileInputStream, который выполняет системный вызов close() для освобождения файлового дескриптора. Если поток не закрыт, дескриптор утечёт.


Путь байтов при WatchService


При создании WatchService:
FileSystems.getDefault().newWatchService() создаёт объект WatchService в куче. На Linux это обёртка над InotifyWatchService, который выполняет системный вызов inotify_init().
inotify_init() создаёт экземпляр inotify в ядре Linux и возвращает файловый дескриптор. Этот дескриптор хранится в нативной памяти JVM, ассоциированной с объектом WatchService.
При регистрации директории (dir.register(...)) выполняется inotify_add_watch(), который добавляет "watch" (наблюдение) за директорией в ядре. Ядро начинает отслеживать события для этой директории и всех её прямых потомков.
inotify в ядре использует собственный буфер событий (обычно 16 КБ по умолчанию, настраивается через /proc/sys/fs/inotify/max_queued_events). Если событий больше, чем вмещает буфер, приходит OVERFLOW.
При вызове watchService.take() поток Java блокируется на системном вызове read() от дескриптора inotify. Поток переводится в состояние WAITING — он не потребляет CPU. Когда событие появляется, ядро пробуждает поток, read() возвращает структуру события, и JVM создаёт объекты WatchKey и WatchEvent в куче.
Объекты WatchKey и WatchEvent создаются в Young Generation. WatchKey может переживать несколько циклов событий, если его не сбросить через reset(). WatchEvent — одноразовый объект, который становится мусором после обработки.
При закрытии WatchService (watchService.close()) выполняется inotify_rm_watch() для всех зарегистрированных наблюдений и close() для дескриптора inotify. Это освобождает ресурсы ядра. Если WatchService не закрыт, дескриптор inotify остаётся открытым до завершения процесса.

Важно: inotify на Linux не рекурсивен. WatchService регистрирует наблюдение только для указанной директории, не для поддиректорий. Для рекурсивного мониторинга Java обходит дерево и создаёт отдельный inotify_add_watch() для каждой директории. Каждый watch потребляет ресурсы ядра (inode наблюдения), и их количество ограничено (/proc/sys/fs/inotify/max_user_watches, обычно 8192).


#Java #для_новичков #beginner #IO #NIO #Files #Files_lines #WatchService
👍4
Что выведет код?

import java.nio.file.*;
import java.util.*;
import java.util.stream.Stream;

public class FilesLinesTricky {
public static void main(String[] args) throws Exception {
Path file = Paths.get("test.txt");
Files.write(file, Arrays.asList("A", "B", "C"));

try (Stream<String> lines = Files.lines(file)) {
Iterator<String> it = lines.iterator();
System.out.print(it.next());
System.out.print(it.next());

Files.write(file, Arrays.asList("D", "E"), StandardOpenOption.APPEND);

while (it.hasNext()) {
System.out.print(it.next());
}
}
}
}


#Tasks
👍3
Варианты ответа:
Anonymous Quiz
30%
ABC
50%
ABCDE
0%
AB
20%
ABDE
👍3
Media is too big
VIEW IN TELEGRAM
Небольшая демонстрация предрелизной логики Devforge.ru 🙈

Жду оценок и мнений! 😏

пысы: схемы можно писать через язык Mermaid 🤓
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Что такое ZGC (Z Garbage Collector)? 🤓

Ответ:

ZGC (Java 11 как экспериментальный, стабилен с Java 15)
— это низколатентный сборщик мусора, работающий с паузами менее 10 мс (в основном менее 1 мс), даже для терабайтных куч.

Использует механизм colored pointers и load barriers. Работает параллельно с приложением, не останавливая его для полной сборки (concurrent).

Не требует больших тюнингов. Подходит для приложений реального времени, микросервисов с низкими SLI по задержкам.

Альтернатива — Shenandoah (от Red Hat), похожий по идеологии.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 24 июля

ℹ️ Кто родился в этот день

Шарль Эми́ль Пика́р (фр. Charles Émile Picard; 24 июля 1856, Париж — 11 декабря 1941, там же) — французский математик. Известен фундаментальными результатами в области математического анализа. Его учебник анализа (Traité d’Analyse) долгое время считался классическим. Внёс существенный вклад также в теорию дифференциальных уравненийтеорию функцийтопологиютеорию групп. Для линейных дифференциальных уравнений разработал аналог теории Галуа. Часть его трудов посвящены истории и философии математики.


🌐 Знаковые события

1987 — IBM выпустила операционную систему DOS version 3.3.


#Biography #Birth_Date #Events #24июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #064]

Тема: WeakHashMap хранит ключи через слабые ссылки, но значения — сильные. Значение может удерживать ключ, предотвращая удаление.

Проблема: WeakHashMap предназначена для хранения записей, ключи которых могут быть собраны сборщиком мусора (GC), если на них нет сильных ссылок.

Это удобно для кэшей или метаданных, привязанных к объектам. Однако внутри WeakHashMap значения хранятся через сильные ссылки (обычные ссылки). Если значение содержит ссылку на свой ключ (прямо или косвенно), то образуется цикл: ключ держит значение через map, а значение держит ключ через свою ссылку. В результате ни ключ, ни значение не становятся достижимыми только через слабую ссылку, и запись никогда не удаляется из WeakHashMap. Это приводит к утечке памяти, так как объекты остаются живыми, несмотря на то, что внешние ссылки на ключ отсутствуют. Разработчики часто не учитывают этот нюанс и используют WeakHashMap для кэширования, полагая, что оно автоматически очищается, а затем сталкиваются с неожиданным ростом памяти.

Решение: При использовании WeakHashMap необходимо гарантировать, что значение не содержит ссылки на ключ.

Если архитектура требует, чтобы значение знало о ключе, следует хранить значение в виде WeakReference или использовать отдельную структуру.

Альтернативно, можно использовать WeakHashMap только для простых типов ключей (например, строк), которые не хранятся в значениях. Для сложных кэшей лучше применять библиотеки типа Caffeine или Guava Cache, которые предоставляют более гибкие стратегии очистки и не страдают от этой проблемы. В случае, если значение обязательно должно ссылаться на ключ, можно использовать java.lang.ref.WeakReference для значения и хранить его в WeakHashMap, но тогда нужно самостоятельно обрабатывать удаление.
import java.util.WeakHashMap;
import java.lang.ref.WeakReference;

public class WeakHashMapPitfall {

static class Key { /* ... */ }

static class Value {
private final Key key; // ссылка на ключ!
Value(Key key) { this.key = key; }
}

public static void main(String[] args) {
//Антипаттерн: значение ссылается на ключ
WeakHashMap<Key, Value> map = new WeakHashMap<>();
Key key = new Key();
map.put(key, new Value(key)); // значение хранит ссылку на ключ
key = null; // убираем внешнюю ссылку
System.gc(); // попытка сборки
// Запись не будет удалена, так как Value держит Key
System.out.println("Size: " + map.size()); // 1 — утечка!

//Решение: не хранить ссылку на ключ в значении
static class SafeValue {
// без ссылки на Key
}
WeakHashMap<Key, SafeValue> safeMap = new WeakHashMap<>();
Key key2 = new Key();
safeMap.put(key2, new SafeValue());
key2 = null;
System.gc();
// Запись будет удалена
System.out.println("Safe size: " + safeMap.size()); // 0

// Альтернатива: использовать WeakReference для значения
WeakHashMap<Key, WeakReference<Value>> refMap = new WeakHashMap<>();
Key key3 = new Key();
refMap.put(key3, new WeakReference<>(new Value(key3))); // всё равно Value ссылается на Key
// это не решает проблему, если Value ссылается на Key
}
}


Объяснение:
 В WeakHashMap запись удаляется, когда ключ становится доступным только через слабую ссылку (т.е. нет сильных ссылок на ключ, кроме самой карты).

Сборщик мусора определяет достижимость объектов: если от корней GC (например, статические поля, локальные переменные) нет цепочки сильных ссылок к ключу, ключ считается слабодостижимым. Однако если значение содержит сильную ссылку на ключ, то цепочка map -> значение -> ключ создает сильную достижимость ключа (через значение).

Таким образом, ключ не является слабодостижимым, и запись не удаляется. Это классический случай утечки памяти, который трудно диагностировать, потому что WeakHashMap работает как ожидается для большинства случаев.


#Java #советы
👍3
Что выведет код?

public class Task240726 {
static class Key {
String id;
Key(String id) { this.id = id; }
@Override
public int hashCode() { return id.hashCode(); }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return id.equals(key.id);
}
}
static class Value {
Key ref;
Value(Key ref) { this.ref = ref; }
}

public static void main(String[] args) throws InterruptedException {
WeakHashMap<Key, Value> map = new WeakHashMap<>();
Key key1 = new Key("one");
Key key2 = new Key("two");

map.put(key1, new Value(key1));
map.put(key2, new Value(null));

key1 = null;
key2 = null;

System.gc();
Thread.sleep(1000);

System.out.println(map.size());
System.out.println(map.containsKey(new Key("one")));
System.out.println(map.containsKey(new Key("two")));
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
57%
1 true false
0%
1 false true
29%
2 true true
14%
0 false false
👍2
Что такое switch с паттернами (Pattern Matching for Switch)? 🤓

Ответ:

Начиная с Java 17 (preview с Java 17, финал в Java 21), switch может сопоставлять не только константы, но и типы объектов с автоматическим приведением.

Пример: 
String result = switch (obj)
{
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case null -> "null"; default -> "Unknown";
};.


Также поддерживаются охранные условия (guards): case Point p when p.x() > 0 -> ....

Это делает switch полноценным инструментом для работы с иерархиями типов, уменьшая количество instanceof и приведений.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История технологии сегодня — 25 июля

ℹ️ Кто родился в этот день

Джон Баннистер Гуденаф (англ. John Bannister Goodenough; 25 июля 1922, Йена, Тюрингия[вд] — 25 июня 2023, Остин) — американский учёный, специалист в области физики и химии твёрдого тела и материаловедения. Известен своим вкладом в разработку первых литий-ионных аккумуляторов.


🌐 Знаковые события

1907 — русский физик Борис Розинг подал заявку на патент на «Способ электрической передачи изображений на расстояние», то есть телевидение.


#Biography #Birth_Date #Events #25июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
История технологии сегодня — 26 июля

ℹ️ Кто родился в этот день

Джеймс Эфрэйм Лавлок (англ. James Ephraim Lovelock; 26 июля 1919, Летчуэрт, Хартфордшир — 26 июля 2022[3], Эбботсбери[вд], Юго-Западная Англия) — британский учёный, независимый исследователь, биофизик, химик, футуролог. Приобрёл известность как создатель Гипотезы Геи (согласно которой планета Земля функционирует как суперорганизм), а также как активный сторонник использования атомной энергии.

Исаак Бэббитт (англ. Isaac Babbitt; 26 июля 1799, Тонтон, Массачусетс, США — 26 мая 1862, Сомервилл, Массачусетс, США) — американский изобретатель. В 1839 году он изобрел подшипник из металлического сплава на основе олова с низким коэффициентом трения. Этот металл был назван по его имени баббитом, и до сих пор широко используется в подшипниках.


🌐 Знаковые события

1925 — впервые поднялся в воздух самолёт «К-1» конструкции К. А. Калинина — первый отечественный пассажирский самолёт, рекомендованный к серийному выпуску.

1989 — 24-летнему американскому студенту Роберту Моррису предъявляется обвинение в создании и запуске компьютерного вируса. Моррис становится первым создателем вируса, подвергнутым уголовному преследованию.


#Biography #Birth_Date #Events #26июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2