Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что выведет код?

@SpringBootApplication
@EnableAsync
public class Task130326 implements CommandLineRunner {

@Autowired
private AsyncService130326 service;

public static void main(String[] args) {
SpringApplication.run(Task130326.class, args);
}

@Override
public void run(String... args) {
service.directCall();
service.proxyCall();
}
}

@Component
class AsyncService130326 {
@Lazy
@Autowired
private AsyncService130326 self;

@Async
public void asyncMethod() {
System.out.println("asyncMethod: " + Thread.currentThread().getName());
}

public void directCall() {
System.out.println("directCall: " + Thread.currentThread().getName());
asyncMethod();
}

public void proxyCall() {
System.out.println("proxyCall: " + Thread.currentThread().getName());
self.asyncMethod();
}
}


#Tasks
👍2
Что такое Java Memory Model (JMM)? 🤓

Ответ:

Java Memory Model (JMM)
— это формальная модель, описывающая, как потоки взаимодействуют через память и какое поведение можно ожидать в многопоточной среде.

JMM определяет правила, когда изменения, сделанные одним потоком, становятся видимыми для других потоков. Она описывает отношение "happens-before", которое гарантирует видимость и упорядоченность действий. Без JMM компилятор и процессор могли бы переупорядочивать инструкции так, что многопоточный код работал бы непредсказуемо.

Ключевые слова synchronized, volatile, final обеспечивают определенные гарантии видимости в рамках JMM.


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

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

Станислав Алексеевич Кудж (род. 14 марта 1979, Москва, РСФСР, СССР) — российский учёный, специалист в области автоматизированных систем государственного управления, многопрофильных и многофункциональных информационных систем.


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

1956 — американская компания Ampex продемонстрировала первый в мире видеомагнитофон VR-1000.

1994 — релиз Linux версии 1.0.0.


#Biography #Birth_Date #Events #14марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
С 07.03 по 13.03
Предыдущий пост(с 28.02 по 06.03)

Воскресный мотивационный пост:
Не было мотивации

Запись встреч/видео:
Видео в процессе подготовки...

Обучающие статьи:
Раздел 8. Stream API и функциональный стиль

Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон
equals/hashCode в операциях distinct, groupingBy, toSet
Иммутабельность данных в потоке — не прихоть, а необходимость

