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
Варианты ответа:
Anonymous Quiz
23%
A
0%
null
54%
ArrayStoreException
23%
ClassCastException
👍4
Что такое Map.ofEntries() и Map.entry()? 🤓

Ответ:

Для создания неизменяемых карт с более чем 10 элементами в Java 9+ используется Map.ofEntries() с Map.entry(). 

Map.of() имеет перегрузки только до 10 пар. Map.ofEntries() принимает массив Map.Entry и подходит для больших карт.

Пример: Map.ofEntries( entry("a", 1), entry("b", 2), ... ).

Также можно использовать Map.copyOf() для создания неизменяемой копии существующей карты.


Все эти методы создают неизменяемые, сериализуемые структуры.



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

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

Михаи́л Кла́вдиевич Тихонра́вов (16 [29] июля 1900, Владимир, Российская империя — 4 марта 1974, Москва, СССР) — советский инженер и конструктор космической и ракетной техники, был сподвижником С. П. Королёва: возглавлял работы по созданию баллистических ракет на жидком топливе, разработал концепцию пакетной схемы (она легла в основу ракеты Р-7), участвовал в проектировании первых искусственных спутников Земли и пилотируемых кораблей.

Влади́мир Козьми́ч Зворы́кин (17 [29] июля 1888 или 30 июля 1889, Муром, Владимирская губерния, Российская империя — 29 июля 1982, Принстон, Нью-Джерси) — российский и американский инженер и изобретатель в области телевизионной техники. Зворыкину принадлежат более 120 американских патентов на различные изобретения. Он получил большое число различных наград. В частности, в 1967 году президент США Линдон Джонсон вручил ему Национальную научную медаль США за научные заслуги за 1966 год. В 1977 году избран в Национальный зал славы изобретателей.

Исидо́р А́йзек Ра́би (англ. Isidor Isaac Rabi; 29 июля 1898, Рыманов, Австро-Венгрия — 11 января 1988, Нью-Йорк, США) — американский физик-ядерщик, лауреат Нобелевской премии по физике в 1944 году «за резонансный метод измерений магнитных свойств атомных ядер», лауреат Премии «За мирный атом» 1967 года.


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

1996 — Microsoft представила операционную систему Windows NT 4.0.

2015 — компания Microsoft представила свою новую операционную систему Windows 10.
ц

#Biography #Birth_Date #Events #29июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

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

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

В Java под сериализацией обычно понимают механизм Java Native Serialization — встроенный протокол, который превращает граф объектов в поток байтов. Однако в современной разработке этот механизм рассматривается как один из многих, и выбор формата сериализации — это архитектурное решение, влияющее на производительность, безопасность и эволюцию системы.

Граф объектов (object graph) — это совокупность объектов, связанных между собой ссылками. При сериализации Java обходит этот граф, начиная с корневого объекта, и записывает каждый объект ровно один раз, даже если на него есть несколько ссылок. Это предотвращает бесконечные циклы и дублирование данных.


Определение: сериализация как процесс

Сериализация — это преобразование объекта из его представления в памяти JVM (структуры в куче со ссылками на другие объекты) в линейный поток байтов. Этот поток можно записать в файл, отправить по сокету, сохранить в базе данных или закэшировать в памяти.
import java.io.*;

public class NativeSerializationExample {

// Serializable — маркерный интерфейс, указывающий JVM, что класс может быть сериализован
public static class User implements Serializable {
// serialVersionUID — контрольная сумма версии класса для проверки совместимости
private static final long serialVersionUID = 1L;

private String name;
private int age;
private transient String password; // transient — поле не сериализуется

public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}

@Override
public String toString() {
return "User{name='" + name + "', age=" + age + ", password='" + password + "'}";
}
}

public byte[] serialize(User user) throws IOException {
// ByteArrayOutputStream — поток, который пишет байты в массив в памяти
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {

// writeObject обходит граф объектов и записывает его в поток
oos.writeObject(user);
oos.flush();

// toByteArray возвращает скопированный массив байтов
return baos.toByteArray();
}
}

public User deserialize(byte[] data) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais)) {

// readObject восстанавливает граф объектов из байтов
return (User) ois.readObject();
}
}
}


Маркерный интерфейс
(marker interface) — это интерфейс в Java без методов, который служит меткой для JVM или библиотек. Serializable не требует реализации методов, но его наличие сигнализирует механизму сериализации, что класс разрешено преобразовывать в байты.

