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
Что выведет код?

import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.nio.*;

public class Task270726 {
public static void main(String[] args) throws Exception {
Path path = Paths.get("test.txt");
try (SeekableByteChannel ch = Files.newByteChannel(path,
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.READ)) {
ch.write(ByteBuffer.wrap("Hello".getBytes()));
ch.position(10);
ch.write(ByteBuffer.wrap("World".getBytes()));
System.out.println(ch.size());
}
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
25%
0
0%
5
25%
10
50%
15
👍2
Что такое String.formatted() и чем отличается от String.format()? 🤓

Ответ:

"Hello %s".formatted("World") (Java 15)
— это метод экземпляра String, который делает то же, что и статический String.format(), но в более удобном синтаксисе для цепочек вызовов.

Внутри он вызывает String.format(this, args).

Отличие только в синтаксисе: formatted() — метод объекта, format() — статический метод. Оба поддерживают спецификаторы (%s, %d, %n).

Это особенно удобно с текстовыми блоками: """ Name: %s """.formatted(name).



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Ребят, с сегодняшнего дня, основные статьи будут публиковаться и на https://devforge.ru/, но на день раньше!

Оцените удобство чтения и формата первой статьи на портале. 🙂

Для написания рецензий и комментов придется зарегистрироваться 🧑‍💻

Ссылка - тык ✈️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 28 июля

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

Па́вел Алексе́евич Черенко́в (15 [28] июля 1904, Новая Чигла, Бобровский уезд, Воронежская губерния, Российская империя — 6 января 1990, Москва, СССР) — советский физик-ядерщикнобелевский лауреат (1958), академик АН СССР (1970). Основные работы Черенкова посвящены физической оптикеядерной физикефизике частиц высоких энергий. В 1934 году обнаружил специфическое голубое свечение прозрачных жидкостей при облучении быстрыми заряженными частицами. Показал отличие данного вида излучения от флуоресценции. В 1936 году установил основное его свойство — направленность излучения, образование светового конуса, ось которого совпадает с траекторией движения частицы. В 1958 году вместе с Таммом и Франком был награждён Нобелевской премией по физике «за открытие и истолкование эффекта Черенкова». 

Никола́й Никола́евич Андре́ев (16 [28] июля 1880, село Курманы, Полтавская губерния, Российская империя — 31 декабря 1970, Москва, СССР) российский и советский физик; специалист в области акустики. Дал строгую теорию распределения звука в движущихся средах. Осуществил исследования по теории распространения звука вдоль поглощаемых поверхностей, теории акустических фильтров и звуковых волн конечной амплитуды. Ряд работ связан с изучением спектра затухающих колебаний, с исследованием колебаний кристаллических и анизотропных сред, вопросов реверберации звука и звукоизоляции. Под его руководством в нашей стране были начаты исследования по нелинейной акустике, по распространению звука в слоистых средах, электромеханическим активным материалам. Создал школу в области физической и технической акустики.

Ро́берт Гук (англ. Robert Hooke; Роберт Хук, 18 (28) июля 1635 — 3 (14) марта 1703) — английский естествоиспытатель и изобретатель. Член Лондонского королевского общества (1663).
К числу открытий Гука принадлежат:
- открытие пропорциональности между упругими растяжениями, сжатиями и изгибами, и производящими их напряжениями (закон Гука),
- правильная формулировка закона всемирного тяготения (приоритет Гука оспаривался Ньютоном, но, по-видимому, не в части формулировки — сила тяготения обратно пропорциональна квадрату расстояния; кроме того, Ньютон утверждал о независимом и более раннем открытии этой формулы, которую, однако, до открытия Гуком никому не сообщал),
- открытие цветов тонких плёнок (то есть, в конечном итоге, явления интерференции света),
- идея о волнообразном распространении света (более или менее одновременно с Гюйгенсом), экспериментальное обоснование её открытой Гуком интерференцией света, волновая теория света,
- гипотеза о поперечном характере световых волн,
- открытия в акустике, например, демонстрация того, что высота звука определяется частотой колебаний,
- теоретическое положение о сущности теплоты как движения частиц тела,
- открытие постоянства температуры таяния льда и кипения воды,
- первое наблюдение живой клетки с помощью усовершенствованного им микроскопа; также женской яйцеклетки и мужских сперматозоидов. Гуку же принадлежит сам термин «клетка» (англ. cell).

Чарлз Хард Та́унс (англ. Charles Hard Townes; 28 июля 1915, Гринвилл, Южная Каролина — 27 января 2015, Окленд, Калифорния) — американский учёный, физик-ядерщик, лауреат Нобелевской премии по физике (1964). Основные труды Таунса посвящены радиоспектроскопииквантовой электронике и её приложениям, нелинейной оптикерадиоастрономии. Независимо от А. М. Прохорова и Н. Г. Басова выдвинул идею нового принципа генерации и усиления электромагнитных волн и на его основе совместно с сотрудниками создал первый квантовый генератор — мазер на аммиаке (1954). В 1958 году совместно с А. Шавловым обосновали и запатентовали возможность создания оптического квантового генератора (лазера). В 1964 «за фундаментальные работы в области квантовой электроники, которые привели к созданию излучателей и усилителей на лазерно-мазерном принципе», Таунс совместно с Н. Г. Басовым и А. М. Прохоровым был удостоен Нобелевской премии по физике.


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

1959 — В Англии впервые введены почтовые коды и установлены автоматические сортировочные машины.


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

Тема: System.arraycopy — native метод, самый быстрый для копирования массивов, но проверяет типы во время выполнения.

Проблема: Копирование массивов — часто выполняемая операция, особенно при работе с коллекциями, буферами и низкоуровневыми структурами.

Существует несколько способов скопировать массив: цикл forArrays.copyOfArrays.copyOfRangeclone() и System.arraycopy. Многие разработчики используют циклы или высокоуровневые методы, забывая о самом производительном варианте. 

System.arraycopy
 — это native метод, реализованный на уровне JVM и операционной системы, который использует прямое копирование памяти (обычно через memcpy в C). Он работает значительно быстрее, чем ручные циклы, особенно для больших массивов, так как оптимизирован под аппаратные возможности. Однако System.arraycopy проверяет совместимость типов во время выполнения и выбрасывает ArrayStoreException, если типы несовместимы.

Эта проверка может быть неочевидна при работе с массивами объектов и ковариантными типами, что приводит к трудноуловимым ошибкам.

Решение: Для копирования массивов всегда используйте System.arraycopy, если вам нужна максимальная производительность и вы контролируете типы.

Для простого копирования всего массива используйте Arrays.copyOf или clone(), которые внутри вызывают System.arraycopy. Для частичного копирования предпочтителен прямой вызов System.arraycopy.

При копировании массивов объектов убедитесь, что исходный массив является подтипом целевого (например, String[] можно скопировать в Object[]). Если типы несовместимы, будет выброшено исключение, поэтому используйте явные проверки или обобщения, чтобы гарантировать корректность. Избегайте ручных циклов для копирования больших объемов данных.
import java.util.Arrays;

public class ArrayCopyPerformance {

public static void main(String[] args) {
int[] src = new int[1000000];
for (int i = 0; i < src.length; i++) src[i] = i;

//Медленно: ручной цикл
int[] dest1 = new int[src.length];
for (int i = 0; i < src.length; i++) {
dest1[i] = src[i];
}

//Быстро: System.arraycopy
int[] dest2 = new int[src.length];
System.arraycopy(src, 0, dest2, 0, src.length);

//Удобно: Arrays.copyOf (использует arraycopy внутри)
int[] dest3 = Arrays.copyOf(src, src.length);

// Частичное копирование
int[] destPartial = new int[100];
System.arraycopy(src, 500, destPartial, 0, 100); // копируем 100 элементов с позиции 500

// Опасность с объектами
String[] strings = {"a", "b", "c"};
Object[] objects = new Object[3];
System.arraycopy(strings, 0, objects, 0, 3); // OK: String[] -> Object[]

try {
// Ошибка: несовместимые типы
Integer[] ints = {1, 2, 3};
System.arraycopy(ints, 0, strings, 0, 3); // ArrayStoreException
} catch (ArrayStoreException e) {
System.err.println("Типы несовместимы: " + e);
}

// Для примитивов проверка осуществляется во время выполнения
// но совместимость гарантируется компилятором, если типы одинаковы
}
}


Объяснение:
 System.arraycopy является intrinsic методом во многих JVM, то есть компилятор может заменить его вызов на высокооптимизированный машинный код без перехода в native-метод. Он копирует непрерывные участки памяти, используя инструкции процессора для быстрого перемещения блоков данных. Это делает его в несколько раз быстрее, чем поэлементное копирование в Java, особенно для массивов примитивов, где нет накладных расходов на упаковку.

При копировании массивов объектов System.arraycopy выполняет проверку типов: если исходный тип не является подтипом целевого, выбрасывается ArrayStoreException с указанием несовместимого элемента. Эта проверка происходит во время выполнения, что может быть неожиданным для разработчиков, привыкших к проверкам на этапе компиляции.

Поэтому при работе с обобщенными массивами или преобразованиями типов рекомендуется использовать Arrays.copyOf или явные проверки через instanceof.


#Java #советы
👍4
Ребят! Продолжаю набор желающих обучиться Java до уровня мидла на Spring Framework.

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

Пишите в лс
@Oleborn
👍2
Что выведет код?

public class Task280726 {
public static void main(String[] args) {
Object[] src = {"A", "B", 1};
String[] dest = new String[3];
System.arraycopy(src, 0, dest, 0, 3);
System.out.println(dest[0]);
}
}


#Tasks
👍3
Варианты ответа:
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