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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Опубликована новая статья!

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

Напоминаю, ребят, вы можете оставлять рецензии и комментарии под ними! Не стесняйтесь))

Кроме того никто не запрещает вам публиковать свои статьи)) Дерзайте (в рамках разумного конечно😂)
👍4
История технологии сегодня — 30 июля

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

Клайв Марльз Синклер (англ. Clive Marles Sinclair; 30 июля 1940 — 16 сентября 2021) — британский предприниматель, изобретатель и бывший владелец компании, которая выпустила микрокомпьютер ZX Spectrum в 1982 году.

Ге́нри Форд (англ. Henry Ford; 30 июля 1863 — 7 апреля 1947) — американский промышленник, владелец заводов по производству автомобилей по всему миру, изобретатель, рационализатор, организатор производства, автор 161 патента США. Его лозунг — «автомобиль для всех»; завод Форда выпускал наиболее дешёвые автомобили в начале эпохи автомобилестроения. Компания «Ford Motor Company» существует по сей день.


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

1970 — компания IBM анонсировала новую серию мейнфреймов System/370.


#Biography #Birth_Date #Events #30июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
[Совет по Java #066]

Тема: Arrays.copyOf создает новый массив с новым размером, не меняет исходный.

Проблема: Многие разработчики воспринимают Arrays.copyOf как метод, модифицирующий исходный массив, особенно при увеличении или уменьшении его длины.

Однако метод всегда создает новый массив заданной длины, копируя в него элементы из исходного. Исходный массив остается неизменным.

При этом, если новый размер больше исходного, недостающие элементы заполняются значениями по умолчанию (0 для примитивов, null для объектов). Если размер меньше, копируются только первые элементы, остальные отбрасываются. Эта семантика часто упускается из виду, что приводит к неожиданным последствиям: ссылки на исходный массив остаются старыми, а изменения в новом массиве не отражаются на старом. Также важно помнить, что для массивов объектов копируются ссылки, а не сами объекты (поверхностное копирование), поэтому изменение объектов внутри скопированного массива затронет и исходный, если они ссылаются на одни и те же экземпляры.

Решение: Используйте Arrays.copyOf для создания новой копии массива с измененной длиной.

Результат всегда присваивайте новой переменной. Если вы хотите расширить существующий массив и продолжить использовать его в том же контексте, необходимо явно переприсвоить переменную: arr = Arrays.copyOf(arr, newLength). При работе с объектами учитывайте, что мутабельность элементов влияет на обе копии. Для глубокого копирования объектов потребуется дополнительная логика. Для простого копирования всей длины или части массива можно использовать Arrays.copyOf или Arrays.copyOfRange.

Также стоит помнить, что Arrays.copyOf внутри использует System.arraycopy, поэтому он эффективен.
import java.util.Arrays;

public class ArraysCopyOfExample {

public static void main(String[] args) {
int[] original = {1, 2, 3};

//Копирование с увеличением длины
int[] expanded = Arrays.copyOf(original, 5);
System.out.println("Expanded: " + Arrays.toString(expanded)); // [1, 2, 3, 0, 0]
System.out.println("Original unchanged: " + Arrays.toString(original)); // [1, 2, 3]

//Копирование с уменьшением длины
int[] truncated = Arrays.copyOf(original, 2);
System.out.println("Truncated: " + Arrays.toString(truncated)); // [1, 2]

//Для объектов — копируются ссылки
class Person { String name; Person(String n) { name = n; } }
Person[] people = {new Person("Alice"), new Person("Bob")};
Person[] copy = Arrays.copyOf(people, 3);
copy[0].name = "Charlie"; // изменяем объект через копию
System.out.println("Original[0].name: " + people[0].name); // Charlie — изменилось!
// Это поверхностное копирование, объекты общие

//Присвоение результата обратно для замены массива
int[] numbers = {10, 20, 30};
numbers = Arrays.copyOf(numbers, numbers.length + 1);
numbers[3] = 40;
System.out.println("After reassign: " + Arrays.toString(numbers)); // [10, 20, 30, 40]
}
}


Объяснение:
 Arrays.copyOf — это удобная обертка над System.arraycopy, которая выделяет новый массив нужной длины и заполняет его.

Исходный массив не затрагивается, так как метод получает его копию ссылки и не изменяет объект массива. При увеличении размера заполнение значениями по умолчанию гарантирует, что массив не содержит мусора. Для примитивов это безопасно, для объектов — добавляются null.

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


#Java #советы
👍4
Что выведет данный код?

import java.util.Arrays;

public class Task300726 {
static class Item300726 {
int value;
Item300726(int v) { value = v; }
}

public static void main(String[] args) {
Item300726[] original = {new Item300726(1), new Item300726(2)};
Item300726[] copy = Arrays.copyOf(original, 2);
copy[0].value = 10;
copy[1] = new Item300726(20);
System.out.println(original[0].value);
System.out.println(original[1].value);
}
}


#Tasks
👍3
17.Идемпотентность в микросервисах: все способы защиты от повторной обработки

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

Что вы узнаете:
- Что такое идемпотентность и почему она бывает технической и бизнес.
- Как в Kafka и Kafka Connect рождаются дубли (и почему идемпотентный продюсер здесь не спасает).
- Почему нельзя просто отключить ретраи – теоретический предел at‑least‑once.
- Четыре способа защиты от дублей в реальном коде:
- Уникальность в БД – финальная гарантия для платежей.
- Redis-блокировка – быстрый фильтр перед БД.
- Inbox Pattern – транзакционная дедупликация для Kafka-консьюмеров.
- Idempotency‑Key для REST API – защита на входе в систему.
- Сравнительный анализ и критерии выбора подхода для вашего сценария.
- Почему после всех защит неизбежна Eventual Consistency и как с ней жить.

Исходный код проекта на GitHub наверняка заслуживает Ваших звезд! 🙂

Ссылка на Youtube
Ссылка на Рутьюб

Смотрите, ставьте лайки, подписывайтесь на каналы!✌️

Жду ваших реакций и оценок
🙂
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍3
Как работает String.lines() и для чего нужен? 🤓

Ответ:

String.lines() (Java 11)
разбивает строку на строки по разделителям \n, \r, \r\n и возвращает Stream<String>.

Это удобно для обработки многострочных текстов, логов, CSV-файлов построчно без BufferedReader.

Пример: 

"a\nb\nc".lines()
.forEach(System.out::println).

В отличие от split("\n"), lines() обрабатывает все стандартные разделители и не создает промежуточный массив, экономя память при больших текстах.



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

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

Габриэ́ль Кра́мер (нем. Gabriel Cramer, 31 июля 1704, Женева, Швейцария — 4 января 1752, Баньоль-сюр-Сез, Франция) — швейцарский математик, ученик и друг Иоганна Бернулли, один из создателей линейной алгебры.


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

1999 — программа Discovery — Lunar Prospector : НАСА производит управляемое крушение космического зонда на Луну, тем самым заканчивая свою миссию по обнаружению замёрзшей воды на поверхности Луны.


#Biography #Birth_Date #Events #31июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Всем привет!

По мотивам вступления вчерашнего видео, написал сегодня статью по маппингу типов передаваемых объектов в Kafka.

Можете ее оценить на devforge.ru и на Хабре.

Кстати - попрошу - оцените где удобнее читать и почему (но только не про мобилку я это знаю и работаю над этим)

❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

Глава 3. Сериализация и форматы обмена

Нативная сериализация Java: интерфейс Serializable

Интерфейс java.io.Serializable — это центральный элемент механизма нативной сериализации Java, существующий с версии JDK 1.1.
Он не требует реализации методов, не содержит констант и не накладывает контрактов на поведение класса. Его единственное назначение — сигнализировать JVM и стандартной библиотеке, что экземпляры данного класса разрешено преобразовывать в поток байтов и восстанавливать из него.
Этот механизм глубоко интегрирован в JVM: он обходит граф объектов, записывает метаданные классов, управляет версионностью и обеспечивает восстановление ссылочной целостности. Однако за простотой использования скрывается сложная внутренняя архитектура, множество подводных камней и серьёзные ограничения, которые делают нативную сериализацию устаревшим выбором для новых систем.


Маркерный интерфейс Serializable

Serializable — это маркерный интерфейс (marker interface). В отличие от обычных интерфейсов, он не объявляет ни одного метода. Его роль чисто декларативная: наличие implements Serializable в объявлении класса изменяет поведение JVM при обработке этого класса механизмом сериализации.
import java.io.Serializable;

public class Document implements Serializable {
// Маркерный интерфейс не требует методов
private String title;
private String content;
}


Маркерный интерфейс
 — это интерфейс в Java без методов и полей, служащий метаданным для компилятора или runtime. CloneableSerializableRandomAccess — примеры таких интерфейсов. Они не влияют на структуру класса напрямую, но изменяют поведение встроенных механизмов JVM. В современной Java роль маркерных интерфейсов частично вытесняется аннотациями (например, @Entity в JPA), но Serializable остаётся неизменным с 1997 года по причинам обратной совместимости.
Аннотация (annotation) — это форма метаданных в Java, введённая в Java 5, которая предоставляет данные о программе, не являющиеся частью самой программы. Аннотации не влияют непосредственно на операции кода, но могут обрабатываться компилятором, инструментами или во время выполнения через рефлексию.


При попытке сериализовать объект, класс которого не реализует SerializableObjectOutputStream.writeObject() выбросит NotSerializableException. Это проверка выполняется рекурсивно: если класс реализует Serializable, но одно из его полей — объект класса без Serializable, исключение возникнет при попытке записать это поле.
public class Container implements Serializable {
private Document doc; // Document должен быть Serializable
private Metadata meta; // Metadata тоже должен быть Serializable
}

Если Metadata не реализует Serializable, сериализация Container завершится NotSerializableException на этапе обхода поля meta. Это ограничение каскадируется по всему графу объектов: каждый объект, достижимый от корневого, должен быть сериализуемым, либо должен быть помечен как transient.


#Java #для_новичков #beginner #IO #NIO #Serialize #Serializable
👍4
Что сериализуется и что нет

Сериализуемое состояние
Механизм нативной сериализации записывает состояние экземпляра объекта — значения всех полей экземпляра, за исключением тех, что явно исключены. Это включает:
Поля всех уровней доступности: publicprotectedpackage-private и private. Сериализация обходит модификаторы доступа через механизм reflection или нативный доступ JNI, поэтому приватные поля сохраняются и восстанавливаются без геттеров и сеттеров.

Поля примитивных типов: их значения записываются в бинарном виде.

Поля ссылочных типов: объекты, на которые ссылаются поля, сериализуются рекурсивно. Если поле ссылается на другой объект, механизм обходит ссылку и сериализует целевой объект, затем возвращается к текущему.
public class Library implements Serializable {
private String name;
private List<Book> books; // Book тоже должен быть Serializable
private Librarian head; // Librarian тоже должен быть Serializable
}


При сериализации Library механизм рекурсивно обойдёт список books: каждый элемент Book будет сериализован со своими полями, включая ссылки на AuthorPublisher и т.д. Затем будет сериализован head (Librarian) со всеми его полями. Результат — глубокая копия всего графа объектов в виде потока байтов.
Глубокая копия (deep copy) — это копия объекта, при которой копируются не только сам объект, но и все объекты, на которые он ссылается, рекурсивно. В отличие от поверхностной копии (shallow copy), где копируются только ссылки на те же внутренние объекты.



Что не сериализуется


Статические поля не сериализуются. Они принадлежат классу, а не экземпляру, и их значения хранятся в Metaspace (в структурах класса), а не в куче вместе с объектом. При десериализации статические поля получают текущие значения, определённые в загруженном классе, а не значения из потока.

Это логично: если бы статические поля сериализовались, восстановление объекта на другой JVM с другим состоянием класса привело бы к конфликтам.
public class Config implements Serializable {
private static String globalPrefix = "app"; // не сериализуется
private String instanceName; // сериализуется
}


Transient-поля
 явно исключаются из сериализации. Ключевое слово transient указывает механизму, что поле не должно быть записано в поток. При десериализации такое поле получает значение по умолчанию: null для ссылочных типов, 0 для чисел, false для boolean.
public class UserSession implements Serializable {
private String username;
private transient String password; // не сериализуется
private transient Connection dbConn; // не сериализуется
private transient byte[] cachedToken; // не сериализуется
}

transient используется для чувствительных данных (пароли, ключи), ресурсов, привязанных к runtime (соединения с БД, потоки, сокеты), и вычисляемых/кэшированных значений, которые можно восстановить после загрузки.
Важно: transient не влияет на видимость поля — оно остаётся приватным или публичным, просто исключается из сериализационного протокола.

Поля-суперклассы
Если класс реализует Serializable, а его суперкласс — нет, то поля суперкласса не сериализуются. При десериализации суперкласс инициализируется через свой no-arg конструктор (конструктор без аргументов). Если такого конструктора нет — InvalidClassException.
public class Base {  // не Serializable
private int baseField;
public Base() {} // обязателен no-arg конструктор
}

public class Derived extends Base implements Serializable {
private int derivedField; // только это поле сериализуется
}

При десериализации Derived поле baseField не восстанавливается из потока. Вместо этого вызывается Base(), и baseField получает значение по умолчанию (0), независимо от того, каким оно было при сериализации. Это частый источник багов: разработчик забывает, что суперкласс не serializable, и удивляется, почему поля предка обнуляются.
No-arg конструктор (no-argument constructor, default constructor) — это конструктор без параметров. В Java, если класс не объявляет явных конструкторов, компилятор генерирует no-arg конструктор автоматически. Если объявлен хотя бы один конструктор с параметрами, no-arg конструктор нужно объявлять явно.


Механизм обхода графа объектов


ObjectOutputStream не просто записывает байты полей. Он обходит весь граф объектов, начиная с корневого, используя алгоритм, похожий на обход в ширину (BFS) или обход в глубину (DFS) с отслеживанием посещённых узлов.

Отслеживание ссылок и циклы

Ключевая особенность: каждый объект в графе записывается ровно один раз. Для этого ObjectOutputStream поддерживает внутреннюю таблицу handles — отображение от сериализованного объекта к целочисленному идентификатору (int). При первой встрече объекта:
Объект записывается полностью (дескриптор класса + поля).
Объекту присваивается handle — следующее свободное целое число.
Handle сохраняется в таблице.
При повторной встрече того же объекта (через другую ссылку или в цикле):
В поток записывается маркер backreference (TC_REFERENCE) + handle ранее записанного объекта.
Полная сериализация не выполняется.
public class Node implements Serializable {
private String name;
private Node next; // может образовать цикл
}

// Создаём цикл: A -> B -> C -> A
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
a.next = b;
b.next = c;
c.next = a; // цикл!

// Сериализация не зацикливается благодаря таблице handles
oos.writeObject(a); // записывает A, B, C, затем backreference на A

Этот механизм предотвращает бесконечную рекурсию при циклических ссылках и гарантирует, что после десериализации топология графа сохранится: c.next будет ссылаться на тот же объект a, что и корневой указатель. Без handles граф с циклами привёл бы к StackOverflowError при сериализации.
Handle (в контексте сериализации Java) — это целочисленный идентификатор, присваиваемый каждому уникальному объекту в потоке сериализации. Handles начинаются с базового значения (обычно baseWireHandle = 0x7E0000) и инкрементируются. Они используются для кодирования backreferences.


#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
serialVersionUID: идентификатор версии класса

Что такое serialVersionUID

serialVersionUID — это статическое поле типа long, которое выступает уникальным идентификатором версии класса для механизма сериализации. При сериализации JVM записывает serialVersionUID класса в поток.
При десериализации она сравнивает serialVersionUID из потока с serialVersionUID загруженного класса. Если значения не совпадают — InvalidClassException.
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
}

