Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥1
Методы
equals и hashCode являются методами класса java.lang.Object в Java. Все классы в Java, неявно или явно, наследуются от класса Object, поэтому они унаследуют эти методы.Класс
Object является корневым классом в иерархии классов Java. Каждый класс в Java наследуется от этого класса, либо прямо, либо через цепочку других классов.Метод
equals используется для сравнения объектов на равенство. Метод equals должен следовать следующим правилам: Симметричность: Для любых ненулевых значений x и y, x.equals(y) должно возвращать true, если и только если y.equals(x) возвращает true.
Транзитивность: Для любых ненулевых значений x, y и z, если x.equals(y) возвращает true и y.equals(z) возвращает true, то x.equals(z) должно возвращать true.
Согласованность: Для любых ненулевых значений x и y, повторные вызовы x.equals(y) должны возвращать одно и то же значение, пока объекты остаются неизменными.
Сравнение с null: Для любого ненулевого значения x, x.equals(null) должно возвращать false.
Метод
hashCode возвращает хеш-код объекта, который используется для повышения производительности в структурах данных, таких как HashMap, HashSet и Hashtable.Подразумеваемый контракт: Метод hashCode должен следовать следующим правилам:
Согласованность: Если объект не изменяется, повторные вызовы метода hashCode должны возвращать одно и то же значение.
Согласованность с equals: Если два объекта равны согласно методу equals, их хеш-коды также должны быть равны.
Неравенство: Если два объекта не равны согласно методу equals, их хеш-коды не обязательно должны быть различными, но желательно минимизировать количество таких коллизий.
Чтобы обеспечить правильное поведение этих методов в пользовательских классах, их часто переопределяют.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
System.out.println(person1.equals(person2)); // true
System.out.println(person1.hashCode() == person2.hashCode()); // true
}
}Современные IDE, такие как IntelliJ IDEA и Eclipse, могут автоматически генерировать методы
equals и hashCode на основе полей класса. Это помогает избежать ошибок и обеспечить правильное соблюдение контрактов.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥3
Хеш-коды могут быть равны для разных объектов из-за того, что они являются конечными представлениями данных, а количество возможных хеш-кодов ограничено. В Java метод
hashCode возвращает значение типа int, что означает, что существует только \(2^{32}\) возможных значений хеш-кодов. Однако количество возможных объектов значительно больше, чем \(2^{32}\), что приводит к коллизиям хеш-кодов.Хеш-коды представлены 32-битным целым числом, что дает \(2^{32}\) возможных значений. Это означает, что множество объектов должно быть отображено в это ограниченное пространство хеш-кодов, что приводит к коллизиям.
Коллизия возникает, когда два разных объекта имеют одинаковый хеш-код. Это неизбежно при использовании конечного диапазона хеш-кодов для представления множества объектов.
В Java структуры данных, такие как
HashMap и HashSet, обрабатывают коллизии хеш-кодов, используя внутренние механизмы для разрешения коллизий. При коллизии хеш-кодов, когда два разных объекта имеют одинаковый хеш-код, они все равно могут быть правильно размещены и найдены в хеш-таблице.public class Example {
private int value;
public Example(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Example example = (Example) obj;
return value == example.value;
}
@Override
public int hashCode() {
return value % 10; // Искусственно создаем коллизии для демонстрации
}
public static void main(String[] args) {
Example ex1 = new Example(1);
Example ex2 = new Example(11);
System.out.println(ex1.hashCode()); // 1
System.out.println(ex2.hashCode()); // 1
System.out.println(ex1.equals(ex2)); // false
HashSet<Example> set = new HashSet<>();
set.add(ex1);
set.add(ex2);
System.out.println(set.size()); // 2, так как объекты разные
}
}Каждый элемент в хеш-таблице указывает на список (или другую структуру), содержащий все элементы с одинаковым хеш-кодом.
В случае коллизии алгоритм ищет другую позицию в хеш-таблице для размещения элемента, используя определенную стратегию пробирования (linear probing, quadratic probing и т.д.).
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤1
Anonymous Quiz
65%
a) substring()
24%
b) subString()
8%
c) getSubstring()
3%
d) extract()
👾6👍5🤔2
Модель памяти Java (Java Memory Model, JMM) определяет, как взаимодействуют потоки через память и как изменения, сделанные одним потоком, становятся видимыми для других потоков. Модель памяти Java является фундаментальной частью многопоточной среды в Java, обеспечивающей корректность и предсказуемость поведения многопоточных программ.
JMM определяет, как потоки взаимодействуют с переменными (данными), хранящимися в общей памяти. Каждая переменная в Java хранится в основной памяти (main memory), и потоки могут иметь локальные копии этих переменных в своих рабочих кешах.
Когда поток читает переменную, он может читать ее из своей локальной копии или из основной памяти. Когда поток записывает переменную, он может записывать ее в свою локальную копию или непосредственно в основную память.
Последовательная согласованность гарантирует, что действия всех потоков будут выполняться в том порядке, в котором они были написаны в коде, если нет явных указаний на обратное.
Видимость означает, что изменения, сделанные одним потоком, становятся видимыми для других потоков. В JMM видимость изменений обеспечивается с помощью синхронизации.
JMM допускает оптимизации, такие как переупорядочивание инструкций, чтобы улучшить производительность, но гарантирует, что видимость и порядок выполнения будут сохраняться, как описано в спецификации.
public class VisibilityExample {
private static boolean flag = false;
private static int counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread writer = new Thread(() -> {
counter = 42;
flag = true;
});
Thread reader = new Thread(() -> {
while (!flag) {
// Ждем пока флаг не станет true
}
System.out.println("Counter: " + counter);
});
writer.start();
reader.start();
writer.join();
reader.join();
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍28🔥9
В Java Streams API потоки данных (
Stream) начинают своё выполнение только при вызове терминального операции. Это ключевая особенность, которая отличает ленивые (lazy) промежуточные операции от жадных (eager) терминальных операций.Промежуточные операции возвращают новый поток и не инициируют выполнение. Они являются ленивыми, то есть, они откладывают выполнение до тех пор, пока не будет вызвана терминальная операция. Примеры:
filter, map, sorted, distinct, flatMap, и т.д. Промежуточные операции составляют цепочку, которая настраивает конвейер обработки данных. Stream<String> stream = Stream.of("one", "two", "three")
.filter(s -> s.length() > 3)
.map(String::toUpperCase); Терминальные операции запускают выполнение всего конвейера потоков. Они потребляют поток и возвращают либо результат, либо выполняют побочные действия. Примеры:
forEach, collect, reduce, count, findFirst, findAny, allMatch, noneMatch, и т.д. long count = stream.count(); // Запуск выполнения
Рассмотрим пример, где промежуточные операции
filter и map не инициируют выполнение, а только настройку конвейера. Выполнение начинается только при вызове терминальной операции forEach. import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Настройка конвейера операций
Stream<String> nameStream = names.stream()
.filter(name -> {
System.out.println("Filter: " + name);
return name.length() > 3;
})
.map(name -> {
System.out.println("Map: " + name);
return name.toUpperCase();
});
System.out.println("Before terminal operation");
// Терминальная операция, которая запускает выполнение
nameStream.forEach(name -> System.out.println("Final result: " + name));
System.out.println("After terminal operation");
}
}
Вывод на экран покажет, что промежуточные операции выполняются только при вызове терминальной операции.
Before terminal operation
Filter: Alice
Map: Alice
Final result: ALICE
Filter: Bob
Filter: Charlie
Map: Charlie
Final result: CHARLIE
Filter: David
Map: David
Final result: DAVID
After terminal operation
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19🔥4
Модификаторы доступа в Java определяют уровень доступа к классам, методам и переменным. Однако, если говорить о
equals и hashCode в контексте модификаторов, то мы имеем в виду их реализацию по умолчанию, предоставляемую классом java.lang.Object. Метод
equals в классе Object по умолчанию проверяет, являются ли два объекта одним и тем же экземпляром. Это проверка на ссылочное равенство. public boolean equals(Object obj) {
return (this == obj);
}Метод
hashCode в классе Object по умолчанию возвращает уникальный целочисленный идентификатор для каждого объекта. Этот идентификатор обычно представляет собой адрес объекта в памяти. public native int hashCode();
Переопределение методов
equals и hashCode необходимо, когда вы хотите, чтобы два различных объекта были равны, если их состояния равны, а не если они являются одним и тем же объектом.public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public static void main(String[] args) {
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode() == p2.hashCode()); // true
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤1
В контексте Java, Heap (куча) и Stack (стек) являются областями памяти, используемыми JVM для управления памятью, необходимой для выполнения программы. Каждая из этих областей имеет свои характеристики и используется для разных целей.
Heap — это область памяти, выделенная для динамического распределения памяти объектов и массивов. Все объекты, созданные с использованием оператора
new, размещаются в куче.Куча разделена на поколения: молодое поколение (Young Generation) и старое поколение (Old Generation).
Молодое поколение включает в себя области Eden Space и Survivor Spaces (S0 и S1).
Старое поколение хранит долгоживущие объекты.
Куча управляется сборщиком мусора (Garbage Collector), который автоматически освобождает память, занятую объектами, которые больше не используются.
Куча используется для хранения объектов, массивов и классов, информация о которых сохраняется на протяжении всего времени их жизни.
public class Example {
public static void main(String[] args) {
Example obj = new Example(); // obj создается в куче
}
}Стек — это область памяти, используемая для управления вызовами методов и хранения локальных переменных, параметров методов и информации о возвратах.
Каждый поток имеет свой собственный стек.
Стек хранит кадры (frames) для каждого вызова метода. Каждый кадр содержит локальные переменные метода и информацию о вызовах.
Память в стеке автоматически управляется при вызове методов и выходе из них. Когда метод вызывается, создается новый кадр в стеке; когда метод завершает выполнение, его кадр удаляется из стека.
Стек используется для хранения примитивных типов данных и ссылок на объекты, которые находятся в куче.
Локальные переменные методов и параметры методов хранятся в стеке.
public class Example {
public static void main(String[] args) {
int localVar = 10; // localVar хранится в стеке
Example obj = new Example(); // Ссылка на obj хранится в стеке, а сам объект — в куче
obj.method();
}
public void method() {
int anotherVar = 20; // anotherVar хранится в стеке
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤2🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26🔥10
Реализация метода
hashCode в Java может привести к нескольким проблемам, если она сделана неправильно. Эти проблемы могут привести к некорректной работе хэш-структур данных, таких как HashMap, HashSet и другие. Рассмотрим основные проблемы, которые могут возникнуть при неправильной реализации метода hashCode.Если два объекта равны согласно методу
equals, они должны иметь одинаковый хэш-код. Нарушение этого правила приведет к тому, что объекты, которые равны с точки зрения equals, могут оказаться в разных корзинах хэш-таблицы. Убедитесь, что если equals возвращает true для двух объектов, то их хэш-коды одинаковы. @Override
public boolean equals(Object obj) {
// Реализация метода equals
}
@Override
public int hashCode() {
// Реализация метода hashCode
}
Если объект, который используется в качестве ключа в
HashMap, изменяется таким образом, что изменяется его хэш-код, это может привести к тому, что объект станет недоступным. Избегайте использования изменяемых объектов в качестве ключей в хэш-таблицах или обеспечьте, чтобы поля, влияющие на хэш-код, не изменялись после создания объекта. // Плохой пример
Map<Person, String> map = new HashMap<>();
Person person = new Person("Alice");
map.put(person, "Engineer");
person.setName("Bob"); // Изменение, влияющее на hashCode
String profession = map.get(person); // Может вернуть null
Два неравных объекта могут иметь одинаковый хэш-код, что приведет к увеличению числа коллизий. Это может снизить производительность хэш-таблицы. Хотя это невозможно полностью избежать, хорошая реализация
hashCode должна стараться минимизировать количество таких коллизий. @Override
public int hashCode() {
int result = 17;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
Если метод
hashCode реализован неэффективно, это может привести к снижению производительности всей программы. Убедитесь, что метод hashCode выполняется быстро и эффективно, особенно для часто используемых объектов.Слишком сложные вычисления в методе
hashCode могут негативно сказаться на производительности. Используйте простые и эффективные алгоритмы для вычисления хэш-кода. @Override
public int hashCode() {
return Objects.hash(name, age);
}
Пример правильной реализации
equals и hashCode import java.util.Objects;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤1🔥1
2. List (например, ArrayList, LinkedList) — упорядоченные коллекции, поддерживающие дублирующиеся элементы.
3. Set (например, HashSet, TreeSet) — коллекции, хранящие только уникальные элементы.
4. Queue (например, PriorityQueue, LinkedList) — коллекции с FIFO-поведеним.
5. Map — пары ключ-значение (HashMap, TreeMap) — не наследуется от Collection.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤2
Это механизм, используемый в Java для обеспечения обратной совместимости между старым кодом, написанным до введения обобщений (generics) в Java 5, и новым кодом, который их использует. Стирание типов позволяет компилировать обобщенный код в байт-код, совместимый с JVM, который не поддерживает обобщения.
Позволяет использовать старый код, написанный до введения обобщений, вместе с новым обобщенным кодом без изменений в существующем коде.
Обеспечивает единообразие работы с различными типами, минимизируя избыточность в коде и устраняя необходимость дублирования кода для разных типов.
При компиляции обобщенного кода компилятор Java удаляет информацию о типах (стирает типы) и заменяет их на их необобщенные версии или верхние границы (bounds). В результате обобщенный код компилируется в байт-код, который может выполняться на обычной JVM.
Обобщенный класс
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}После стирания типов компилированный код будет выглядеть примерно так
public class Box {
private Object value;
public void set(Object value) {
this.value = value;
}
public Object get() {
return value;
}
}После стирания типов информация о типах удаляется, и во время выполнения типовые параметры становятся объектами
Object.Обобщения работают только с ссылочными типами, так как примитивные типы не могут быть использованы в качестве типовых параметров.
Невозможно получить информацию о типовых параметрах через рефлексию, так как она теряется во время компиляции.
Невозможность создания массивов обобщенных типов
public class Box<T> {
private T value;
public T[] createArray(int size) {
return new T[size]; // Ошибка компиляции
}
}Обходное решение с использованием рефлексии
public class Box<T> {
private T value;
private Class<T> type;
public Box(Class<T> type) {
this.type = type;
}
@SuppressWarnings("unchecked")
public T[] createArray(int size) {
return (T[]) java.lang.reflect.Array.newInstance(type, size);
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍28
Процесс обработки JSP страницы начинается с запроса от клиента к серверу и заканчивается ответом серверу клиенту. Этот процесс включает несколько этапов, в которых страница компилируется в сервлет, выполняется, и результат возвращается клиенту.
Когда клиент (обычно это веб-браузер) отправляет HTTP-запрос для получения страницы, запрос передается на веб-сервер.
Веб-сервер, получив запрос, передает его на сервлет-контейнер (например, Apache Tomcat), который обрабатывает запросы для JSP страниц.
Проверка необходимости компиляции:
Первый запрос или обновление: Если это первый запрос к данной JSP странице или если страница была изменена, сервлет-контейнер проверяет, скомпилирована ли JSP страница.
Кеширование: Если страница уже была скомпилирована и не изменялась, этот шаг пропускается.
Компиляция:
Страница компилируется в сервлетный код (Java класс).
Сервлетный код компилируется в байт-код (класс-файл).
Скомпилированный сервлет загружается в память, и контейнер вызывает метод
init(), если сервлет еще не был загружен.Контейнер создает объекты
HttpServletRequest и HttpServletResponse, представляющие запрос клиента и ответ сервера соответственно, и передает их методу service() сервлета, который затем вызывает соответствующие методы doGet() или doPost().public class ExampleJspServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Dynamic content from JSP</h1>");
out.println("<p>Current time: " + new java.util.Date() + "</p>");
out.println("</body></html>");
}
}Сервлет выполняет свою логику, включая:
Доступ к параметрам запроса.
Взаимодействие с базой данных или другими серверными ресурсами.
Динамическое создание HTML контента.
Сервлет формирует HTML страницу, которая возвращается клиенту. Содержимое передается через объект
HttpServletResponse.Ответ возвращается через веб-сервер клиенту (например, браузеру), который отображает полученный HTML контент пользователю.
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<noscript>Example JSP</noscript>
</head>
<body>
<h1>Welcome to JSP</h1>
<p>Current time: <%= new java.util.Date() %></p>
</body>
</html>
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🎉1👀1
Центральный компонент в Spring MVC, который обрабатывает входящие HTTP-запросы. Он распределяет запросы к соответствующим контроллерам, управляет данными и формирует ответы. Этот компонент действует как координатор между различными частями приложения, такими как контроллеры, сервисы и представления.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1🔥1
Жизненный цикл JSP страницы включает несколько этапов, которые происходят с момента запроса страницы клиентом до ее ответа. Эти этапы позволяют сервлет-контейнеру управлять страницей, обеспечивая её корректное выполнение и оптимизацию.
На этом этапе страница переводится в сервлетный код. Это означает, что JSP контейнер берет исходный код страницы и генерирует соответствующий Java код для сервлета.
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<noscript>Example JSP</noscript>
</head>
<body>
<h1>Welcome to JSP</h1>
<p>Current time: <%= new java.util.Date() %></p>
</body>
</html>
Переведенный сервлетный код компилируется в байт-код (класс-файл). Это стандартная компиляция Java, которая создает скомпилированный класс из Java исходного кода.
На этом этапе скомпилированный класс загружается в память JVM (Java Virtual Machine). Это стандартный процесс загрузки классов в Java, осуществляемый класс-загрузчиком.
Контейнер вызывает метод
jspInit() сервлета, который соответствует JSP странице. Этот метод аналогичен методу init() в обычных сервлетах и вызывается один раз, когда сервлет загружается в память. Здесь можно выполнять любые операции инициализации, такие как настройка ресурсов.public void jspInit() {
// Инициализационный код
}Когда клиент отправляет запрос к JSP странице, контейнер создает объекты
HttpServletRequest и HttpServletResponse и передает их методу jspService(), который обрабатывает запросы и генерирует ответ. Этот метод вызывается каждый раз при поступлении запроса.public void _jspService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<html><head><noscript>Example JSP</noscript></head><body>");
out.println("<h1>Welcome to JSP</h1>");
out.println("<p>Current time: " + new java.util.Date() + "</p>");
out.println("</body></html>");
}
}Когда контейнер решает выгрузить JSP страницу (например, при завершении работы приложения или перезагрузке сервера), он вызывает метод
jspDestroy(). Этот метод аналогичен методу destroy() в обычных сервлетах и позволяет выполнять любые операции очистки, такие как освобождение ресурсов.public void jspDestroy() {
// Очистка ресурсов
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤1