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
Напоминаю, завтра в 16 по МСК, я дам ссылочку присоединившись по которой, мы сможем посидеть и пообщаться.

Поотвечаю на вопросы, обсудим наболевшее)))

Приходите
🔥7
Скоро релиз))

Если я когда нибудь победю Angular)))

Как вам? (это еще не доработанная версия)
🔥11
История технологии сегодня — 19 июля

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

Сэмюэл Кольт (Самуи́л Кольтангл. Samuel Colt; 22 июля 1814Хартфорд — 10 января 1862Хартфорд) — американский оружейник, изобретатель и промышленник, основатель компании Colt’s Patent Fire-Arms Manufacturing Company (сейчас Colt’s Manufacturing Company). Происходил из аристократической и достаточно богатой семьи, но независимо от этого основал компанию сам. Наибольшей известностью пользуется как реформатор револьверного оружия: в 1835 году изобрёл капсульный револьвер, который быстро потеснил другие системы и дал толчок для создания револьверов под унитарный металлический патрон.


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

1927 — В Ленинграде испытан первый отечественный аэроглиссер.


#Biography #Birth_Date #Events #19июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ну как так то...)))
🤓2
Вот такая система оценок будет. 💪

Как вам? ☺️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Please open Telegram to view this post
VIEW IN TELEGRAM
🤓2
Quod licet Iovi, non licet bovi
👍3
История технологии сегодня — 20 июля

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

Григо́рий Васи́льевич Кисунько́ (укр. Григорій Васильович Кісунько; 20 июля 1918, Бельманка, Екатеринославская губерния — 11 октября 1998, Москва) — советский учёный в области радиоэлектроники. Один из создателей советской системы противоракетной обороны.

Герд Карл Би́нниг (нем. Gerd Karl Binnig; род. 20 июля 1947) — немецкий физик, в 1986 году совместно с Генрихом Рорером получил Нобелевскую премию по физике за изобретение сканирующего туннельного микроскопа. Разработки Биннига расширили возможности нанотехнологий, позволив не только визуализировать отдельные атомы, но и манипулировать ими.

Гео́ргий Э́рихович Лангема́к (8 (20) июля 1898, Старобельск, Старобельский уезд, Харьковская губерния, Российская империя — 11 января 1938, Москва, РСФСР, СССР) российский и советский учёный, один из пионеров ракетной техники и один из создателей первых реактивных снарядов в СССР. Основоположник исследований по конструированию реактивных снарядов на бездымном порохе, открыл так называемый закон подобия, знание которого позволило определять оптимальную геометрию сопла реактивного двигателя без длительных дорогостоящих экспериментов — аналитическим расчётом. Ввёл в русский язык термин «космонавтика».


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

1807 — Нисефор Ньепс с братом получили патент на один из ранних двигателей внутреннего сгоранияпиреолофор[англ.].

1969 — «Аполлон-11» совершил первую в истории пилотируемую посадку на Луну.


#Biography #Birth_Date #Events #20июля
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Вакансия мечты, не? 🚽
Please open Telegram to view this post
VIEW IN TELEGRAM
1
[Совет по Java #062]

Тема: Thread.stop() устарел и опасен (разблокирует все мониторы, оставляя данные в несогласованном состоянии).

Проблема: Метод Thread.stop() принудительно завершает поток, выбрасывая ThreadDeath в целевом потоке. При этом освобождаются все блокировки, которые поток удерживал, но изменения, сделанные в критической секции, не откатываются.

Это приводит к тому, что разделяемые объекты остаются в частично обновлённом состоянии, а другие потоки, захватившие освобождённые мониторы, могут читать несогласованные данные. Остановка потока в произвольный момент нарушает инварианты объектов, и восстановить состояние системы становится невозможно. Метод был объявлен устаревшим (deprecated) ещё в Java 1.2 и никогда не должен использоваться в production-коде.

Решение: Вместо принудительной остановки используйте кооперативный механизм прерывания. Вызовите Thread.interrupt() на потоке, который нужно завершить.

Целевой поток должен периодически проверять статус прерывания с помощью Thread.interrupted() или Thread.currentThread().isInterrupted() и завершать свою работу корректно, освобождая ресурсы и приводя данные в согласованное состояние. Для блокирующих операций (например, wait()sleep()) прерывание выбрасывает InterruptedException, которое следует обработать, прервав выполнение.

Дополнительно можно использовать volatile boolean флаг для сигнализации завершения, но прерывание является стандартным и более интегрированным с JDK.
public class ThreadStopExample {

//Антипаттерн: Thread.stop()
public static void badStop() throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
System.out.println("Работаю...");
// Если здесь stop() будет вызван, данные могут быть повреждены
}
});
t.start();
Thread.sleep(100);
t.stop(); // Опасно! Освобождает мониторы, оставляя объекты в мусорном состоянии
}