Этот механизм защищает от десериализации объектов, сериализованных несовместимой версией класса. Например, если класс Document v1 имел поля title и author, а в v2 поле author было удалено и добавлено content, десериализация v1-объекта в v2-класс без контроля версий привела бы к повреждению данных или ClassCastException.


Генерация по умолчанию

Если класс не объявляет serialVersionUID явно, JVM вычисляет его автоматически на основе структуры класса. Алгоритм генерации (документирован в спецификации Java Object Serialization) учитывает:
Имя класса.
Модификаторы класса (publicfinalabstractinterface).
Имена интерфейсов, которые реализует класс.
Имена полей, их модификаторы, типы.
Имена методов, их модификаторы, сигнатуры (возвращаемый тип, параметры).
Имена конструкторов, их сигнатуры.
Имена и сигнатуры методов writeObjectreadObjectwriteReplacereadResolve.
Имена статических полей (кроме serialVersionUID itself).
Этот хеш вычисляется через алгоритм, похожий на Secure Hash Algorithm (SHA), но упрощённый. Результат — 64-битное целое число long.
SHA (Secure Hash Algorithm) — семейство криптографических хеш-функций, которые преобразуют входные данные произвольной длины в строку фиксированной длины (дайджест). В контексте serialVersionUID используется не криптографический SHA, а похожий алгоритм хеширования структурных элементов класса.


Проблема автоматической генерации

Автоматическая генерация serialVersionUID кажущеися удобной, но на практике является источником хрупкости. Любое изменение класса — даже добавление приватного метода, изменение порядка полей, добавление комментария (нет, комментарии не влияют, но форматирование может влиять на порядок элементов в некоторых компиляторах) — может изменить вычисленный serialVersionUID.
// Версия 1
public class User implements Serializable {
private String name;
private int age;
}

// Версия 2 — добавлен метод, serialVersionUID изменился!
public class User implements Serializable {
private String name;
private int age;

public void greet() { // новый метод изменяет вычисленный UID
System.out.println("Hello");
}
}

Если объект User был сериализован версией 1, а десериализуется версией 2, JVM вычислит новый serialVersionUID для загруженного класса, сравнит с UID из потока, обнаружит несоответствие и выбросит:
java.io.InvalidClassException: User; local class incompatible:
stream classdesc serialVersionUID = -123456789,
local class serialVersionUID = 987654321

Это происходит даже если изменение семантически совместимо: добавление метода не влияет на состояние объекта, но меняет вычисленный хеш.

Решение: явное объявление
Рекомендуется всегда объявлять serialVersionUID явно:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
}



При явном объявлении JVM не вычисляет UID динамически, а использует указанное значение. Теперь добавление методов, изменение порядка полей, рефакторинг внутренней структуры не влияет на совместимость сериализации — до тех пор, пока изменения совместимы с форматом потока.
Как выбрать начальное значение? Традиционно используется 1L. Если позже потребуется несовместимое изменение, значение увеличивают: 2L3L и т.д. Некоторые команды используют timestamp или хеш от начальной структуры класса, но на практике последовательные номера проще в управлении.


Когда менять serialVersionUID

serialVersionUID следует изменять (инкрементировать) при несовместимых изменениях структуры класса.

Несовместимые изменения включают:
Удаление полей.
Изменение типа поля (например, int → String).
Изменение модификаторов класса с Serializable на несериализуемый.
Перемещение класса в другой пакет (имя класса входит в поток).
Изменение порядка полей суперкласса (влияет на порядок в потоке).
Изменение класса с enum на обычный или наоборот.


Совместимые изменения

При явном serialVersionUID можно вносить изменения, не ломающие десериализацию старых объектов:
Добавление полей: новые поля в потоке отсутствуют, поэтому при десериализации они получают значения по умолчанию (null0false). JVM использует ObjectInputStream.defaultReadObject(), который читает из потока только те поля, которые есть в текущем классе, игнорируя лишние данные в потоке.
Добавление классов в иерархию: если добавить новый суперкласс, который сам реализует Serializable, старые объекты десериализуются корректно — новые поля суперкласса получат значения по умолчанию.
Удаление классов из иерархии: если удалить сериализуемый суперкласс, поля этого суперкласса в потоке игнорируются.
Добавление/удаление методов writeObject/readObject: не влияет на serialVersionUID при явном объявлении.
Изменение модификатора доступа поля: не влияет, так как сериализация обходит модификаторы.
Пометка поля как transient: поле перестаёт читаться из потока, получает значение по умолчанию.
// Версия 1
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
}