Ключевое слово transient — модификатор в Java, который указывает, что поле не должно быть сериализовано. При десериализации такое поле получает значение по умолчанию (null для объектов, 0 для чисел, false для boolean). Используется для чувствительных данных (пароли, ключи) или вычисляемых/кэшированных значений, которые можно восстановить после загрузки.

serialVersionUID — это статическое поле типа long, которое выступает идентификатором версии класса. При десериализации JVM сравнивает serialVersionUID в потоке байтов с serialVersionUID загруженного класса. Если они не совпадают, выбрасывается InvalidClassException. Это механизм защиты от несовместимых изменений структуры класса.



Десериализация: обратный процесс

Десериализация — это восстановление объекта из потока байтов. JVM читает байты, определяет класс объекта, создаёт экземпляр без вызова конструктора (через механизм allocation без инициализации) и заполняет поля значениями из потока.
public class DeserializationFlow {

// При десериализации конструктор НЕ вызывается
// Вместо этого JVM использует механизм sun.misc.Unsafe.allocateInstance()
// или аналогичный внутренний API для создания "сырого" объекта

public void demonstrate(Path file) throws Exception {
try (ObjectInputStream ois = new ObjectInputStream(
new BufferedInputStream(Files.newInputStream(file)))) {

Object obj = ois.readObject();

// Если класс реализует readObject(), он вызывается после заполнения полей
// Это позволяет выполнить пост-инициализацию: восстановить transient-поля,
// проверить инварианты, зарегистрировать слушателей
}
}
}

Инвариант (invariant) — это условие, которое всегда истинно для объекта в корректном состоянии. Например, у класса DateRange инвариантом может быть start <= end. При десериализации, если поля заполняются напрямую из потока, инварианты могут быть нарушены, поэтому используется метод readObject() для валидации.

Важный нюанс: при десериализации не вызываются конструкторы класса и его суперклассов. Это означает, что логика инициализации, определённая в конструкторах, пропускается. Если класс зависит от конструкторской логики (например, создание внутренних структур), необходимо реализовать метод private void readObject(ObjectInputStream in), который JVM вызовет после заполнения полей.


#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
Сценарии использования сериализации

Сохранение состояния приложения

Приложения, работающие с долгоживущими сессиями или сложным состоянием, используют сериализацию для персистентности. Например, графический редактор сохраняет документ как сериализованный граф фигур, фильтров и настроек слоёв.
public class SessionPersistence {

public static class AppState implements Serializable {
private static final long serialVersionUID = 1L;
private List<Document> openDocuments;
private Map<String, UserPreference> preferences;
private transient Connection dbConnection; // соединение не сохраняем
}

public void saveState(AppState state, Path file) throws IOException {
// Atomic write: сначала пишем во временный файл, потом атомарно перемещаем
Path temp = Files.createTempFile(file.getParent(), "state_", ".tmp");
try (ObjectOutputStream oos = new ObjectOutputStream(
new BufferedOutputStream(Files.newOutputStream(temp)))) {
oos.writeObject(state);
}
Files.move(temp, file, StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.ATOMIC_MOVE);
}

public AppState loadState(Path file) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new BufferedInputStream(Files.newInputStream(file)))) {
AppState state = (AppState) ois.readObject();
// Восстанавливаем transient-поля
state.dbConnection = createNewConnection();
return state;
}
}

private Connection createNewConnection() {
// Логика создания соединения
return null;
}
}


Передача по сети


В распределённых системах объекты сериализуются для передачи между узлами. Это фундамент RMI (Remote Method Invocation) и многих RPC-фреймворков.

RMI (Remote Method Invocation) — это механизм Java, позволяющий вызывать методы объектов, находящихся в другой JVM (возможно, на удалённой машине), так, будто они являются локальными. Параметры и возвращаемые значения методов автоматически сериализуются и передаются по сети.

// Интерфейс удалённого сервиса
public interface OrderService extends Remote {
Order createOrder(OrderRequest request) throws RemoteException;
}

// Реализация на сервере
public class OrderServiceImpl extends UnicastRemoteObject implements OrderService {
public Order createOrder(OrderRequest request) {
// OrderRequest сериализуется при передаче от клиента к серверу
// Order сериализуется при возврате от сервера к клиенту
return new Order(/* ... */);
}
}


Кэширование