//Правильно: кооперативное завершение через прерывание
public static void goodInterrupt() throws InterruptedException {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Безопасная работа...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// Восстанавливаем статус прерывания и выходим
Thread.currentThread().interrupt();
break;
}
}
System.out.println("Поток корректно завершён");
});
t.start();
Thread.sleep(2000);
t.interrupt(); // Сигнал остановки
t.join();
}

// Альтернатива: volatile флаг
private volatile boolean running = true;

public void safeWithFlag() throws InterruptedException {
Thread t = new Thread(() -> {
while (running) {
System.out.println("Работа с флагом...");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// При прерывании тоже можем выйти
Thread.currentThread().interrupt();
break;
}
}
});
t.start();
Thread.sleep(1000);
running = false; // Сигнал остановки
t.interrupt(); // Дополнительно для прерывания блокировок
t.join();
}

public static void main(String[] args) throws InterruptedException {
// badStop(); // Не запускайте!
goodInterrupt();
}
}


Объяснение:
 При вызове stop() JVM немедленно завершает поток, не давая ему возможности завершить текущую операцию.

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

Это нарушает атомарность и может привести к трудноотлавливаемым повреждениям данных. interrupt() лишь устанавливает флаг прерывания, не вмешиваясь в выполнение. Поток сам решает, когда и как реагировать на этот сигнал, что позволяет ему корректно завершить работу. Блокирующие методы (например, sleep()wait()join()BlockingQueue.put()) проверяют статус прерывания и выбрасывают InterruptedException, обеспечивая немедленную реакцию на сигнал. В коде с интенсивными вычислениями необходимо явно проверять isInterrupted() в цикле.


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

public class ThreadStopTricky {
private static int value = 0;

public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> {
synchronized (ThreadStopTricky.class) {
value = 1;
try { Thread.sleep(1000); } catch (InterruptedException e) {}
value = 2;
}
});
t.start();
Thread.sleep(50);
t.stop();
synchronized (ThreadStopTricky.class) {
System.out.println(value);
}
}
}


#Tasks
👍2
👍2
This media is not supported in your browser
VIEW IN TELEGRAM
Ребят, продолжаю набирать желающих обучиться Java!

Изучаем до уровня стандартного мидла)

Пишите @Oleborn
Что такое System.Logger API (Java 9)? 🤓

Ответ:

System.Logger (Java 9)
— это стандартный API для логирования, позволяющий приложениям использовать разные реализации (SLF4J, Log4j2, JUL) через абстракцию.

Получаем логгер через System.getLogger("name"). Предоставляет уровни (INFO, DEBUG, ERROR) и методы log().

Не требует внешних зависимостей. Однако широкого распространения не получил, так как SLF4J уже де-факто стандарт.

Используется в модульной системе и для внутренних нужд JDK.



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

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

Ру́дольф «Руди» Артур Ма́ркус (англ. Rudolph «Rudy» Arthur Marcus; 21 июля 1923, Монреаль, Канада — 16 июля 2026, Пасадина, Калифорния) — американский химик канадского происхождения, основоположник теории переноса электрона в растворах (теория Маркуса), лауреат Нобелевской премии по химии 1992 года («За вклад в теорию реакций переноса электрона в химических системах»).


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

1969 — американский астронавт Нил Армстронг стал первым человеком, ступившим на Луну. Через 19 минут к нему присоединился Базз Олдрин.

1973 — запущен «Марс-4».


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

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

Files.list() и Files.walk() — обход содержимого директорий

Работа с файловой системой в Java NIO.2 предоставляет два ключевых метода для обхода директорий: Files.list() для непосредственного содержимого и Files.walk() для рекурсивного обхода дерева. Оба метода возвращают Stream<Path>, что позволяет использовать всю мощь Stream API для фильтрации, преобразования и агрегации данных. Однако за этой элегантностью скрываются важные нюансы производительности, управления ресурсами и потребления памяти, которые критичны при работе с большими файловыми деревьями.