Советы по Java:
[Совет по Java #013]
Тема: Аннотация @Transactional не работает на private и protected методах

[Совет по Java #014]
Тема: Инъекция через конструктор предпочтительнее инъекции в поле

[Совет по Java #015]
Тема: @Async по умолчанию не работает внутри того же класса


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

Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍2
🤣3
🤣6
История технологий сегодня — 15 марта

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

Жоре́с Ива́нович Алфёров (бел. Жарэс Іванавіч Алфёраў; 15 марта 1930, Витебск — 1 марта 2019, Санкт-Петербург) — советский и российский учёный-физик. Политический деятель. Лауреат Нобелевской премии по физике (2000 год, за разработку полупроводниковых гетероструктур и создание быстрых опто- и микроэлектронных компонентов).


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

1892 — американский изобретатель Джесс Рено (англ. Jesse Reno) запатентовал первый эскалатор.

1985 — зарегистрирован первый домен в зоне .com


#Biography #Birth_Date #Events #15марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Вы соскучились по встречам?
Anonymous Poll
56%
Да!
4%
Нет!
0%
Похер
36%
Встреча с кем?
4%
Что я тут делаю?
👍1
История технологий сегодня — 16 марта

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

Ричард Мэттью Столлман (англ. Richard Matthew Stallman; род. 16 марта 1953, Манхэттен, Нью-Йорк), также упоминаемый как rms, — основатель движения свободного программного обеспечения, проекта GNU, Фонда свободного программного обеспечения и Лиги за свободу программирования. Автор концепции «копилефта», призванной защищать идеалы движения; эту концепцию он с помощью юристов позже воплотил в лицензии GNU General Public License (GNU GPL) для ПО. Ранее также известный программист. Из авторских программ можно отметить GNU Emacs, Коллекция компиляторов GNU (GCC) и Отладчик GNU (GDB). С середины 1990-х годов Столлман стал программировать значительно меньше, посвятив себя распространению идей свободного ПО.

Э́ндрю Стюарт Таненба́ум (англ. Andrew Stuart Tanenbaum; род. 16 марта 1944, Нью-Йорк, Нью-Йорк), иногда упоминаемый под псевдонимом AST или Papá Tanenbaumголландский учёный-компьютерщик американского происхождения, почётный профессор компьютерных наук в Амстердамском свободном университете в Нидерландах. Известен как автор MINIX (свободная Unix-подобная операционная система для учебных целей) и RFID-вируса. Автор книг по информатике, которые считаются стандартами в данной области. Сам он считает свою преподавательскую деятельность наиболее важной. Является главным разработчиком пакета «Amsterdam Compiler Kit».

Фре́дерик Ра́йнес (англ. Frederick Reines; 16 марта 1918, Патерсон, штат Нью-Джерси, США — 26 августа 1998, Ориндж, штат Калифорния, США)американский физик, профессор, лауреат Нобелевской премии по физике (1995) за открытие нейтрино.


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

1936 — с конвейера Горьковского автомобильного завода сошла первая советская легковая автомашина — лимузин марки «М-1».


#Biography #Birth_Date #Events #16марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 8. Stream API и функциональный стиль в Java

Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон

(Практика): Саботаж и починка в «Библиотеке»

Создание «сломанного» класса Book

Создайте или обновите класс Book с минимальными полями:
public class Book {
private final String title;
private final String author;

public Book(String title, String author) {
this.title = Objects.requireNonNull(title, "title не может быть null");
this.author = Objects.requireNonNull(author, "author не может быть null");
}

public String getTitle() { return title; }
public String getAuthor() { return author; }

@Override
public String toString() {
return "Book[title=" + title + ", author=" + author + "]";
}
}


Частичная реализация equals (только по title)

Добавьте equals, сравнивающий только название:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return title.equals(book.title); // Только title! Автор игнорируется.
}


Намеренно не добавляйте hashCode!

Демонстрация проблемы в Stream API

Проблема 1: distinct() «слипает» разные книги

Создайте тестовый метод в Library или Main:
public void demonstrateDistinctProblem() {
List<Book> books = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский"), // Другой автор!
new Book("1984", "Оруэлл")
);

List<Book> distinct = books.stream()
.distinct()
.collect(Collectors.toList());

System.out.println("Исходный список: " + books.size() + " книг");
System.out.println("После distinct: " + distinct.size() + " книг");
System.out.println("Уникальные книги: " + distinct);
}


Ожидаемый вывод:
Исходный список: 3 книг
После distinct: 2 книг
Уникальные книги: [Book[title=Война и мир, author=Толстой], Book[title=1984, author=Оруэлл]]


Что произошло: distinct() использует hashCode и equals. Поскольку hashCode не переопределён, наследуется от Object — разные адреса памяти → разные хэши. Но equals говорит, что книги равны по названию.
На самом деле поведение непредсказуемо: distinct() внутри использует HashSet, который сначала сравнивает hashCode, потом equals. Если хэши разные (а они будут разные), элементы попадают в разные бакеты и никогда не сравниваются через equals. Толстой и Достоевский останутся оба!

Проверьте: Запустите код. Если обе «Войны и мира» остались — hashCode разные спасает. Если одна пропала — вы попали в коллизию (редко, но возможно).

Проблема 2: toMap и потеря значений

Создайте Map книга → количество на складе:
public void demonstrateToMapProblem() {
List<Book> inventory = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский")
);

Map<Book, Integer> stock = inventory.stream()
.collect(Collectors.toMap(
Function.identity(),
b -> 10, // количество на складе
(a, b) -> a + b // слияние при коллизии
));

System.out.println("Map: " + stock);

// Попытка получить значение по «эквивалентной» книге
Book lookup = new Book("Война и мир", "Толстой");
System.out.println("Поиск " + lookup + ": " + stock.get(lookup));

Book lookupWrongAuthor = new Book("Война и мир", "Гоголь");
System.out.println("Поиск " + lookupWrongAuthor + ": " + stock.get(lookupWrongAuthor));
}


Анализ:
toMap использует hashCode для выбора бакета, equals для проверки существования ключа
Без переопределённого hashCode — разные объекты, разные бакеты
Даже если equals сказал бы «равны», HashMap не найдёт элемент из-за разных хэшей
lookup и lookupWrongAuthor оба вернут null или случайное значение
👍5
Проблема 3: groupingBy ломается
public void demonstrateGroupingProblem() {
List<Book> books = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский"),
new Book("Война и мир", "Тургенев")
);

Map<Book, List<Book>> byBook = books.stream()
.collect(Collectors.groupingBy(Function.identity()));

System.out.println("Групп: " + byBook.size());
byBook.forEach((k, v) -> System.out.println(k + " -> " + v.size()));
}


Ожидаемо: 3 группы (все разные авторы) или 1 группа (если equals/hashCode считают их одинаковыми).
Фактически: Непредсказуемо из-за отсутствующего hashCode.


Исправление: корректные equals и hashCode

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

Обновите класс Book:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return title.equals(book.title) &&
author.equals(book.author); // Теперь оба поля!
}

@Override
public int hashCode() {
return Objects.hash(title, author); // Консистентно с equals
}