Сериализованные объекты хранятся в распределённом кэше (Redis, Memcached) или локальном кэше на диске. При повторном запросе объект десериализуется, избегая дорогостоящих вычислений или обращений к базе данных.
public class CacheManager {

private final Map<String, byte[]> localCache = new ConcurrentHashMap<>();

public void put(String key, Serializable value) throws IOException {
byte[] bytes = serialize(value);
localCache.put(key, bytes);
}

public <T> T get(String key, Class<T> type)
throws IOException, ClassNotFoundException {
byte[] bytes = localCache.get(key);
if (bytes == null) return null;
return type.cast(deserialize(bytes));
}

private byte[] serialize(Serializable obj) throws IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(obj);
return baos.toByteArray();
}
}

private Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais)) {
return ois.readObject();
}
}
}


RPC (Remote Procedure Call)

RPC-фреймворки (gRPC, Apache Thrift, Dubbo) используют сериализацию для кодирования запросов и ответов. Клиент вызывает метод, как будто он локальный, но фреймворк сериализует аргументы, отправляет их по сети, десериализует на сервере, вызывает реальный метод и возвращает результат обратным путём.

RPC (Remote Procedure Call) — это парадигма межпроцессного взаимодействия, при которой программа вызывает процедуру (метод) в другом адресном пространстве (другой JVM, другой сервер) без явного кодирования сетевых деталей. RPC абстрагирует сеть, делая удалённый вызов похожим на локальный.



#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
Альтернативы Java Native Serialization

Java Native Serialization — не единственный и далеко не лучший выбор для большинства задач. Современные системы используют специализированные форматы, которые превосходят стандартный механизм по скорости, размеру данных и совместимости.

Текстовые форматы

JSON (JavaScript Object Notation) — текстовый формат, ставший де-факто стандартом для веб-API. Он человекочитаем, поддерживается всеми языками, но избыточен: ключи повторяются в каждом объекте, числа хранятся как строки, нет типизации.
// Jackson — самая популярная библиотека для JSON в Java
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonExample {
private static final ObjectMapper mapper = new ObjectMapper();

public String toJson(User user) throws JsonProcessingException {
return mapper.writeValueAsString(user);
}

public User fromJson(String json) throws JsonProcessingException {
return mapper.readValue(json, User.class);
}
}


XML
(eXtensible Markup Language) — строго типизированный текстовый формат с поддержкой схем (XSD). Используется в enterprise-системах, SOAP-сервисах, конфигурациях. Избыточен по размеру и медленнее JSON в парсинге.