Stream API — это фреймворк в Java для обработки последовательностей элементов в функциональном стиле. Он поддерживает ленивые (lazy) промежуточные операции (filter, map, sorted) и энергичные (eager) терминальные операции (count, collect, forEach). Stream не хранит данные, а описывает конвейер вычислений.


Files.list(Path) — содержимое директории (не рекурсивно)

Метод Files.list(Path dir) возвращает Stream<Path>, содержащий прямые потомки указанной директории. Поток ленивый: записи директории читаются по мере запроса элементов из потока, а не все сразу.
public class DirectoryLister {

public void listDirectory(Path dir) throws IOException {
// Stream<Path> — ленивый поток, записи читаются по требованию
try (Stream<Path> stream = Files.list(dir)) {
stream.forEach(path -> {
String type = Files.isDirectory(path) ? "[DIR]" : "[FILE]";
System.out.println(type + " " + path.getFileName());
});
}
}
}

Важно: Files.list() возвращает поток, который держит открытым файловый дескриптор директории до тех пор, пока поток не закрыт. Поэтому обязательно использование try-with-resources — иначе дескриптор утечёт на уровне ОС, даже если объект Stream будет собран GC.

Файловый дескриптор (file descriptor) — это целочисленный идентификатор, который ядро ОС присваивает открытому файлу или ресурсу. В Unix это индекс в таблице открытых файлов процесса. JVM не знает о дескрипторах ОС — GC управляет только памятью кучи, не ресурсами операционной системы.

Фильтрация по расширению: пример вывода всех .txt файлов
public class TxtFileFinder {

public void findTxtFiles(Path dir) throws IOException {
try (Stream<Path> stream = Files.list(dir)) {
stream.filter(Files::isRegularFile) // только файлы, не директории
.filter(this::hasTxtExtension) // фильтр по расширению
.forEach(this::printFileInfo);
}
}

private boolean hasTxtExtension(Path path) {
String fileName = path.getFileName().toString();
// endsWith чувствителен к регистру — на case-sensitive ФС это корректно
return fileName.endsWith(".txt");
}

private void printFileInfo(Path path) {
try {
long size = Files.size(path);
FileTime modified = Files.getLastModifiedTime(path);
System.out.printf("%s (%d bytes, modified: %s)%n",
path.getFileName(), size, modified);
} catch (IOException e) {
System.err.println("Ошибка чтения атрибутов: " + path.getFileName());
}
}

// Альтернатива: чтение атрибутов за один вызов для каждого файла
public void findTxtFilesOptimized(Path dir) throws IOException {
try (Stream<Path> stream = Files.list(dir)) {
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".txt"))
.map(p -> {
try {
BasicFileAttributes attrs = Files.readAttributes(
p, BasicFileAttributes.class
);
return new FileInfo(p.getFileName(), attrs.size(),
attrs.lastModifiedTime());
} catch (IOException e) {
return null;
}
})
.filter(Objects::nonNull)
.forEach(System.out::println);
}
}

private record FileInfo(Path name, long size, FileTime modified) {}
}

В оптимизированной версии readAttributes() читает все атрибуты за один системный вызов вместо отдельных Files.size() и Files.getLastModifiedTime(). Однако map() в Stream API требует, чтобы лямбда не выбрасывала checked-исключения, поэтому приходится оборачивать в try-catch внутри лямбды.

Record — это тип класса в Java, введённый в Java 16 (preview в 14-15), который автоматически генерирует конструктор, геттеры, equals(), hashCode() и toString() на основе объявленных компонентов. record FileInfo(Path name, long size, FileTime modified) создаёт неизменяемый (immutable) класс данных.


Псевдокод: внутренняя работа Files.list()

функция list(Path dir) -> Stream<Path>:
открыть файловый дескриптор директории dir (системный вызов opendir)
создать DirectoryStream для чтения записей
создать Stream, который при запросе следующего элемента:
вызывает DirectoryStream.readNextEntry()
если запись есть — возвращает Path(entry_name)
если записей больше нет — закрывает дескриптор и завершает поток
зарегистрировать onClose для Stream, который закрывает дескриптор
вернуть Stream