// Версия 2 — совместимое изменение: добавлено поле
public class User implements Serializable {
private static final long serialVersionUID = 1L; // тот же UID!
private String name;
private String email; // новое поле, старые объекты получат null
}

При десериализации объекта версии 1 в класс версии 2 поле email получит null. Это безопасно, но требует, чтобы код класса корректно обрабатывал null (например, через инициализацию в readObject() или ленивую инициализацию в геттере).

Обратная совместимость: новый объект в старый класс
Если объект сериализован версией 2 (с полем email), а десериализуется классом версии 1 (без email), JVM прочитает из потока поле email, но не найдёт соответствующего поля в классе. В этом случае данные поля сохраняются во внутренней структуре ObjectInputStream как no-data или skipped fields, но не восстанавливаются. Это не вызывает исключения, но данные теряются.


#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
Расширенный контроль: writeObject, readObject, readResolve

writeObject и readObject
Класс может переопределить поведение сериализации, объявив приватные методы:
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

JVM вызывает эти методы через рефлексию, если они объявлены. Метод writeObject вызывается после записи дескриптора класса, но перед записью полей. Внутри него можно выполнить out.defaultWriteObject() (записать стандартные поля) и затем дописать произвольные данные.
public class SecureDocument implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
private transient String secretContent; // не сериализуем напрямую