YAML (YAML Ain't Markup Language) — текстовый формат, ориентированный на читаемость человеком. Широко используется для конфигураций (Kubernetes, Docker Compose, Spring Boot application.yml). Поддерживает ссылки (anchors), что позволяет представлять циклические графы.


Бинарные форматы

Protocol Buffers (protobuf) — бинарный формат сериализации от Google. Основан на схемах (.proto файлы), строго типизирован, компактен, быстр. Каждое поле имеет номер (tag), и в потоке хранятся только заполненные поля. Поддерживает обратную и прямую совместимость: можно добавлять поля, не ломая старых клиентов.
// user.proto
syntax = "proto3";
message User {
string name = 1;
int32 age = 2;
string email = 3;
}


// Сгенерированный Java-код из .proto
User user = User.newBuilder()
.setName("Alice")
.setAge(30)
.setEmail("alice@example.com")
.build();

byte[] bytes = user.toByteArray();
User parsed = User.parseFrom(bytes);


Apache Avro
— бинарный формат от Apache Hadoop ecosystem. Отличается от protobuf тем, что схема передаётся вместе с данными (или хранится в реестре схем, Schema Registry). Это делает Avro идеальным для потоковой обработки (Kafka), где потребители могут не знать заранее точную версию схемы. Avro использует схему для записи (writer schema) и схему для чтения (reader schema), разрешение совместимости происходит на лету.

Schema Registry — это сервис (например, Confluent Schema Registry), который хранит версии схем Avro/Protobuf/JSON Schema. Производители регистрируют схему и получают ID. Потребители по ID получают схему и десериализуют данные. Это централизованное управление эволюцией форматов.

Kryo — высокопроизводительная библиотека сериализации для Java. Не требует Serializable, работает напрямую с полями объектов через рефлексию. Значительно быстрее Java Native Serialization и компактнее. Используется в игровых движках, Apache Spark, Akka. Недостаток: привязан к Java, не кроссплатформенный.
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

public class KryoExample {
private final Kryo kryo = new Kryo();

public byte[] serialize(Object obj) {
Output output = new Output(1024, -1);
kryo.writeClassAndObject(output, obj);
return output.toBytes();
}

public Object deserialize(byte[] bytes) {
Input input = new Input(bytes);
return kryo.readClassAndObject(input);
}
}



FlatBuffers
— бинарный формат от Google, оптимизированный для десериализации без парсинга. Данные хранятся в виде плоского буфера с прямым доступом к полям по смещению. Не требует выделения объектов при чтении — идеален для игр и embedded-систем с жёсткими ограничениями по памяти и GC.

Сравнение подходов

Java Native Serialization — это механизм, встроенный в JVM, но он имеет серьёзные недостатки: медленный (рефлексия, метаданные классов в потоке), объёмный (полные имена классов, дескрипторы полей), небезопасный (десериализация произвольных классов может выполнить вредоносный код) и жёстко привязан к Java. В современных микросервисных архитектурах его использование считается антипаттерном, за исключением специфических сценариев (RMI-наследие, кэширование сессий в Tomcat).

JSON доминирует в веб-интеграции благодаря читаемости и универсальности. Protobuf и Avro — выбор для высоконагруженных внутренних коммуникаций и потоковой обработки. Kryo — для纯 Java-систем, где нужна максимальная скорость сериализации в памяти.


#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
Критерии выбора формата сериализации

Производительность
Производительность измеряется по трём метрикам: скорость сериализации (объект → байты), скорость десериализации (байты → объект) и размер полученного сообщения.
Java Native Serialization медленна из-за рефлексии и записи метаданных класса для каждого объекта. Protobuf и Kryo в 10–100 раз быстрее. JSON медленнее бинарных форматов из-за парсинга текста и boxing'а чисел.

Boxing — это автоматическая упаковка примитивных типов в объектные обёртки. В JSON число 42 парсится как строка, затем преобразуется в int, что требует дополнительных операций по сравнению с прямым чтением 4 байт из бинарного потока.

Читаемость
Текстовые форматы (JSON, XML, YAML) человекочитаемы, что упрощает отладку, логирование и ручное тестирование API. Бинарные форматы требуют специальных инструментов (hex-редакторов, декодеров protobuf) для анализа. В production-системах читаемость часто жертвуется ради производительности, но для конфигураций, API-спецификаций и логов предпочтительны текстовые форматы.

Версионность (эволюция схем)
Версионность — способность формата адаптироваться к изменениям структуры данных без поломки существующих клиентов и серверов.
Java Native Serialization требует ручного управления serialVersionUID и ломается при изменении сигнатуры класса (удаление/переименование полей, изменение типов). Protobuf поддерживает добавление полей (старые клиенты игнорируют неизвестные теги), удаление полей (если они не обязательны), изменение типов в рамках wire-совместимости. Avro поддерживает схемы чтения и записи разных версий с автоматическим разрешением различий.

Wire-совместимость (wire compatibility) — это свойство формата, при котором изменение схемы не нарушает формат бинарного потока. Например, в protobuf изменение int32 на int64 wire-совместимо, так как оба типа кодируются как varint, но семантически может привести к truncation на старых клиентах.

Безопасность
Безопасность — критичнейший критерий, особенно для десериализации данных из недоверенных источников.
Java Native Serialization имеет печальную репутацию: десериализация ObjectInputStream.readObject() может выполнить произвольный код, если в classpath есть классы с уязвимыми конструкторами, методами readObject() или ObjectInputValidation. Это привело к множеству критических CVE (Common Vulnerabilities and Exposures). Начиная с Java 9, добавлен механизм JEP 290 — фильтрация входящих классов через ObjectInputFilter.

CVE (Common Vulnerabilities and Exposures) — это стандартизированный каталог публично известных уязвимостей информационной безопасности. Каждой уязвимости присваивается уникальный идентификатор, например CVE-2015-4852 (уязвимость десериализации в Oracle WebLogic).
// JEP 290: фильтрация десериализации с Java 9
public void safeDeserialize(InputStream in) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(in);

// Разрешаем десериализовать только классы из доверенных пакетов
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"maxdepth=5;maxrefs=10000;com.myapp.*;!*"
);
ois.setObjectInputFilter(filter);

Object obj = ois.readObject();
}


ObjectInputFilter
— интерфейс, введённый в Java 9 (JEP 290), позволяющий задавать белые и чёрные списки классов для десериализации, ограничивать глубину графа объектов, количество ссылок и размер данных.
JSON безопаснее, так как парсер создаёт только простые структуры (строки, числа, массивы, объекты), не вызывая произвольных конструкторов Java. Однако и JSON имеет уязвимости: XXE (XML External Entity) при использовании небезопасных XML-парсеров, или DOS-атаки через Billion Laughs в YAML/XML.