Ключевой момент: DirectoryStream читает записи порциями из буфера ядра. Размер буфера зависит от файловой системы и ОС (обычно 4–32 КБ). Это означает, что для директории с миллионом файлов Files.list() не загружает все имена в память сразу — оно читает их порциями по мере итерации потока.


Files.walk(Path) — рекурсивный обход дерева

Метод Files.walk(Path start) выполняет обход дерева файловой системы, начиная с указанного пути, и возвращает Stream<Path> со всеми посещёнными путями. Обход выполняется в порядке pre-order depth-first (прямой порядок, глубина вперёд): сначала обрабатывается родительский узел, затем его потомки.
public void walkDirectory(Path root) throws IOException {
try (Stream<Path> stream = Files.walk(root)) {
stream.forEach(path -> {
int depth = root.relativize(path).getNameCount();
String indent = " ".repeat(depth);
System.out.println(indent + path.getFileName());
});
}
}



Files.walk(Path, int maxDepth) — ограничение глубины

Метод Files.walk(Path start, int maxDepth) ограничивает глубину обхода. maxDepth = 0 означает только сам start. maxDepth = 1start и его прямые потомки (аналогично Files.list() плюс сам корень).
public void walkWithDepthLimit(Path root) throws IOException {
// Обходим только 2 уровня вглубь
try (Stream<Path> stream = Files.walk(root, 2)) {
stream.filter(Files::isRegularFile)
.forEach(p -> System.out.println(p.toAbsolutePath()));
}
}



#Java #для_новичков #beginner #IO #NIO #Files #Fileslist #Fileswalk
👍5
Порядок обхода и символические ссылки

По умолчанию Files.walk() следует по символическим ссылкам (follows symlinks). Это означает, что если директория содержит символическую ссылку на другую директорию, обход зайдёт внутрь этой целевой директории.

Это может привести к:
Бесконечному циклу, если символическая ссылка указывает на предка в дереве (например, ln -s .. loop).
Обходу файловой системы за пределами ожидаемого дерева (например, ссылка на /etc).

Для безопасного обхода используйте FileVisitOption.FOLLOW_LINKS явно, только если это нужно, или обходите ссылки как файлы:
public class SafeWalker {

// Безопасный обход: символические ссылки обрабатываются как файлы, не следуем по ним
public void walkNoFollowLinks(Path root) throws IOException {
// По умолчанию walk НЕ следует по ссылкам — это поведение по умолчанию
// Но явно можно указать:
try (Stream<Path> stream = Files.walk(root)) {
stream.forEach(path -> {
if (Files.isSymbolicLink(path)) {
System.out.println("[LINK] " + path.getFileName());
}
});
}
}

// Обход с явным следованием по ссылкам (осторожно!)
public void walkFollowingLinks(Path root) throws IOException {
try (Stream<Path> stream = Files.walk(root, FileVisitOption.FOLLOW_LINKS)) {
stream.forEach(System.out::println);
}
}
}

FileVisitOption — это enum, определяющий опции обхода дерева. FOLLOW_LINKS указывает, что символические ссылки на директории должны трактоваться как директории и обходиться рекурсивно. Без этой опции ссылки трактуются как обычные файлы.

Псевдокод: внутренняя работа Files.walk()
функция walk(Path start, int maxDepth) -> Stream<Path>:
создать очередь (Deque) для обхода в ширину/глубину
поместить start в очередь с глубиной 0
создать Stream, который при запросе элемента:
извлекает следующий путь из очереди
возвращает его как элемент Stream
если это директория И глубина < maxDepth:
открыть DirectoryStream для этой директории
для каждой записи:
поместить entry в очередь с глубиной + 1
зарегистрировать onClose для закрытия всех открытых DirectoryStream
вернуть Stream

Важно: Files.walk() открывает DirectoryStream для каждой директории в дереве. Все эти потоки держат открытые файловые дескрипторы до закрытия основного Stream<Path>. Поэтому try-with-resources обязателен — иначе при аварийном завершении обхода дескрипторы останутся открытыми.


#Java #для_новичков #beginner #IO #NIO #Files #Fileslist #Fileswalk
👍5
Параллельный обход: предостережения