private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // записываем title и другие нетранзиентные поля
// Шифруем секретный контент перед записью
String encrypted = encrypt(secretContent);
out.writeUTF(encrypted);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // читаем title
// Дешифруем секретный контент
String encrypted = in.readUTF();
this.secretContent = decrypt(encrypted);
}

private String encrypt(String data) { /* ... */ return data; }
private String decrypt(String data) { /* ... */ return data; }
}

Этот паттерн позволяет сериализовать transient-поля в зашифрованном виде, выполнять валидацию инвариантов, восстанавливать производные структуры.

readResolve
Метод readResolve вызывается после полной десериализации объекта и позволяет заменить десериализованный экземпляр на другой объект. Это критично для реализации singleton и enum-like паттернов.
Singleton — это порождающий паттерн проектирования, гарантирующий, что класс имеет только один экземпляр, и предоставляющий глобальную точку доступа к нему. При десериализации singleton механизм создаёт новый объект, нарушая гарантию единственности. readResolve возвращает существующий экземпляр.

public class Singleton implements Serializable {
private static final long serialVersionUID = 1L;
private static final Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton getInstance() {
return INSTANCE;
}

// При десериализации возвращаем существующий INSTANCE, а не новый объект
private Object readResolve() {
return INSTANCE;
}
}

Без readResolve десериализация Singleton создала бы новый объект в куче, и getInstance() вернул бы старый INSTANCE, в то время как десериализованный объект — отдельная сущность. readResolve гарантирует, что любая десериализация вернёт канонический экземпляр.

writeReplace
Аналогично, writeReplace вызывается перед сериализацией и позволяет заменить объект на другой перед записью в поток. Используется для прокси-сериализации (serialization proxy pattern), когда внутреннее представление объекта не должно попадать в поток.

Прокси-сериализация (serialization proxy pattern) — это паттерн, при котором вместо самого объекта сериализуется лёгкий прокси-объект (proxy), содержащий только необходимые данные, а при десериализации прокси через readResolve восстанавливает оригинальный объект через его конструктор или фабрику. Это защищает инварианты, предотвращает создание "сырых" объектов и позволяет менять внутреннюю структуру класса, не ломая совместимость.

public class ComplexObject implements Serializable {
private static final long serialVersionUID = 1L;
private final int x;
private final int y;
// инвариант: x >= 0 && y >= 0

public ComplexObject(int x, int y) {
if (x < 0 || y < 0) throw new IllegalArgumentException();
this.x = x;
this.y = y;
}

// Вместо себя сериализуем прокси
private Object writeReplace() {
return new SerializationProxy(x, y);
}

// Запрещаем десериализацию ComplexObject напрямую
private void readObject(ObjectInputStream in) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}

private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 1L;
private final int x;
private final int y;

SerializationProxy(int x, int y) {
this.x = x;
this.y = y;
}

// При десериализации прокси восстанавливаем ComplexObject через конструктор
private Object readResolve() {
return new ComplexObject(x, y); // инвариант проверяется!
}
}
}

Этот паттерн рекомендуется Джошуа Блохом (Joshua Bloch) в "Effective Java" как предпочтительный способ сериализации сложных объектов


#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
Что выведет код?

import java.io.*;