XXE (XML External Entity) — тип атаки на XML-парсеры, при котором злоумышленник внедряет внешнюю сущность в XML-документ, заставляя парсер обращаться к внутренним файлам сервера или внешним ресурсам.
Protobuf и Avro безопасны по дизайну: они не поддерживают произвольную загрузку классов, работают только со схемой, строго типизированы и не позволяют встроить исполняемый код в сообщение.
Кроссплатформенность

Если система полиглотная (использует несколько языков программирования), Java Native Serialization и Kryo неприменимы — они привязаны к JVM. JSON, XML, protobuf, Avro, FlatBuffers имеют реализации на десятках языков и являются lingua franca для межсервисного взаимодействия.

Полиглотная система (polyglot system) — это система, построенная с использованием нескольких языков программирования. Например, backend на Java/Go, ML-сервис на Python, frontend на JavaScript. В таких системах формат обмена данных должен быть языконезависимым.


Размер данных
Бинарные форматы значительно компактнее текстовых. Например, объект с тремя полями в JSON может занимать 100 байт из-за повторяющихся ключей и скобок. Тот же объект в protobuf — 10–20 байт. Это критично для мобильных приложений, IoT-устройств и высоконагруженных сетевых протоколов.


#Java #для_новичков #beginner #IO #NIO #Serialize
👍4
Путь байтов в памяти JVM при сериализации

Сериализация: от объекта к байтам
Когда вы вызываете ObjectOutputStream.writeObject(user):
Объект в куче. Объект user и все объекты, на которые он ссылается (глубокий граф), находятся в куче (Heap). ObjectOutputStream обходит граф, начиная с корневого объекта. Для каждого объекта он записывает:
Дескриптор класса (полное имя класса, serialVersionUID, флаги)
Типы и значения полей (примитивы записываются как есть, объекты — рекурсивно)
Для каждого нового класса в иерархии — его дескриптор
Обход графа и отслеживание ссылок. ObjectOutputStream поддерживает таблицу handle — целочисленных идентификаторов для уже записанных объектов. Если в графе есть циклические ссылки (A ссылается на B, B ссылается на A) или повторяющиеся ссылки, второе вхождение объекта записывается не полностью, а как backreference (handle ранее записанного объекта). Это предотвращает бесконечную рекурсию и дублирование.
Буферизация. ObjectOutputStream пишет данные во внутренний буфер byte[] (обычно 512 байт или больше). Этот буфер создаётся в Young Generation, в Eden. Когда буфер заполняется, он сбрасывается (flush) в нижележащий поток: ByteArrayOutputStream, FileOutputStream или SocketOutputStream.
ByteArrayOutputStream. Если сериализация идёт в память, ByteArrayOutputStream накапливает байты в динамически расширяющемся массиве. При превышении ёмкости создаётся новый массив вдвое большего размера, данные копируются, старый массив становится мусором. Это может породить несколько временных массивов в Young Generation, которые собираются Minor GC.
Возвращаемый массив. baos.toByteArray() создаёт новый массив точного размера и копирует в него данные. Исходный внутренний буфер ByteArrayOutputStream остаётся в куче до сборки мусора. Итоговый массив байтов — это объект в куче, который может быть передан по сети, сохранён в файл или закэширован.
Работа GC. Все временные объекты — внутренние буферы ObjectOutputStream, промежуточные массивы ByteArrayOutputStream, объекты ObjectStreamClass (метаданные классов) — создаются в Young Generation. При короткой сериализации они уничтожаются при следующей Minor GC. Если сериализация выполняется в цикле (например, сериализация 10 000 объектов для пакетной отправки), буферы могут пережить несколько циклов Minor GC и мигрировать в Survivor Space, а затем в Old Generation.
ObjectStreamClass — это внутренний класс JVM, представляющий метаданные сериализуемого класса: имя, serialVersionUID, поля, методы writeObject/readObject. Эти объекты кэшируются внутри ObjectOutputStream для повторного использования, но при сериализации множества разных классов они накапливаются.


Десериализация: от байтов к объекту

