Java for Beginner
673 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Итераторы в Java

Внутреннее устройство Iterator в Java
Итераторы в Java реализованы через интерфейс Iterator, который определяет три ключевых метода: hasNext(), next() и remove(). Различные коллекции, такие как ArrayList, LinkedList, HashSet и другие, имеют свои реализации этих методов, которые адаптированы под их внутреннее устройство.

1. hasNext()

Метод hasNext() отвечает за проверку наличия следующего элемента в коллекции. Он возвращает true, если итератор не достиг конца, и false — если элементы закончились. Для разных коллекций этот метод реализован по-разному:
В списках (ArrayList, LinkedList) проверяется текущий индекс относительно размера списка.
В HashSet проверяется наличие следующего узла в таблице.


Пример внутренней реализации hasNext() для ArrayList:
public boolean hasNext() {
return cursor != size;
}
Здесь cursor — это текущая позиция в массиве элементов, а size — размер списка.


2. next()

Метод next() возвращает следующий элемент коллекции и сдвигает курсор итератора. В случае, если элементов больше нет, метод бросает исключение NoSuchElementException. В ArrayList этот метод выглядит следующим образом:
public E next() {
checkForComodification(); // Проверка на модификацию коллекции
int i = cursor; // Запоминаем текущую позицию
if (i >= size) // Проверяем выход за пределы списка
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData; // Ссылка на данные
cursor = i + 1; // Смещаем курсор
return (E) elementData[lastRet = i];
}
Этот метод также выполняет проверку на конкурентные изменения коллекции (checkForComodification()), что предотвращает возникновение ConcurrentModificationException.


3. remove()

Метод remove() удаляет последний элемент, возвращенный методом next(). Он должен быть вызван строго после вызова next() и не может быть использован повторно до следующего вызова next(). Пример для ArrayList:
public void remove() {
if (lastRet < 0) // Проверка, был ли вызван метод next()
throw new IllegalStateException();
checkForComodification(); // Проверка на модификацию
try {
ArrayList.this.remove(lastRet); // Удаление элемента
cursor = lastRet; // Корректировка курсора
lastRet = -1;
expectedModCount = modCount; // Обновление состояния
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
Здесь expectedModCount и modCount используются для контроля изменений коллекции во время итерации.


Внутреннее устройство ListIterator


Интерфейс ListIterator расширяет Iterator и предоставляет дополнительные возможности для перемещения назад и изменения элементов. Реализация методов previous(), set(), add() требует дополнительной поддержки индексов и контроля состояния коллекции:
previous(): Возвращает предыдущий элемент и сдвигает указатель в обратном направлении.
set(E e): Заменяет элемент, возвращенный последним вызовом next() или previous().
add(E e): Вставляет элемент перед текущей позицией указателя.


Пример реализации ListIterator для ArrayList:
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
cursor = i;
return (E) elementData[lastRet = i];
}
Здесь cursor перемещается назад, а метод возвращает элемент на новой позиции.


#Java #Training #Medium #Iterator
Примеры применения Iterator и ListIterator

Удаление всех элементов, которые удовлетворяют условию

Использование Iterator упрощает удаление элементов по условию, исключая проблемы с ConcurrentModificationException:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class RemoveIfExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
numbers.add(i);
}

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
if (number % 2 == 0) { // Удаление четных чисел
iterator.remove();
}
}

System.out.println(numbers); // [1, 3, 5, 7, 9]
}
}


Обход и модификация элементов

ListIterator позволяет изменять элементы во время обхода:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ModifyElementsExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");

ListIterator<String> listIterator = names.listIterator();
while (listIterator.hasNext()) {
String name = listIterator.next();
if ("Bob".equals(name)) {
listIterator.set("Robert"); // Замена "Bob" на "Robert"
}
}

System.out.println(names); // [Alice, Robert, Charlie]
}
}


Перемещение в обе стороны

ListIterator поддерживает двусторонний обход коллекции:

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class BiDirectionalTraversalExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");

ListIterator<String> listIterator = names.listIterator();

// Перемещение вперед
while (listIterator.hasNext()) {
System.out.println("Next: " + listIterator.next());
}

// Перемещение назад
while (listIterator.hasPrevious()) {
System.out.println("Previous: " + listIterator.previous());
}
}
}