stream.parallel() распределяет обход по потокам ForkJoinPool.commonPool(). Это может ускорить обход на многопроцессорных системах, но есть риски:
Порядок обхода непредсказуем: элементы обрабатываются в произвольном порядке.
Конкурентный доступ к файловой системе: множественные потоки одновременно открывают директории, что может привести к contention (конкуренции) на уровне ОС.
Символические ссылки: при параллельном обходе с FOLLOW_LINKS риск зацикливания возрастает.
Ошибки обхода: если один поток получает AccessDeniedException, это не прерывает другие потоки, но обработка ошибок усложняется.

Рекомендуется использовать параллельный обход только для больших деревьев на быстрых локальных дисках (SSD, NVMe) и только если операция для каждого файла тяжёлая (например, вычисление хэша, а не просто чтение размера).

ForkJoinPool — это пул потоков в Java, реализующий алгоритм work-stealing (воровство задач). Потоки, завершившие свои задачи, "воруют" задачи из очередей других потоков, что обеспечивает равномерную загрузку. commonPool() — это общий пул, используемый по умолчанию для параллельных Stream.



Осторожно: обход может быть дорогим для больших деревьев

Проблема 1: Потребление памяти

Files.walk() использует внутреннюю очередь для хранения путей, ожидающих обхода. Для сильно ветвящегося дерева (например, директория с миллионом файлов на первом уровне) эта очередь может занять значительный объём памяти. Хотя поток ленивый, внутренний буфер между DirectoryStream и Stream может накапливать пути.

Проблема 2: Файловые дескрипторы
Каждая открытая директория в обходе дерева держит файловый дескриптор. Для глубокого дерева с тысячами директорий это может исчерпать лимит дескрипторов процесса (ulimit -n на Unix, обычно 1024–65536). Если лимит исчерпан, последующие opendir() вызовы вернут EMFILE (too many open files), что JVM преобразует в IOException.

Проблема 3: Время обхода
Обход дерева из миллионов файлов может занять минуты. Stream не предоставляет механизма прерывания (cancellation) стандартными средствами. Прерывание потока через Thread.interrupt() не прерывает внутренний обход — он продолжит открывать директории и читать записи.

Проблема 4: Сортировка и материализация
Если вы применяете sorted() к результату Files.walk(), весь поток материализуется в список в памяти перед сортировкой. Для большого дерева это может привести к OutOfMemoryError.

// ОПАСНО: материализует все пути в память
Files.walk(root).sorted().forEach(...); // OOM при миллионе файлов

// Безопасно: обход без сортировки
Files.walk(root).forEach(...); // ленивый, память ограничена буфером


Альтернатива: Files.walkFileTree() для контролируемого обхода


Для сценариев, где требуется точный контроль над обходом (пропуск поддеревьев, обработка ошибок, прерывание), используйте Files.walkFileTree() с FileVisitor:
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;

public class ControlledTreeWalker implements FileVisitor<Path> {

private final AtomicLong totalSize = new AtomicLong(0);
private final long maxSizeToProcess; // прервать после достижения лимита
private volatile boolean cancelled = false;

public ControlledTreeWalker(long maxSize) {
this.maxSizeToProcess = maxSize;
}

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if (cancelled) {
return FileVisitResult.TERMINATE; // прервать обход
}
// Пропускаем скрытые директории
try {
if (Files.isHidden(dir)) {
return FileVisitResult.SKIP_SUBTREE; // не заходить внутрь
}
} catch (IOException e) {
// Продолжаем, если не удалось проверить
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (cancelled) {
return FileVisitResult.TERMINATE;
}
if (attrs.isRegularFile()) {
totalSize.addAndGet(attrs.size());
if (totalSize.get() >= maxSizeToProcess) {
cancelled = true;
return FileVisitResult.TERMINATE;
}
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
// Логируем ошибку, но продолжаем обход
System.err.println("Ошибка доступа к файлу: " + file + " — " + exc.getMessage());
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
if (exc != null) {
System.err.println("Ошибка обхода директории: " + dir + " — " + exc.getMessage());
}
return FileVisitResult.CONTINUE;
}

public long getTotalSize() {
return totalSize.get();
}

public static long calculateSizeWithLimit(Path root, long limit) throws IOException {
ControlledTreeWalker walker = new ControlledTreeWalker(limit);
Files.walkFileTree(root, walker);
return walker.getTotalSize();
}
}



#Java #для_новичков #beginner #IO #NIO #Files #Fileslist #Fileswalk
👍6