Контракт equals и hashCode:
Рефлексивность: x.equals(x) всегда true
Симметричность: x.equals(y) ⇔ y.equals(x)
Транзитивность: x.equals(y) и y.equals(z) → x.equals(z)
Консистентность: повторные вызовы дают тот же результат
С null: x.equals(null) всегда false
Критически: Если x.equals(y), то x.hashCode() == y.hashCode()

Проверка исправления

Повторите все демонстрации
public void demonstrateFixed() {
List<Book> books = Arrays.asList(
new Book("Война и мир", "Толстой"),
new Book("Война и мир", "Достоевский"),
new Book("1984", "Оруэлл")
);

// distinct() — теперь 3 уникальные книги
System.out.println("distinct: " + books.stream().distinct().count());

// toMap — теперь 3 записи
Map<Book, Integer> stock = books.stream()
.collect(Collectors.toMap(
Function.identity(),
b -> 10,
Integer::sum
));
System.out.println("Map size: " + stock.size());

// Поиск работает
Book lookup = new Book("Война и мир", "Толстой");
System.out.println("Найдено: " + stock.get(lookup));
}


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


#Java #для_новичков #beginner #stream_api #практика
👍4
Альтернативные стратегии equals/hashCode

Стратегия 1: Только неизменяемый идентификатор (ISBN)

Если Book имеет уникальный ISBN:
private final String isbn;  // уникальный, неизменяемый

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
return isbn.equals(((Book) o).isbn);
}

@Override
public int hashCode() {
return isbn.hashCode();
}


Плюсы: Эффективно, надёжно. Минусы: Требует уникального поля.

Стратегия 2: Все поля

Плюсы: Корректно для value objects. Минусы: Медленнее, чувствительно к изменениям.

Стратегия 3: Не переопределять (только identity)

Если Book — entity с уникальным ID в базе данных, и вы никогда не сравниваете разные объекты с одинаковым ID:
// Нет equals/hashCode — используем Object

Плюсы: Просто. Минусы: distinct(), toMap, groupingBy работают по ссылке, не по содержимому.

Практические задания

Задача 1: воспроизвести все три проблемы

В проекте «Библиотека» создайте класс BrokenBook с equals только по title и без hashCode.

Продемонстрируйте:
distinct() «проглатывает» книги или нет (зависит от хэш-коллизий)
toMap создаёт больше записей, чем ожидается
groupingBy разбивает на неожиданное количество групп
Зафиксируйте наблюдения в комментариях.

Задача 2: исправить и сравнить

Создайте FixedBook с корректной парой equals/hashCode. Повторите те же операции.

Убедитесь, что:
Толстой, Достоевский, Тургенев — три разные книги с одинаковым названием
distinct() оставляет все три
toMap создаёт три записи
Поиск по ключу находит правильное значение

Задача 3: специфический equals для бизнес-логики

Добавьте в Library метод findByTitle(String title), который находит все книги с данным названием (независимо от автора).
Реализуйте через filter с кастомным предикатом, не через equals:
public List<Book> findByTitle(String title) {
return books.stream()
.filter(b -> b.getTitle().equals(title))
.collect(Collectors.toList());
}


Объясните, почему это безопаснее, чем менять equals для всего класса.

Задача 4: кеширование hashCode (звёздочка)


Если поля title и author неизменяемы, закешируйте hashCode:
private int hashCode;  // 0 = не вычислено

@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Objects.hash(title, author);
hashCode = result;
}
return result;
}


Сравните производительность при частом использовании в HashMap для миллиона операций.

#Java #для_новичков #beginner #stream_api #практика
👍5
Что выведет код?