public class Task310726 {
static class DataV1 implements Serializable {
private static final long serialVersionUID = 1L;
String name = "John";
}

static class DataV2 implements Serializable {
private static final long serialVersionUID = 1L;
String fullName;
}

public static void main(String[] args) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(new DataV1());
}

byte[] bytes = baos.toByteArray();
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
DataV2 obj = (DataV2) ois.readObject();
System.out.println(obj.fullName);
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
14%
John
29%
null
14%
InvalidClassException
43%
ClassCastException
👍2
Что такое Collectors.toMap() и как разрешать конфликты ключей? 🤓

Ответ:

Collectors.toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)
 собирает стрим в Map.

Если возникают дублирующиеся ключи, нужно передать mergeFunction для разрешения конфликта.

Пример: 
list.stream().collect(Collectors.toMap(Item::getId, Function.identity(), (oldVal, newVal) -> newVal)) — при конфликте берется новое значение.

Также можно указать Supplier для конкретной реализации Map. Без mergeFunction конфликт вызовет IllegalStateException.



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

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

Дуглас Дин Оше́ров (англ. Douglas Dean Osheroff; род. 1 августа 1945, Абердин, Вашингтон, США) — американский физик, лауреат Нобелевской премии по физике в 1996 г. (совместно с Дэвидом Ли и Робертом Ричардсоном) «за открытие сверхтекучести гелия-3». Открытие было сделано в 1971 году, когда Ошеров был аспирантом в Корнеллском университете.

Жорж Шарпа́к (фр. Georges Charpak, в семье и среди знакомых — Гриша Шарпак; 1 августа 1924 — 29 сентября 2010) — французский физик, лауреат Нобелевской премии по физике в 1992 году «за открытие и создание детекторов частиц, в частности многопроволочной пропорциональной камеры».


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

1774 — английским учёным Джозефом Пристли открыт химический элемент кислород.


#Biography #Birth_Date #Events #01августа
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Всем привет! 😉

Опубликовал еще пару статей на devforge.ru

Пагинация, которую начинаешь ненавидеть 😵

Как я память искал


Поправил мобильное отображение статей. Должно быть комфортно читать)))

Проверяйте))

Если не сложно напишите хоть одно слово о том как вам? ☺️
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Я, который решил добавить в прод либу о которой много пишут в интернете🤣
🤣5🔥1
История технологии сегодня — 02 августа

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

Элайша Грей (другой вариант имени в русских источниках: Элиша, англ. Elisha Gray; 2 августа 1835, Барнсвилл, Огайо — 21 января 1901, Ньютонвилл, Мидлсекс) — американский инженер-электрик, соучредитель компании Western Electric Manufacturing Company. Элайша Грей также считается отцом современного музыкального синтезатора, и получил более 70 патентов на свои изобретения. Он был одним из основателей Graybar, приобретя контрольный пакет акций компании вскоре после её создания.

Сэ́мюэл Ду́нкан Блэк (англ. Samuel Duncan Black, более известный как С. Дункан Блэк (англ. S. Duncan Black); 1883—1951) — американский предприниматель и изобретатель, вместе с Алонзо Дж. Деккером основавший компанию по производству инструментов Black & Decker. Известность они приобрели благодаря совместной разработке пистолетной рукоятки и спускового переключателя для переносных электрических дрелей, а также универсального двигателя для переносных электроинструментов.


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

1932 — Карл Андерсон сделал снимок со следом античастицы позитрон.

2020 — космический корабль Dragon 2 компании SpaceX впервые совершил посадку после космического полёта с астронавтами в миссии SpaceX DM-2.


#Biography #Birth_Date #Events #02августа
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ребят, напоминаю, набираю желающих обучиться Java!

Карьерным консультантом не работаю, но до уровня крепкого мидла с Spring доведу.

Что изучаем: Spring Boot, Spring Security, Spring Data, OAuth2, FeignClient, Specification, Pageable, WebClient, PostgreSQL, Redis, Hibernate, REST API, Caffeine, JUnit, Mockito, WireMock, Zonky, Kafka, Apache Common и так далее. Но тут не все))

Кроме того могу провести мок-интервью или проверить ваш пет-проект. По итогу дам развернутый фидбек.

Пишите, спрашивайте, за спрос денег не берут)

@Oleborn
👍6