Java | Фишки и трюки
7.21K subscribers
182 photos
29 videos
6 files
40 links
Java: примеры кода, интересные фишки и полезные трюки

Купить рекламу: https://telega.in/c/java_tips_and_tricks

✍️По всем вопросам: @Pascal4eg
Download Telegram
⌨️ Есть ли какие-либо рекомендации о том, какие поля следует использовать при подсчете hashCode()?

Общий совет: выбирать поля, которые с большой долью вероятности будут различаться. Для этого необходимо использовать уникальные, лучше всего примитивные поля, например, такие как id, uuid. При этом нужно следовать правилу, если поля задействованы при вычислении hashCode(), то они должны быть задействованы и при выполнении equals().

#java #hashCode #equals
Please open Telegram to view this post
VIEW IN TELEGRAM
👍102
⌨️ Могут ли у разных объектов быть одинаковые hashCode()?

Да, могут. Метод hashCode() не гарантирует уникальность возвращаемого значения. Ситуация, когда у разных объектов одинаковые хэш коды называется коллизией. Вероятность возникновения коллизии зависит от используемого алгоритма генерации хэш кода.

#java #hashCode
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍63
public interface Adder {
int add(int a, int b);
}



public interface SmartAdder extends Adder {
int add(double a, double b);
}



public interface Nothing {
}
👍51
Какой из интерфейсов является функциональным?
Anonymous Quiz
48%
Adder
12%
SmartAdder
9%
Nothing
31%
Ни один из них
3👍1
⌨️ Суть лямбда-выражений

Лямбда-выражения на первый взгляд могут показаться чем-то сложным и загадочным, но на самом деле они просты и интуитивно понятны.

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

Если говорить ещё проще, лямбда-выражение — это просто другой способ создания и реализации объекта определённого типа. Рассмотрим это на примере создания нового потока.

У класса Thread есть конструктор:

public Thread(Runnable target) {
...
}


То есть в конструктор нужно передать объект типа Runnable. До лямбда-выражений мы сделали бы так:

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();

Здесь мы создаём анонимный класс, реализующий интерфейс Runnable, с определённым методом run.
Если использовать лямбда-выражение, тот же код будет выглядеть следующим образом:

Runnable r = () -> System.out.println("Hello World");
new Thread(r).start();

Или проще:

new Thread(() -> System.out.println("Hello World")).start();


Лямбда-выражение заменяет собой анонимный класс, который раньше был бы необходим для реализации Runnable. Лямбда-выражение может использоваться только там, где ожидается реализация функционального интерфейса — интерфейса с единственным абстрактным методом.
А интерфейс Runnable именно такой:

@FunctionalInterface
public interface Runnable {
public abstract void run();
}


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

#java #lambda #Runnable
Please open Telegram to view this post
VIEW IN TELEGRAM
2🔥15👍74👎1
Какой интерфейс нужно реализовать в классе, чтобы объект этого класса можно было использовать в выражении "for-each" справа?
Anonymous Quiz
81%
Iterable
9%
Comparable
6%
Serializable
4%
Runnable
👍2🎉1
class Person {
Long id;
String name;

public Person(Long id, String name) {
this.id = id;
this.name = name;
}

public boolean equals(Person other) {
if (other == this) {
return true;
}
return id != null && id.equals(other.id);
}
}



public class Quest {
public static void main(String[] args) {
Person person1 = new Person(1L, "John");
Person person2 = new Person(1L, "Jane");
System.out.print(person1.equals(person2));
System.out.print("-");
System.out.print(Objects.equals(person1, person2));
}
}


#java #quest
Что выведет код?
Anonymous Quiz
29%
true-true
36%
true-false
13%
false-true
22%
false-false
1👍5🔥1🎉1
⌨️ Клонирование объектов

Использование оператора присваивания не создает нового объекта, а лишь копирует ссылку на объект. Таким образом, две ссылки указывают на одну и ту же область памяти, на один и тот же объект. Для создания нового объекта с таким же состоянием используется клонирование объекта.

Класс Object содержит protected метод clone(), осуществляющий побитовое копирование объекта производного класса. Однако сначала необходимо переопределить метод clone() как public для обеспечения возможности его вызова. В переопределенном методе следует вызвать базовую версию метода super.clone(), которая и выполняет собственно клонирование.

Чтобы окончательно сделать объект клонируемым, класс должен реализовать интерфейс Cloneable. Интерфейс Cloneable не содержит методов относится к маркерным интерфейсам, а его реализация гарантирует, что метод clone() класса Object возвратит точную копию вызвавшего его объекта с воспроизведением значений всех его полей. В противном случае метод генерирует исключение CloneNotSupportedException. Следует отметить, что при использовании этого механизма объект создается без вызова конструктора.