Вывод:
Next: Alice
Next: Bob
Next: Charlie
Previous: Charlie
Previous: Bob
Previous: Alice


#Java #Training #Medium #Iterator
Метод Thread.sleep()

В Java метод Thread.sleep() — это статический метод класса Thread, который позволяет временно приостановить выполнение текущего потока. Это очень полезная функция, особенно когда вам нужно дать другим потокам возможность работать, а также для создания задержек в выполнении программ.

Основы работы с Thread.sleep()

Сигнатура метода выглядит так:
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
millis — время в миллисекундах, на которое поток должен быть приостановлен.
nanos — дополнительное время в наносекундах (второй вариант метода).


Внутреннее устройство

Когда вы вызываете Thread.sleep(), текущий поток переходит в состояние "ожидания". Это состояние отличается от состояния "блокировки", так как поток не может быть разбужен, пока не истечет указанный период времени или пока его не прервет другой поток (например, с помощью метода interrupt()).

Внутренне Thread.sleep() использует механизм планирования потоков операционной системы. Это значит, что управление возвращается в операционную систему, которая решает, какой поток должен быть выполнен в данный момент времени.


Применение

Метод Thread.sleep() часто используется в следующих ситуациях:
Создание временной задержки. Например, если вы хотите, чтобы программа ждала несколько секунд перед выполнением следующей операции.
Синхронизация потоков. Если у вас несколько потоков, и вам нужно, чтобы один из них подождал завершения другого.
Тестирование. Во время тестирования асинхронного кода, вы можете использовать sleep() для имитации задержек.


Примеры использования

