Forwarded from English Beginners
Другие методы класса Object
clone()
Метод clone() создает и возвращает копию объекта. Для использования этого метода класс должен реализовывать интерфейс Cloneable, иначе вызов метода clone() приведет к выбросу исключения CloneNotSupportedException.
Пример использования метода clone()
wait(), notify() и notifyAll()
Эти методы используются для управления межпотоковым взаимодействием в многопоточных приложениях.
wait(): Приостанавливает текущий поток до тех пор, пока другой поток не вызовет notify() или notifyAll() для этого объекта.
notify(): Пробуждает один поток, ожидающий на этом объекте.
notifyAll(): Пробуждает все потоки, ожидающие на этом объекте.
Эти методы должны вызываться внутри синхронизированного блока или метода.
Пример использования wait() и notify()
finalize() (В настоящее время не рекомендуется к использованию)
Метод finalize() вызывается перед сборкой мусора для объекта. Он может быть использован для выполнения очистки ресурсов, но его использование не рекомендуется из-за непредсказуемости момента вызова и возможности замены на более современные механизмы управления ресурсами, такие как try-with-resources и интерфейс AutoCloseable.
Пример использования finalize()
#Java #Training #Object
clone()
Метод clone() создает и возвращает копию объекта. Для использования этого метода класс должен реализовывать интерфейс Cloneable, иначе вызов метода clone() приведет к выбросу исключения CloneNotSupportedException.
Пример использования метода clone()
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
public static void main(String[] args) {
try {
Person person1 = new Person("John", 25);
Person person2 = (Person) person1.clone();
System.out.println(person1);
System.out.println(person2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
wait(), notify() и notifyAll()
Эти методы используются для управления межпотоковым взаимодействием в многопоточных приложениях.
wait(): Приостанавливает текущий поток до тех пор, пока другой поток не вызовет notify() или notifyAll() для этого объекта.
notify(): Пробуждает один поток, ожидающий на этом объекте.
notifyAll(): Пробуждает все потоки, ожидающие на этом объекте.
Эти методы должны вызываться внутри синхронизированного блока или метода.
Пример использования wait() и notify()
public class WaitNotifyExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Thread 1: Waiting for lock...");
lock.wait();
System.out.println("Thread 1: Got the lock!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2: Got the lock!");
lock.notify();
System.out.println("Thread 2: Notified!");
}
});
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
finalize() (В настоящее время не рекомендуется к использованию)
Метод finalize() вызывается перед сборкой мусора для объекта. Он может быть использован для выполнения очистки ресурсов, но его использование не рекомендуется из-за непредсказуемости момента вызова и возможности замены на более современные механизмы управления ресурсами, такие как try-with-resources и интерфейс AutoCloseable.
Пример использования finalize()
public class FinalizeExample {
@Override
protected void finalize() throws Throwable {
try {
System.out.println("Finalize method called");
} finally {
super.finalize();
}
}
public static void main(String[] args) {
FinalizeExample obj = new FinalizeExample();
obj = null;
System.gc(); // Запрос сборщика мусора (не гарантия вызова finalize)
}
}
#Java #Training #Object
Введение в Queue
Queue — это интерфейс в Java, представляющий коллекцию, предназначенную для хранения элементов в порядке, поддерживающем принципы FIFO (First-In-First-Out). Элементы добавляются в конец очереди и извлекаются из начала. Queue является частью коллекций Java и находится в пакете java.util.
Реализации Queue
Существует несколько реализаций интерфейса Queue в Java:
LinkedList: Класс LinkedList реализует интерфейс Queue и предоставляет эффективные операции вставки и удаления с обоих концов списка.
PriorityQueue: Класс PriorityQueue реализует интерфейс Queue и предоставляет приоритетную очередь, в которой элементы упорядочены на основе их естественного порядка или на основе предоставленного компаратора.
ArrayDeque: Класс ArrayDeque реализует интерфейс Deque (двусторонняя очередь) и может использоваться как Queue.
Основные операции:
Добавление элементов:
add(E e): Добавляет элемент в конец очереди. Выбрасывает исключение IllegalStateException, если очередь ограничена и заполнена.
offer(E e): Добавляет элемент в конец очереди. Возвращает false, если очередь ограничена и заполнена.
Удаление элементов:
remove(): Удаляет и возвращает элемент из начала очереди. Выбрасывает исключение NoSuchElementException, если очередь пуста.
poll(): Удаляет и возвращает элемент из начала очереди. Возвращает null, если очередь пуста.
Просмотр элементов:
element(): Возвращает, но не удаляет, элемент из начала очереди. Выбрасывает исключение NoSuchElementException, если очередь пуста.
peek(): Возвращает, но не удаляет, элемент из начала очереди. Возвращает null, если очередь пуста.
Пример использования Queue
#Java #Training #Collections #Queue
Queue — это интерфейс в Java, представляющий коллекцию, предназначенную для хранения элементов в порядке, поддерживающем принципы FIFO (First-In-First-Out). Элементы добавляются в конец очереди и извлекаются из начала. Queue является частью коллекций Java и находится в пакете java.util.
Реализации Queue
Существует несколько реализаций интерфейса Queue в Java:
LinkedList: Класс LinkedList реализует интерфейс Queue и предоставляет эффективные операции вставки и удаления с обоих концов списка.
Queue<String> queue = new LinkedList<>();
PriorityQueue: Класс PriorityQueue реализует интерфейс Queue и предоставляет приоритетную очередь, в которой элементы упорядочены на основе их естественного порядка или на основе предоставленного компаратора.
Queue<String> priorityQueue = new PriorityQueue<>();
ArrayDeque: Класс ArrayDeque реализует интерфейс Deque (двусторонняя очередь) и может использоваться как Queue.
Queue<String> arrayDeque = new ArrayDeque<>();
Основные операции:
Добавление элементов:
add(E e): Добавляет элемент в конец очереди. Выбрасывает исключение IllegalStateException, если очередь ограничена и заполнена.
queue.add("Element");
offer(E e): Добавляет элемент в конец очереди. Возвращает false, если очередь ограничена и заполнена.
queue.offer("Element");
Удаление элементов:
remove(): Удаляет и возвращает элемент из начала очереди. Выбрасывает исключение NoSuchElementException, если очередь пуста.
String element = queue.remove();
poll(): Удаляет и возвращает элемент из начала очереди. Возвращает null, если очередь пуста.
String element = queue.poll();
Просмотр элементов:
element(): Возвращает, но не удаляет, элемент из начала очереди. Выбрасывает исключение NoSuchElementException, если очередь пуста.
String element = queue.element();
peek(): Возвращает, но не удаляет, элемент из начала очереди. Возвращает null, если очередь пуста.
String element = queue.peek();
Пример использования Queue
import java.util.LinkedList;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
// Добавление элементов
queue.add("Element 1");
queue.offer("Element 2");
// Просмотр элемента
System.out.println("Peek: " + queue.peek());
// Удаление элементов
System.out.println("Removed: " + queue.remove());
System.out.println("Polled: " + queue.poll());
// Попытка просмотра/удаления из пустой очереди
System.out.println("Peek from empty queue: " + queue.peek());
System.out.println("Poll from empty queue: " + queue.poll());
}
}
#Java #Training #Collections #Queue
Что выведет код?
#Tasks
import java.util.*;
public class QueueExample {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.offer(4);
queue.poll();
queue.remove();
System.out.println(queue);
}
}
#Tasks
Когда Вам удобно принять участие в онлайн встрече?
Anonymous Poll
0%
В субботу
43%
В воскресение
57%
Хоть когда, пофиг!
0%
Никогда! Отстаньте от меня!!!
Введение в Stack
Stack — это класс в Java, представляющий коллекцию, предназначенную для хранения элементов в порядке, поддерживающем принципы LIFO (Last-In-First-Out). Элементы добавляются и извлекаются с одного конца стека, называемого вершиной. Класс Stack находится в пакете java.util и наследуется от класса Vector.
Создание Stack
Для создания экземпляра Stack нужно импортировать класс java.util.Stack и использовать его конструктор:
Основные операции
Добавление элементов:
push(E item): Добавляет элемент на вершину стека.
Удаление элементов:
pop(): Удаляет и возвращает элемент с вершины стека. Выбрасывает исключение EmptyStackException, если стек пуст.
Просмотр элементов:
peek(): Возвращает, но не удаляет, элемент с вершины стека. Выбрасывает исключение EmptyStackException, если стек пуст.
Проверка состояния стека:
empty(): Проверяет, пуст ли стек. Возвращает true, если стек пуст, и false в противном случае.
search(Object o): Ищет элемент в стеке и возвращает его положение, начиная с вершины стека. Возвращает -1, если элемент не найден.
Пример использования Stack
Сравнение Queue и Stack
Очередь (Queue): Использует порядок FIFO (First-In-First-Out). Элементы добавляются в конец и удаляются из начала.
Стек (Stack): Использует порядок LIFO (Last-In-First-Out). Элементы добавляются и удаляются с вершины.
#Java #Training #Collections
Stack — это класс в Java, представляющий коллекцию, предназначенную для хранения элементов в порядке, поддерживающем принципы LIFO (Last-In-First-Out). Элементы добавляются и извлекаются с одного конца стека, называемого вершиной. Класс Stack находится в пакете java.util и наследуется от класса Vector.
Создание Stack
Для создания экземпляра Stack нужно импортировать класс java.util.Stack и использовать его конструктор:
Stack<String> stack = new Stack<>();
Основные операции
Добавление элементов:
push(E item): Добавляет элемент на вершину стека.
stack.push("Element");
Удаление элементов:
pop(): Удаляет и возвращает элемент с вершины стека. Выбрасывает исключение EmptyStackException, если стек пуст.
String element = stack.pop();
Просмотр элементов:
peek(): Возвращает, но не удаляет, элемент с вершины стека. Выбрасывает исключение EmptyStackException, если стек пуст.
String element = stack.peek();
Проверка состояния стека:
empty(): Проверяет, пуст ли стек. Возвращает true, если стек пуст, и false в противном случае.
boolean isEmpty = stack.empty();
search(Object o): Ищет элемент в стеке и возвращает его положение, начиная с вершины стека. Возвращает -1, если элемент не найден.
int position = stack.search("Element");
Пример использования Stack
import java.util.Stack;
public class StackExample {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
// Добавление элементов
stack.push("Element 1");
stack.push("Element 2");
stack.push("Element 3");
// Просмотр элемента
System.out.println("Peek: " + stack.peek());
// Удаление элементов
System.out.println("Popped: " + stack.pop());
System.out.println("Popped: " + stack.pop());
// Проверка состояния стека
System.out.println("Is stack empty? " + stack.empty());
// Поиск элемента
stack.push("Element 4");
stack.push("Element 5");
System.out.println("Position of 'Element 4': " + stack.search("Element 4"));
// Удаление оставшихся элементов
System.out.println("Popped: " + stack.pop());
System.out.println("Popped: " + stack.pop());
// Попытка просмотра/удаления из пустого стека
System.out.println("Is stack empty? " + stack.empty());
}
}
Сравнение Queue и Stack
Очередь (Queue): Использует порядок FIFO (First-In-First-Out). Элементы добавляются в конец и удаляются из начала.
Стек (Stack): Использует порядок LIFO (Last-In-First-Out). Элементы добавляются и удаляются с вершины.
#Java #Training #Collections
Напоминаю, что у нас есть чат - https://t.me/Java_Beginner_chat 😂
Дженерики
Дженерики (Generics) — это механизм в языке программирования Java, который позволяет создавать классы, интерфейсы и методы с параметризованными типами. Они были введены в Java 5 для обеспечения безопасности типов и устранения необходимости явных приведений типов. Дженерики позволяют писать универсальный код, который может работать с разными типами данных, не теряя при этом безопасность типов.
Зачем нужны дженерики?
Безопасность типов: Дженерики позволяют обнаруживать ошибки на этапе компиляции, предотвращая проблемы, связанные с приведением типов и некорректным использованием объектов.
Повторное использование кода: Дженерики позволяют создавать универсальные классы, интерфейсы и методы, которые могут работать с любыми типами данных.
Читаемость и удобство кода: Код с дженериками легче читать и понимать, так как не нужно явно приводить типы.
Основные концепции дженериков
Обобщенные классы: Классы, которые могут работать с любыми типами данных.
В этом примере T — это параметризованный тип. При создании экземпляра класса Box вы можете указать конкретный тип:
Обобщенные методы: Методы, которые могут работать с любыми типами данных.
Вызов обобщенного метода:
Обобщенные интерфейсы: Интерфейсы, которые могут работать с любыми типами данных.
Использование обобщенного интерфейса:
Преимущества дженериков
Повышение безопасности типов: Программисты могут избегать классовых приведений, которые могут привести к ошибкам времени выполнения. Дженерики позволяют обнаруживать ошибки на этапе компиляции.
Повторное использование кода: Дженерики позволяют разработчикам создавать классы, методы и интерфейсы, которые могут работать с любыми типами данных.
Улучшение читаемости кода: Дженерики позволяют программистам писать более понятный и краткий код, не нуждаясь в явных приведениях типов.
Рассмотрим простой пример с использованием дженериков. Создадим класс, который может хранить пару объектов любого типа:
Теперь мы можем использовать этот класс с любыми типами данных:
#Java #Training #Generics
Дженерики (Generics) — это механизм в языке программирования Java, который позволяет создавать классы, интерфейсы и методы с параметризованными типами. Они были введены в Java 5 для обеспечения безопасности типов и устранения необходимости явных приведений типов. Дженерики позволяют писать универсальный код, который может работать с разными типами данных, не теряя при этом безопасность типов.
Зачем нужны дженерики?
Безопасность типов: Дженерики позволяют обнаруживать ошибки на этапе компиляции, предотвращая проблемы, связанные с приведением типов и некорректным использованием объектов.
Повторное использование кода: Дженерики позволяют создавать универсальные классы, интерфейсы и методы, которые могут работать с любыми типами данных.
Читаемость и удобство кода: Код с дженериками легче читать и понимать, так как не нужно явно приводить типы.
Основные концепции дженериков
Обобщенные классы: Классы, которые могут работать с любыми типами данных.
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
В этом примере T — это параметризованный тип. При создании экземпляра класса Box вы можете указать конкретный тип:
Box<Integer> integerBox = new Box<>();
integerBox.set(10);
System.out.println(integerBox.get());
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
System.out.println(stringBox.get());
Обобщенные методы: Методы, которые могут работать с любыми типами данных.
public class Util {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
}
Вызов обобщенного метода:
Integer[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"one", "two", "three"};
Util.printArray(intArray);
Util.printArray(strArray);
Обобщенные интерфейсы: Интерфейсы, которые могут работать с любыми типами данных.
public interface Pair<K, V> {
K getKey();
V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
}
Использование обобщенного интерфейса:
Pair<String, Integer> pair = new OrderedPair<>("One", 1);
System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
Преимущества дженериков
Повышение безопасности типов: Программисты могут избегать классовых приведений, которые могут привести к ошибкам времени выполнения. Дженерики позволяют обнаруживать ошибки на этапе компиляции.
Повторное использование кода: Дженерики позволяют разработчикам создавать классы, методы и интерфейсы, которые могут работать с любыми типами данных.
Улучшение читаемости кода: Дженерики позволяют программистам писать более понятный и краткий код, не нуждаясь в явных приведениях типов.
Рассмотрим простой пример с использованием дженериков. Создадим класс, который может хранить пару объектов любого типа:
public class Pair<T, U> {
private T first;
private U second;
public Pair(T first, U second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public U getSecond() {
return second;
}
public void setFirst(T first) {
this.first = first;
}
public void setSecond(U second) {
this.second = second;
}
@Override
public String toString() {
return "Pair{" + "first=" + first + ", second=" + second + '}';
}
}
Теперь мы можем использовать этот класс с любыми типами данных:
Pair<Integer, String> pair = new Pair<>(1, "One");
System.out.println(pair); // Output: Pair{first=1, second=One}
pair.setFirst(2);
pair.setSecond("Two");
System.out.println(pair); // Output: Pair{first=2, second=Two}
#Java #Training #Generics
Что выведет код?
#Tasks
public class TypeCastingExample {
public static void main(String[] args) {
Parent parent = new Child();
Child child;
try {
child = (Child) parent;
System.out.println(child.getMessage());
} catch (ClassCastException e) {
System.out.println("ClassCastException");
}
try {
Parent anotherParent = new AnotherChild();
child = (Child) anotherParent;
System.out.println(child.getMessage());
} catch (ClassCastException e) {
System.out.println("ClassCastException");
}
}
}
class Parent {
String getMessage() {
return "Parent";
}
}
class Child extends Parent {
@Override
String getMessage() {
return "Child";
}
}
class AnotherChild extends Parent {
@Override
String getMessage() {
return "AnotherChild";
}
}
#Tasks
Варианты ответа:
Anonymous Quiz
67%
Child ClassCastException
0%
Child Child
0%
Parent ClassCastException
33%
Child AnotherChild
Forwarded from English Beginners
Продвинутые возможности дженериков
Ограничения типов
Ограниченные параметры типов (Bounded Type Parameters): Позволяют задать верхние и нижние границы для параметризованных типов.
В этом примере T должен быть подклассом Number.
Множественные ограничения (Multiple Bounds): Параметр типа может быть ограничен несколькими типами.
Подстановочные знаки (Wildcards)
Неограниченные подстановочные знаки: Позволяют параметризованному типу принимать любой тип.
Ограниченные сверху подстановочные знаки (Upper Bounded Wildcards): Позволяют параметризованному типу принимать любой тип, который является подклассом указанного.
Ограниченные снизу подстановочные знаки (Lower Bounded Wildcards): Позволяют параметризованному типу принимать любой тип, который является суперклассом указанного.
Преимущества и недостатки дженериков
Преимущества:
Повышение безопасности типов.
Уменьшение необходимости приведения типов.
Повышение читаемости и повторного использования кода.
Недостатки:
Усложнение кода для начинающих программистов.
Невозможность использовать примитивные типы в качестве параметров типов.
Ограничения, связанные с типизацией во время выполнения (Type Erasure).
Типизация во время выполнения (Type Erasure)
Дженерики в Java реализованы с использованием механизма стирания типов (Type Erasure). Это означает, что информация о параметризованных типах удаляется во время компиляции, и все операции с дженериками выполняются с использованием типов Object или соответствующих ограниченных типов. Это позволяет обеспечить совместимость с кодом, не использующим дженерики, но накладывает некоторые ограничения, такие как невозможность использования примитивных типов и невозможность создания массивов параметризованных типов.
Примеры сложных случаев использования дженериков
Рекурсивные обобщенные типы:
Обобщенные методы в обобщенных классах:
Использование:
#Java #Training #Generics
Ограничения типов
Ограниченные параметры типов (Bounded Type Parameters): Позволяют задать верхние и нижние границы для параметризованных типов.
public class Box<T extends Number> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
В этом примере T должен быть подклассом Number.
Множественные ограничения (Multiple Bounds): Параметр типа может быть ограничен несколькими типами.
public <T extends Number & Comparable<T>> T findMax(T[] array) {
T max = array[0];
for (T element : array) {
if (element.compareTo(max) > 0) {
max = element;
}
}
return max;
}
Подстановочные знаки (Wildcards)
Неограниченные подстановочные знаки: Позволяют параметризованному типу принимать любой тип.
public void printList(List<?> list) {
for (Object element : list) {
System.out.println(element);
}
}
Ограниченные сверху подстановочные знаки (Upper Bounded Wildcards): Позволяют параметризованному типу принимать любой тип, который является подклассом указанного.
public void addNumbers(List<? extends Number> list) {
double sum = 0;
for (Number number : list) {
sum += number.doubleValue();
}
System.out.println("Sum: " + sum);
}
Ограниченные снизу подстановочные знаки (Lower Bounded Wildcards): Позволяют параметризованному типу принимать любой тип, который является суперклассом указанного.
public void addIntegers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
Преимущества и недостатки дженериков
Преимущества:
Повышение безопасности типов.
Уменьшение необходимости приведения типов.
Повышение читаемости и повторного использования кода.
Недостатки:
Усложнение кода для начинающих программистов.
Невозможность использовать примитивные типы в качестве параметров типов.
Ограничения, связанные с типизацией во время выполнения (Type Erasure).
Типизация во время выполнения (Type Erasure)
Дженерики в Java реализованы с использованием механизма стирания типов (Type Erasure). Это означает, что информация о параметризованных типах удаляется во время компиляции, и все операции с дженериками выполняются с использованием типов Object или соответствующих ограниченных типов. Это позволяет обеспечить совместимость с кодом, не использующим дженерики, но накладывает некоторые ограничения, такие как невозможность использования примитивных типов и невозможность создания массивов параметризованных типов.
Примеры сложных случаев использования дженериков
Рекурсивные обобщенные типы:
public class ComparableBox<T extends Comparable<T>> {
private T value;
public ComparableBox(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public int compareTo(ComparableBox<T> other) {
return value.compareTo(other.getValue());
}
}
Обобщенные методы в обобщенных классах:
public class Util {
public static <T> boolean compare(Box<T> box1, Box<T> box2) {
return box1.get().equals(box2.get());
}
}
Использование:
Box<Integer> box1 = new Box<>();
box1.set(10);
Box<Integer> box2 = new Box<>();
box2.set(10);
boolean result = Util.compare(box1, box2); // true
#Java #Training #Generics