public class MyClass implements Cloneable {
private int value;

// Конструкторы и методы класса

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


Это решение эффективно только в случае, если поля клонируемого объекта представляют собой значения базовых типов и их обёрток или неизменяемых (immutable) объектных типов. Если же поле клонируемого типа является изменяемым ссылочным типом, то для корректного клонирования требуется другой подход. Причина заключается в том, что при создании копии поля оригинал и копия представляют собой ссылку на один и тот же объект. В этой ситуации следует также клонировать и сам объект поля класса.

Такое клонирование возможно только в случае, если тип атрибута класса также реализует интерфейс Cloneable и переопределяет метод clone(). Так как, если это будет иначе вызов метода невозможен из-за его недоступности. Отсюда следует, что если класс имеет суперкласс, то для реализации механизма клонирования текущего класса-потомка необходимо наличие корректной реализации такого механизма в суперклассе. При этом следует отказаться от использования объявлений final для полей объектных типов по причине невозможности изменения их значений при реализации клонирования.

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

✔️ Специализированный конструктор копирования - в классе описывается конструктор, который принимает объект этого же класса и инициализирует поля создаваемого объекта значениями полей переданного.

✔️ Фабричный метод - (Factory method), который представляет собой статический метод, возвращающий экземпляр своего класса.

✔️ Механизм сериализации - сохранение и последующее восстановление объекта в/из потока байтов.

#java #clone
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍5🔥1
public class Quest {
public static void main(String[] args) {
int x = 1;
System.out.println("x+1 = " + x+1);
}
}


#java #quest
Что выведет код?
Anonymous Quiz
48%
x+1 = 2
36%
x+1 = 11
17%
ошибка компиляции
5🔥10❤‍🔥2
5😁13❤‍🔥1
ThreadLocal - это класс в Java, который позволяет создавать локальные переменные, привязанные к каждому потоку. Это полезно, когда вам нужно иметь уникальную копию переменной для каждого потока, чтобы избежать конфликтов и обеспечить безопасность в многопоточной среде.

Преимущества ThreadLocal:

1⃣ Безопасность потоков: Каждый поток имеет свою собственную копию переменной, что устраняет соревнование и конфликты доступа.

2⃣ Улучшение производительности: Использование ThreadLocal может уменьшить необходимость синхронизации при доступе к переменным в многопоточной среде.

3⃣ Удобство: ThreadLocal делает работу с потоками более удобной и чистой, позволяя каждому потоку иметь свои собственные данные.

ThreadLocal полезен в ситуациях, когда вам нужно сохранить состояние для каждого потока, и он помогает избежать сложностей, связанных с синхронизацией и разделяемыми ресурсами.
5👍9❤‍🔥1
⌨️ В чем отличие между поверхностным и глубоким клонированием?

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

✔️ Если класс имеет только члены примитивных типов, то будет создана совершенно новая копия объекта и возвращена ссылка на этот объект.

✔️ Если класс помимо членов примитивных типов содержит члены ссылочных типов, то тогда копируются ссылки на объекты этих классов. Следовательно, оба объекта будут иметь одинаковые ссылки.

Глубокое копирование дублирует абсолютно всю информацию объекта:

✔️ Нет необходимости копировать отдельно примитивные данные;

✔️ Все члены ссылочного типа в оригинальном классе должны поддерживать клонирование. Для каждого такого члена при переопределении метода clone() должен вызываться super.clone();

✔️ Если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.

#java #clone
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥1
⌨️ Byte Buddy — это мощная библиотека для Java, предназначенная для создания, модификации и управления байт-кодом на лету, без необходимости использования низкоуровневых инструментов вроде ASM. Основное применение Byte Buddy — это динамическое создание и изменение классов в рантайме, что может быть полезно для написания агентов Java, создания прокси-объектов, внедрения кода в существующие классы и тестирования.

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

Byte Buddy поддерживает широкий диапазон версий Java и интегрируется с такими библиотеками, как Spring и Hibernate.

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;

import static net.bytebuddy.matcher.ElementMatchers.named;

public class ByteBuddyExample {
public static void main(String[] args) throws Exception {
// Создаем новый класс динамически
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class) // Класс наследуется от Object
.name("com.example.HelloWorld") // Указываем имя нового класса
.method(named("toString")) // Переопределяем метод toString
.intercept(FixedValue.value("Hello from Byte Buddy!")) // Метод будет возвращать фиксированное значение
.make()
.load(ByteBuddyExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();

// Создаем экземпляр нового класса
Object instance = dynamicType.getDeclaredConstructor().newInstance();

// Вызываем переопределенный метод toString
System.out.println(instance.toString()); // Вывод: Hello from Byte Buddy!
}
}

#java #ByteBuddy
Please open Telegram to view this post
VIEW IN TELEGRAM
18🐳9🔥2
public class Quest {
public static void main(String[] args) {
String s = null;
if (s != null & s.length() > 0) {
System.out.println("YES!");
} else {
System.out.println("NO!");
}
}
}

#java #quest
👍5🔥2
Что выведет код?
Anonymous Quiz
8%
YES!
39%
NO!
53%
Ошибка NullPointerException
🎉4
⌨️ Дженерики (generics) — это механизм, который позволяет создавать классы, интерфейсы и методы, работающие с различными типами данных, при этом сохраняя строгую типизацию. Они были введены в Java начиная с версии 5 для повышения безопасности типов и повторного использования кода.

Дженерики позволяют определить параметр типа, который будет заменен конкретным типом данных при создании экземпляра класса или вызове метода. Например, List<T> может использоваться как List<String>, List<Integer> и т.д.

Благодаря дженерикам, ошибки типа (например, попытка вставить объект неправильного типа) обнаруживаются на этапе компиляции, а не во время выполнения программы.

Один универсальный класс или метод может работать с различными типами данных, что позволяет избежать дублирования кода.

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


public class Box<T> {
private T item;

public void setItem(T item) {
this.item = item;
}

public T getItem() {
return item;
}
}

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String str = stringBox.getItem();

В этом примере класс Box<T> является обобщенным и может работать с любым типом данных, который заменит T при создании объекта.

#java #generics #T
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍16🔥3❤‍🔥21
⌨️ Потокобезопасность (Thread-Safe) — это свойство объекта или кода, которое гарантирует корректное поведение при одновременном доступе нескольких потоков. Например, потокобезопасный счетчик будет правильно учитывать каждое увеличение, даже если его используют одновременно несколько потоков. Это означает, что результаты работы будут предсказуемыми и соответствующими ожиданиям, независимо от того, сколько потоков обращаются к объекту или выполняют код одновременно.

#java #ThreadSafe
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤‍🔥1