public class Task160326 {
public static void main(String[] args) {
String s1 = " hello ".trim();
String s2 = " hello ".strip();
String s3 = "\u2000hello\u2000".trim();
String s4 = "\u2000hello\u2000".strip();

System.out.println(s1.equals(s2));
System.out.println(s3.length());
System.out.println(s4.length());
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
27%
true 5 5
13%
false 7 5
60%
true 7 5
0%
false 5 5
👍2
Что такое Profiling и для чего он нужен? 🤓

Ответ:

Profiling
— это процесс динамического анализа работающего приложения для измерения его производительности, использования памяти и поведения потоков.

Профилировщики (например, JProfiler, VisualVM, YourKit) собирают данные о:
CPU Profiling — какие методы потребляют больше всего процессорного времени;
Memory Profiling — какие объекты занимают память, поиск утечек памяти;
Thread Profiling — состояние потоков, поиск deadlock'ов.

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


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

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

Влади́мир Па́влович Барми́н (4 (17) марта 1909 года, Москва — 17 июля 1993 года, там же) советский учёный, конструктор реактивных пусковых установок, ракетно-космических и боевых стартовых комплексов. Один из основоположников советской космонавтики. С 1947 года под руководством Бармина были разработаны стартовые комплексы для многих ракет конструкции Королёва: Р-1, Р-2, Р-11, Р-5, Р-5М — первой стратегической ракеты с ядерным боезарядом Р-5М. В 1957 году завершены работы над стартовым комплексом первой в мире межконтинентальной баллистической ракеты Р-7, которая вывела на орбиту Земли первый искусственный спутник Земли и первого космонавта Юрия Гагарина.

Готтлиб Вильгельм Даймлер (нем. Gottlieb Wilhelm Daimler); собственно Доймлер (Däumler; 17 марта 1834, Шорндорф, Королевство Вюртемберг, Германский союз — 6 марта 1900, Бад-Канштатт, Королевство Вюртемберг, Германская империя) — немецкий инженер, конструктор и промышленник. Совместно с Вильгельмом Майбахом Даймлер разработал один из первых автомобилей и несколько типов бензиновых двигателей внутреннего сгорания.


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

1966 — первый пуск с космодрома Плесецк: ракетой-носителем «Восток-2» на орбиту Земли выведен искусственный спутник «Космос-112».


#Biography #Birth_Date #Events #17марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #016]

Тема: Ленивая загрузка в Hibernate (FetchType.LAZY) не работает после закрытия сессии.

Проблема: Hibernate использует шаблон ленивой загрузки для оптимизации производительности — связанные сущности подгружаются только при первом обращении к ним.

Однако для этого необходима открытая сессия (persistence context). Когда сессия закрывается (например, после завершения транзакции), доступ к неинициализированным прокси-объектам вызывает исключение LazyInitializationException.

Это классическая проблема при передаче сущностей на уровень представления (view) или при работе с ними вне транзакционного контекста.

Решение: Существует три основных подхода.

Первый — продлить транзакцию на весь сервисный метод, чтобы сессия оставалась открытой до полной обработки данных.
Второй — инициализировать все необходимые связи явно через JOIN FETCH в HQL/JPQL или через EntityGraph.
Третий — использовать паттерн Open Session in View (OSIV), который держит сессию открытой до завершения HTTP-запроса, но это антипаттерн для production из-за проблем с производительностью и риска утечек соединений.

@Entity
public class Order {
@Id private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private User user; // ленивая загрузка

@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<Item> items;
// геттеры/сеттеры
}

@Service
public class OrderService {

@PersistenceContext
private EntityManager em;

//Антипаттерн: сессия закрыта при доступе к ленивым полям
public Order badFind(Long id) {
Order order = em.find(Order.class, id);
// Транзакция закончилась, сессия закрыта
order.getUser().getName(); // LazyInitializationException!
return order;
}

//Решение 1: @Transactional на уровне сервиса
@Transactional
public Order goodFindTransactional(Long id) {
Order order = em.find(Order.class, id);
order.getUser().getName(); // Ок, сессия открыта
return order;
}

//Решение 2: JOIN FETCH в запросе
@Transactional(readOnly = true)
public Order findWithJoinFetch(Long id) {
return em.createQuery(
"SELECT o FROM Order o JOIN FETCH o.user WHERE o.id = :id", Order.class)
.setParameter("id", id)
.getSingleResult(); // user уже загружен
}

//Решение 3: EntityGraph
@Transactional(readOnly = true)
public Order findWithEntityGraph(Long id) {
EntityGraph<?> graph = em.getEntityGraph("Order.withUserAndItems");
return em.find(Order.class, id,
Collections.singletonMap("javax.persistence.fetchgraph", graph));
}
}

// DTO-подход — лучшая практика
@Data
class OrderDTO {
private Long id;
private String userName; // только нужные поля, без прокси
}

@Service
class OrderDTOService {
@PersistenceContext
private EntityManager em;

public OrderDTO findDTO(Long id) {
return em.createQuery(
"SELECT NEW com.example.OrderDTO(o.id, u.name) " +
"FROM Order o JOIN o.user u WHERE o.id = :id", OrderDTO.class)
.setParameter("id", id)
.getSingleResult(); // Никаких прокси, только данные
}
}


Объяснение: Корень проблемы — отделение уровня персистентности от уровня представления.

Самое надежное решение — не передавать сущности наружу, а преобразовывать их в DTO (Data Transfer Objects) внутри транзакции.

Если все же нужны сущности, то либо продлевайте транзакцию (@Transactional на весь метод контроллера — плохо), либо инициализируйте все необходимое до закрытия сессии. OSIV (spring.jpa.open-in-view=true) включен по умолчанию в Spring Boot — это удобно для разработки, но в production может держать соединения с БД долго и маскировать проблемы с производительностью.


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

import java.util.*;

public class Task170326 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add(null);
list1.add("text");
list1.remove(1);

List<String> list2 = new ArrayList<>();
list2.add("text");
list2.remove(0);

System.out.println(list1.isEmpty());
System.out.println(list2.isEmpty());
System.out.println(list1.size() == 0);
System.out.println(list2.size() == 0);
}
}


#Tasks
👍2