Пример 1: Простая задержка
public class SleepExample {
public static void main(String[] args) {
System.out.println("Программа начинается.");
try {
Thread.sleep(2000); // Ожидание 2 секунды
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2 секунды прошли.");
}
}


Пример 2: Синхронизация потоков
public class SleepSynchronization {
public static void main(String[] args) {
Thread thread1 = new Thread(new Task("Поток 1"));
Thread thread2 = new Thread(new Task("Поток 2"));

thread1.start();
thread2.start();
}
}

class Task implements Runnable {
private String name;

public Task(String name) {
this.name = name;
}

public void run() {
try {
System.out.println(name + " запущен.");
Thread.sleep(1000); // Ожидание 1 секунда
System.out.println(name + " завершен.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


Важно помнить

Использование Thread.sleep() может приводить к неэффективному использованию ресурсов, так как поток, который "спит", не выполняет никакой работы.
Если поток прерывается во время сна, выбрасывается исключение InterruptedException, и вам нужно корректно его обрабатывать.


#Java #Training #Medium #Thread_sleep
Что выведет код?

public class Task101024_1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
System.out.print("A");
Thread.sleep(500);
System.out.print("B");
} catch (InterruptedException e) {
System.out.print("E");
}
});

Thread t2 = new Thread(() -> {
try {
System.out.print("C");
Thread.sleep(200);
System.out.print("D");
} catch (InterruptedException e) {
System.out.print("F");
}
});

t1.start();
t2.start();
t1.join();
t2.join();
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
42%
ACBD
8%
CABD
33%
ACDB
17%
CDAB
0%
ACAB
А еще это переработчик кофе. Из одного внутреннего состава в другое. 😂👍

Подписывайтесь на канал, у нас интересно - https://t.me/Java_for_beginner_dev

#Mems
Что выведет код?

import java.util.regex.*;

public class Task101024_2 {
public static void main(String[] args) {
String input = "a1b2c3";
Pattern pattern = Pattern.compile("\\d");
Matcher matcher = pattern.matcher(input);

StringBuilder result = new StringBuilder();
while (matcher.find()) {
result.append(matcher.group());
}
System.out.println(result.toString());
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
18%
abc
27%
a1b2c3
0%
c3
55%
123
0%
java
Класс TimeUnit

Класс TimeUnit из пакета java.util.concurrent предоставляет удобный способ работы с временными единицами. Он позволяет выполнять преобразования между различными единицами времени и упрощает работу с многопоточностью, особенно при использовании методов, которые требуют указания времени ожидания.

Основы класса TimeUnit

Класс TimeUnit реализует перечисление (enum), содержащее семь основных временных единиц:

NANOSECONDS - наносекунды
MICROSECONDS - микросекунды
MILLISECONDS - миллисекунды
SECONDS - секунды
MINUTES - минуты
HOURS - часы
DAYS - дни

Каждая единица времени в TimeUnit поддерживает методы для преобразования в другие единицы и для ожидания.

Внутреннее устройство

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

Применение

Класс TimeUnit используется в различных ситуациях:
Упрощение кода. Он позволяет избежать неочевидных преобразований между различными единицами времени, делая код более читаемым и понятным.
Управление потоками. Использование методов sleep() и awaitTermination() из ExecutorService с классом TimeUnit помогает избежать ошибок, связанных с неправильными единицами времени.
Тестирование. Класс удобно использовать для создания временных задержек в тестах.


Примеры использования

Пример 1: Преобразование единиц времени
import java.util.concurrent.TimeUnit;

public class TimeUnitExample {
public static void main(String[] args) {
long hours = 2;
long minutes = TimeUnit.HOURS.toMinutes(hours);
long seconds = TimeUnit.HOURS.toSeconds(hours);

System.out.println(hours + " часа = " + minutes + " минут.");
System.out.println(hours + " часа = " + seconds + " секунд.");
}
}


Пример 2: Ожидание с использованием TimeUnit
import java.util.concurrent.TimeUnit;

public class TimeUnitSleepExample {
public static void main(String[] args) {
System.out.println("Программа начинается.");
try {
TimeUnit.SECONDS.sleep(2); // Ожидание 2 секунды
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2 секунды прошли.");
}
}


Пример 3: Использование с ExecutorService
import java.util.concurrent.*;

public class ExecutorServiceExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1);

executor.submit(() -> {
try {
System.out.println("Задача началась.");
TimeUnit.SECONDS.sleep(3); // Ожидание 3 секунды
System.out.println("Задача завершена.");
} catch (InterruptedException e) {
e.printStackTrace();
}
});

executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow(); // Принудительное завершение
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}


Важно помнить

Использование TimeUnit делает код более читаемым и менее подверженным ошибкам, связанным с неправильным использованием временных единиц.
При использовании методов ожидания всегда обрабатывайте InterruptedException, чтобы избежать проблем с прерываниями потоков.


#Java #Training #Medium #TimeUnit
Память в JVM

Основные компоненты памяти в JVM

Когда программа на Java выполняется, JVM распределяет память между различными внутренними структурами и объектами. Память в JVM делится на несколько областей, каждая из которых выполняет определенную роль. Основные области памяти включают:


Heap (Куча):

Используется для динамического размещения объектов во время выполнения программы.
Все объекты и их данные (например, строки, массивы и т.д.) хранятся здесь.
Разделена на несколько генераций: Young Generation, Old (Tenured) Generation и Permanent Generation (PermGen) (до Java 8) или Metaspace (с Java 8).
Управляется сборщиком мусора (Garbage Collector, GC), который отвечает за освобождение памяти от неиспользуемых объектов.


Stack (Стек):

Хранит вызовы методов и локальные переменные каждого потока.
Каждый поток имеет свой собственный стек.
В каждой области стека находятся кадры (stack frames), которые содержат локальные переменные, параметры метода и указатели на объекты в куче.
Стек управляется автоматически: память освобождается при завершении выполнения метода.


Method Area (Методная область):

Используется для хранения метаданных классов, таких как данные о методах, переменных и константах.
До Java 8 эта область была частью PermGen, с Java 8 ее заменила Metaspace.
Размер Metaspace не ограничен памятью JVM, а зависит от объема доступной оперативной памяти системы.


Program Counter Register (Регистры счетчика программы):

Хранит адрес текущей выполняемой инструкции для каждого потока.
При переключении контекста потоков каждый поток сохраняет свое состояние с помощью этого регистра.


Native Method Stack (Стек нативных методов):

Используется для хранения данных, связанных с вызовом нативных методов (методы, написанные на других языках, таких как C или C++).
Содержит информацию, необходимую для взаимодействия с библиотеками и операционной системой.


Устройство и работа Heap

Куча (Heap) является самой большой и сложной областью памяти JVM. Она делится на несколько частей:

Young Generation (Молодое поколение):

Новые объекты создаются сначала в этой области.
Молодое поколение делится на три области: Eden, Survivor Space 1 и Survivor Space 2.
Когда область Eden заполняется, выполняется минорная сборка мусора (Minor GC), после которой живые объекты перемещаются в Survivor Space.


Old Generation (Старшее поколение):

Объекты, которые пережили несколько циклов сборки мусора в Young Generation, перемещаются в Old Generation.
В этой области хранятся долгоживущие объекты, такие как статические данные или объекты, которые остаются актуальными на протяжении всего выполнения программы.
Сборка мусора в этой области называется Major GC или Full GC.


#Java #Training #Medium #JVM
Metaspace

Метаданные классов хранятся здесь.
Metaspace автоматически расширяется при необходимости, если в системе достаточно свободной памяти.
Взаимодействие компонентов памяти


При выполнении программы Java каждая из этих областей играет определенную роль:

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

Пример кода и распределение памяти
public class MemoryExample {
public static void main(String[] args) {
int localVariable = 10; // Локальная переменная в стеке
String greeting = "Hello, World!"; // Строка в пуле строк (String Pool)
Object obj = new Object(); // Объект в куче
MemoryExample example = new MemoryExample(); // Новый экземпляр в куче
example.printGreeting(greeting);
}

public void printGreeting(String message) {
System.out.println(message); // Параметр метода в стеке
}
}


Локальная переменная localVariable создается в стеке.
Строка greeting хранится в пуле строк (особая часть кучи, где хранятся строковые литералы).
Объект obj создается в области Eden в куче.
Экземпляр MemoryExample также создается в куче.
При вызове метода printGreeting создается новый кадр стека, в который передается параметр message.


Особенности управления памятью

Сборка мусора (Garbage Collection):
Сборщик мусора автоматически освобождает память от объектов, на которые нет ссылок.
В JVM используется несколько алгоритмов сборки мусора: Serial GC, Parallel GC, CMS (Concurrent Mark-Sweep) и G1 (Garbage First).
Выбор сборщика мусора зависит от требований приложения и его характеристик (например, интерактивные приложения могут предпочитать CMS, а серверные — G1).


Проблемы утечек памяти (Memory Leaks):

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


Оптимизация использования памяти:

Использование пулов объектов и избегание создания избыточных экземпляров.
Правильное управление объемом кучи с помощью параметров JVM (-Xmx и -Xms).


#Java #Training #Medium #JVM
Что выведет код?

public class Task111024 {
private static int counter = 0;

static {
counter += 5;
System.out.println("Static block: " + counter);
}

{
counter += 10;
System.out.println("Instance block: " + counter);
}

public Main() {
counter += 20;
System.out.println("Constructor: " + counter);
}

public static void main(String[] args) {
System.out.println("Main Start: " + counter);
Main obj = new Main();
System.out.println("Main End: " + counter);
}
}


#Tasks
О, наконец-то нормальный девиз😂

https://t.me/Java_for_beginner_dev

#Mems
Java Memory Model (JMM)

Java Memory Model (JMM) определяет, как изменения в памяти, производимые одним потоком, становятся видимыми другим потокам. JMM описывает:
Как потоки взаимодействуют с основной памятью.
Правила синхронизации доступа к переменным.
Гарантии порядка операций, чтобы избежать непредсказуемого поведения в многопоточном программировании.
Главной целью JMM является устранение несогласованности данных и определение поведения программы на уровне видимости и порядка выполнения операций в многопоточной среде.


Основные концепции JMM

Главная (основная) память (Main Memory):
Это область памяти, где хранятся все общие переменные (поля классов, статические переменные и т. д.).
Каждому потоку также выделяется собственная локальная память (Thread Local Memory), где временно сохраняются копии значений переменных.


Кэширование и локальная память потоков:
Потоки могут не сразу обращаться к основной памяти. Вместо этого они используют кэш или локальную копию переменных.
Изменения, сделанные в локальной памяти одного потока, могут не быть видны другим потокам, если они не будут синхронизированы с основной памятью.


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


Инструкции и пересортировка:
Компилятор и процессор могут менять порядок выполнения операций для оптимизации.
Например, две независимые операции могут поменяться местами, если это ускорит выполнение программы. JMM устанавливает правила, чтобы такая пересортировка не нарушала корректность многопоточной программы.


#Java #Training #Medium #JMM
Правила JMM для взаимодействия потоков

JMM включает несколько базовых правил для обеспечения корректного поведения программы:

Правило Happens-Before:
Если одна операция происходит "раньше" другой с точки зрения JMM, это означает, что все изменения, сделанные первой операцией, будут видны второй.
Например, вызов Thread.start() имеет отношение happens-before к всем действиям внутри нового потока.


Правило синхронизации (synchronized блоки):
Использование ключевого слова synchronized гарантирует, что все изменения, сделанные внутри блока, будут видны другим потокам после выхода из него.
class Counter {
private int count = 0;

public synchronized void increment() {
count++; // Этот блок синхронизирован, изменения видны другим потокам.
}

public synchronized int getCount() {
return count;
}
}


Volatile-переменные:

Использование ключевого слова volatile указывает, что изменения в этой переменной немедленно записываются в основную память.
volatile гарантирует видимость, но не упорядочение операций.

class FlagExample {
private volatile boolean flag = false;

public void setFlagTrue() {
flag = true;
}

public void checkFlag() {
if (flag) {
System.out.println("Flag is true!");
}
}
}


Конструкторы и финализация:
Все операции, выполненные в конструкторе, должны быть завершены до того, как ссылка на объект станет доступной другим потокам.
Это правило предотвращает "полуинициализированные" объекты в многопоточной среде.
Внутреннее устройство JMM и управление памятью


На уровне устройства, JMM опирается на низкоуровневую архитектуру процессора и системы памяти:

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


Барьеры памяти (Memory Barriers):
JMM вводит так называемые барьеры памяти (memory barriers или fences) для обеспечения правильного порядка операций.
Существуют два типа барьеров:
Барьеры записи (Write Barrier) — гарантируют, что все предшествующие записи переменных видны другим потокам.
Барьеры чтения (Read Barrier) — гарантируют, что все последующие операции чтения переменных происходят после предыдущих операций записи.


Пересортировка (Reordering):
Компилятор и процессор могут изменять порядок инструкций для повышения производительности.
int x = 0, y = 0;
x = 1; // A
y = x + 1; // B
// Компилятор может поменять A и B местами, если это ускоряет выполнение.


Final-поля:
Поля, объявленные как final, имеют особые гарантии: они инициализируются до того, как объект станет доступен другим потокам.
Это предотвращает утечки ссылок на не полностью инициализированные объекты.


Пример нарушения JMM
Рассмотрим типичную проблему гонки данных:
class SharedObject {
private int counter = 0;

public void increment() {
counter++; // Несинхронизированный доступ к переменной.
}

public int getCounter() {
return counter;
}
}
Если несколько потоков одновременно вызывают метод increment, результат может быть непредсказуемым из-за отсутствия синхронизации: один поток может перезаписать значение, измененное другим потоком.


Правильное использование JMM

Для обеспечения корректности программы в многопоточной среде можно использовать следующие механизмы:

Использование synchronized для метода increment():
public synchronized void increment() {
counter++;
}
Применение AtomicInteger для безопасного инкремента:

java
Копировать код
import java.util.concurrent.atomic.AtomicInteger;

class SharedObject {
private AtomicInteger counter = new AtomicInteger(0);

public void increment() {
counter.incrementAndGet(); // Безопасная атомарная операция.
}

public int getCounter() {
return counter.get();
}
}


#Java #Training #Medium #JMM
Всем доброго субботнего утра!☀️

Ничего больше писать не буду, все равно никто не отвечает😂

Просто хороших выходных каждому и даром!🍻🖐
Хоть какая то польза от работы программистом😂😂😂

https://t.me/Java_for_beginner_dev

#Mems
This media is not supported in your browser
VIEW IN TELEGRAM
Всем доброго утра!🖐

Сегодня в 16:00 по МСК, мы вновь соберемся, что покодить)))
Сегодня мы напишем чат, и попробуем запустить его для работы через удаленный сервер!
😱

Жду всех, будет интересно!)
Ссылка на встречу опубликована в нашем чате - https://t.me/Java_Beginner_chat

Присоединяйтесь!