При ObjectInputStream.readObject():
Байты в куче. Исходный массив байтов или поток находится в куче (если это ByteArrayInputStream) или читается из нативного буфера (если это FileInputStream или SocketInputStream).
Чтение дескрипторов. ObjectInputStream читает заголовок потока (magic number, версия протокола), затем дескриптор класса. Он ищет класс в Metaspace через Class.forName(). Если класс не загружен, ClassLoader загружает его байткод в Metaspace.
Создание объекта без конструктора. JVM выделяет память под объект в Young Generation (Eden) через механизм sun.misc.Unsafe.allocateInstance() (или аналог в новых версиях). Это выделение памяти без вызова конструктора — объект создаётся "сырым", с полями в состоянии по умолчанию (null, 0, false).
Заполнение полей. ObjectInputStream читает значения полей из потока и записывает их напрямую в память объекта (через JNI или Unsafe). Для объектных полей — рекурсивная десериализация. Для transient-полей — значение не читается, остаётся по умолчанию.
Восстановление графа. Если в потоке есть backreference (ссылка на ранее десериализованный объект), ObjectInputStream подставляет ссылку на существующий объект из таблицы handles. Это восстанавливает точную топологию исходного графа, включая циклы.
Вызов readObject(). Если класс реализует private void readObject(ObjectInputStream in), JVM вызывает этот метод после заполнения полей. Это позволяет выполнить пост-инициализацию: восстановить transient-поля, вычислить производные значения, проверить инварианты.
Работа GC. Вновь созданные объекты размещаются в Eden. Если десериализуемый граф большой (миллионы объектов), они могут быстро заполнить Young Generation и вызвать Minor GC в процессе десериализации. Если объекты сохраняются в долгоживущую структуру (коллекцию, кэш), они переходят в Old Generation.


Потоковая сериализация и GC

При сериализации в FileOutputStream или SocketOutputStream:
Буфер ObjectOutputStream (в куче) периодически сбрасывается. Сброс вызывает системный вызов write() (POSIX), который копирует байты из кучи JVM в нативную память ядра (кэш страниц или сетевой буфер).
Буфер в куче переиспользуется — новые объекты не создаются при каждом сбросе. Это снижает давление на GC.
Если сериализация выполняется в цикле с высокой частотой (например, в RPC-сервере, обрабатывающем 10 000 запросов в секунду), объекты ObjectOutputStream и ObjectInputStream создаются и закрываются для каждого запроса. Каждый такой объект — это десятки килобайт в куче. При интенсивной нагрузке они быстро заполняют Eden и вызывают частые Minor GC.

Оптимизация: пул переиспользуемых потоков. В высоконагруженных системах ObjectOutputStream создаётся один раз и сбрасывается через reset() между запросами. Метод reset() очищает внутренние таблицы handles, позволяя сериализовать новый граф объектов без создания нового потока.
public class PooledSerializer {
private final ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
private final ObjectOutputStream oos;

public PooledSerializer() throws IOException {
this.oos = new ObjectOutputStream(baos);
}

public synchronized byte[] serialize(Object obj) throws IOException {
baos.reset(); // сбрасываем буфер, не создавая новый
oos.reset(); // сбрасываем таблицу handles
oos.writeObject(obj);
oos.flush();
return baos.toByteArray(); // копирует текущий буфер
}
}

В этом примере baos.reset() очищает счётчик байтов, но не освобождает внутренний массив. oos.reset() очищает таблицу handles, позволяя сериализовать новые объекты без утечки памяти. Это существенно снижает давление на GC в сценариях с высокой частотой сериализации.


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

import java.io.*;

public class Task290726 {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
MyObject290726 obj = new MyObject290726("Hello");
oos.writeObject(obj);
oos.close();

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
MyObject290726 deserialized = (MyObject290726) ois.readObject();
ois.close();

System.out.println(deserialized.value);
}

static class MyObject290726 implements Serializable {
transient String value;
MyObject290726(String value) { this.value = value; }
}
}


#Tasks
👍3
Варианты ответа:
Anonymous Quiz
25%
10 2
50%
10 20
0%
1 20
25%
1 2
👍1
Что такое Stream.iterate() с предикатом? 🤓

Ответ:

Java 9 добавила перегрузку Stream.iterate(T seed, Predicate hasNext, UnaryOperator next).

Это эквивалент цикла for (seed; hasNext(seed); seed = next(seed)).

Например: 
Stream.iterate(0, i -> i < 10, i -> i + 1)
.forEach(System.out::println).

Позволяет генерировать последовательности с условием остановки без необходимости использовать limit() или takeWhile().

Это делает код более читаемым для математических итераций.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Опубликована новая статья!

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

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

Кроме того никто не запрещает вам публиковать свои статьи)) Дерзайте (в рамках разумного конечно😂)
👍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