История технологии сегодня — 04 августа
ℹ️ Кто родился в этот день
Джорджо Паризи (итал. Giorgio Parisi; род. 4 августа 1948 года) — итальянский физик-теоретик. Труды в основном посвящены квантовой теории поля, статистической механике и сложным системам. Соавтор уравнения ДГЛАП, уравнений Кардара — Паризи — Жанга. Нобелевская премия по физике (2021) за «открытие того, как беспорядок и флуктуации взаимодействуют в физических системах в масштабах от атомов до планет».
🌐 Знаковые события
1954 — свой первый полёт совершил британский сверхзвуковой самолёт P-1.
2007 — запущен космический аппарат НАСА «Феникс», предназначенный для исследования Марса.
#Biography #Birth_Date #Events #04августа
Джорджо Паризи (итал. Giorgio Parisi; род. 4 августа 1948 года) — итальянский физик-теоретик. Труды в основном посвящены квантовой теории поля, статистической механике и сложным системам. Соавтор уравнения ДГЛАП, уравнений Кардара — Паризи — Жанга. Нобелевская премия по физике (2021) за «открытие того, как беспорядок и флуктуации взаимодействуют в физических системах в масштабах от атомов до планет».
1954 — свой первый полёт совершил британский сверхзвуковой самолёт P-1.
2007 — запущен космический аппарат НАСА «Феникс», предназначенный для исследования Марса.
#Biography #Birth_Date #Events #04августа
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)
Глава 3. Сериализация и форматы обмена
JSON — современный стандарт обмена данными
JSON (JavaScript Object Notation) — это текстовый формат сериализации данных, изначально выведенный из синтаксиса литералов объектов языка JavaScript. Несмотря на происхождение, JSON является полностью языконезависимым форматом и определён стандартом RFC 8259 (декабрь 2017), который заменил RFC 7159. Сегодня JSON де-факто является стандартным форматом обмена данными в веб-сервисах, конфигурационных файлах, логах, сообщениях брокеров и хранилищах документов.
Что такое JSON
JSON представляет собой текстовый формат, основанный на двух структурах:
Коллекция пар ключ-значение — в разных языках это реализуется как объект, запись, структура, словарь, хэш-таблица или ассоциативный массив.
Упорядоченный список значений — в большинстве языков это массив, вектор, список или последовательность.
Формат спроектирован так, чтобы быть легко читаемым для человека и легко парсящимся для машины. Это достигается за счёт минималистичного синтаксиса и строгих правил: каждый документ JSON — это либо один объект, либо один массив.
JSON как основа REST API
JSON стал доминирующим форматом представления ресурсов в REST API благодаря нескольким факторам:
Нативная поддержка в JavaScript, который исполняется в браузере клиента.
Простота генерации и парсинга на стороне сервера — любой язык имеет библиотеку для работы с JSON.
Читаемость при отладке — в отличие от бинарных форматов, JSON можно просмотреть в инструментах разработчика браузера или в логах.
Отсутствие необходимости в схеме для базового использования — в отличие от XML, JSON не требует DTD или XSD для валидации.
#Java #для_новичков #beginner #IO #NIO #Serialize #JSON
Глава 3. Сериализация и форматы обмена
JSON — современный стандарт обмена данными
JSON (JavaScript Object Notation) — это текстовый формат сериализации данных, изначально выведенный из синтаксиса литералов объектов языка JavaScript. Несмотря на происхождение, JSON является полностью языконезависимым форматом и определён стандартом RFC 8259 (декабрь 2017), который заменил RFC 7159. Сегодня JSON де-факто является стандартным форматом обмена данными в веб-сервисах, конфигурационных файлах, логах, сообщениях брокеров и хранилищах документов.
Сериализация — это процесс преобразования структуры данных или объекта в формат, который можно сохранить на диск, передать по сети или записать в базу данных, с возможностью последующего восстановления (десериализации) в исходную структуру.
RFC (Request for Comments) — серия документов Интернет-инженерного совета (IETF), описывающих стандарты, протоколы и технологии Интернета. RFC 8259 является официальной спецификацией формата JSON.
Что такое JSON
JSON представляет собой текстовый формат, основанный на двух структурах:
Коллекция пар ключ-значение — в разных языках это реализуется как объект, запись, структура, словарь, хэш-таблица или ассоциативный массив.
Упорядоченный список значений — в большинстве языков это массив, вектор, список или последовательность.
Формат спроектирован так, чтобы быть легко читаемым для человека и легко парсящимся для машины. Это достигается за счёт минималистичного синтаксиса и строгих правил: каждый документ JSON — это либо один объект, либо один массив.
Парсинг — это процесс анализа строки или потока данных в соответствии с правилами формального языка (грамматикой) с целью построения структурированного представления, обычно дерева разбора или абстрактного синтаксического дерева (AST).
JSON как основа REST API
REST (Representational State Transfer) — это архитектурный стиль проектирования распределённых систем, введённый Роем Филдингом в его докторской диссертации 2000 года. REST не является протоколом или стандартом в строгом смысле; это набор ограничений и принципов.
Ключевое ограничение — statelessness (отсутствие состояния): каждый запрос от клиента к серверу должен содержать всю информацию, необходимую для его обработки, и сервер не хранит контекст клиента между запросами.
JSON стал доминирующим форматом представления ресурсов в REST API благодаря нескольким факторам:
Нативная поддержка в JavaScript, который исполняется в браузере клиента.
Простота генерации и парсинга на стороне сервера — любой язык имеет библиотеку для работы с JSON.
Читаемость при отладке — в отличие от бинарных форматов, JSON можно просмотреть в инструментах разработчика браузера или в логах.
Отсутствие необходимости в схеме для базового использования — в отличие от XML, JSON не требует DTD или XSD для валидации.
DTD (Document Type Definition) и XSD (XML Schema Definition) — это механизмы определения структуры и допустимых элементов XML-документа. JSON изначально не имел встроенного механизма схем, хотя позже появились JSON Schema и OpenAPI.
#Java #для_новичков #beginner #IO #NIO #Serialize #JSON
👍3
Структура JSON
Спецификация JSON определяет шесть типов значений:
Объекты
Объект — это неупорядоченный набор пар ключ-значение, заключённый в фигурные скобки. Ключ — это строка, значение — любой допустимый JSON-тип. Пары разделяются запятыми.
В Java объект JSON обычно маппится на
Массивы
Массив — это упорядоченная последовательность значений, заключённая в квадратные скобки. Значения разделяются запятыми. Индексация начинается с нуля.
В Java массив JSON маппится на
Строки
Строка — это последовательность из нуля или более символов Unicode, заключённая в двойные кавычки. JSON поддерживает escape-последовательности:
Строки в JSON кодируются в UTF-8, UTF-16 или UTF-32. На практике UTF-8 доминирует, так как обеспечивает обратную совместимость с ASCII и экономит память для латинских текстов.
Числа
Числа в JSON записываются в десятичной системе и могут быть целыми или с плавающей точкой. Поддерживается экспоненциальная нотация. Нет различия между целыми и вещественными числами на уровне синтаксиса — это одна грамматическая категория.
Спецификация JSON не ограничивает точность или диапазон чисел. Однако на практике числа с плавающей точкой часто интерпретируются как IEEE 754 double-precision (64 бита), что даёт точность около 15–17 значащих цифр. Для финансовых расчётов, где критична точность до копеек, это может привести к ошибкам округления. В таких случаях числа передаются как строки.
Булевы значения
Два литерала:
Null
Литерал
Преимущества JSON
Читаемость
JSON читается без специальных инструментов. Структура вложенности визуально очевидна благодаря отступам. Это снижает порог входа для разработчиков и упрощает отладку. В отличие от бинарных форматов, JSON можно открыть в любом текстовом редакторе или даже в терминале через
Универсальная поддержка
Каждый современный язык программирования имеет библиотеку для работы с JSON. В Java экосистеме существуют Jackson, Gson, JSON-B, JSON-P, org.json и многие другие. В JavaScript JSON — нативный формат. В Python — модуль
Простота
Грамматика JSON минимальна: всего два контейнерных типа и четыре скалярных. Нет пространств имён, нет схем, нет атрибутов, нет комментариев (официально — комментарии не поддерживаются, хотя некоторые парсеры их принимают). Эта простота снижает сложность парсеров и уменьшает вероятность ошибок при сериализации.
Интеграция с веб-стеком
JSON родственен JavaScript, что делает его естественным выбором для веб-приложений. Fetch API, XMLHttpRequest, WebSocket — все эти API работают с JSON напрямую. Браузер предоставляет нативный объект
Недостатки JSON
Размер и текстовая природа
JSON — текстовый формат. Каждое число, булево значение или null записывается символами, а не бинарными данными. Ключи объектов повторяются в каждом документе. Для массовых данных это приводит к значительному overhead.
В этом примере ключи
Отсутствие строгой типизации
JSON не имеет схемы встроенной в сам документ. Поле
Отсутствие поддержки бинарных данных
JSON не имеет встроенного типа для бинарных данных. Для передачи бинарного контента (изображения, PDF, аудио) используется кодирование в Base64, что увеличивает размер данных примерно на 33%. Альтернативы — передача бинарных данных отдельно (multipart/form-data) или использование бинарных форматов.
Отсутствие комментариев
Спецификация JSON явно запрещает комментарии. Это создаёт проблемы для конфигурационных файлов, где комментарии полезны для документирования. Решения: использование JSON5 (расширение JSON с комментариями), YAML или TOML для конфигурации, либо отдельные файлы документации.
Производительность парсинга
Парсинг JSON требует посимвольного разбора строки, построения дерева объектов в памяти и часто — рефлексии для маппинга на Java-объекты. Для высоконагруженных систем это может стать bottleneck. Оптимизации включают потоковый парсинг (Jackson Streaming API), бинарные форматы на основе JSON (BSON, MessagePack) или полный отказ от JSON в пользу protobuf/Avro.
#Java #для_новичков #beginner #IO #NIO #Serialize #JSON
Спецификация JSON определяет шесть типов значений:
Объекты
Объект — это неупорядоченный набор пар ключ-значение, заключённый в фигурные скобки. Ключ — это строка, значение — любой допустимый JSON-тип. Пары разделяются запятыми.
{
"user": {
"id": 42,
"name": "Alice",
"active": true
}
}В Java объект JSON обычно маппится на
Map<String, Object> или на POJO (Plain Old Java Object — простой Java-объект без специальных требований к наследованию или аннотациям). Ключи в объекте JSON уникальны в пределах одного объекта, но спецификация не запрещает дубликаты — поведение при дубликатах зависит от парсера. Большинство парсеров используют последнее встреченное значение.Массивы
Массив — это упорядоченная последовательность значений, заключённая в квадратные скобки. Значения разделяются запятыми. Индексация начинается с нуля.
{
"orders": [
{ "id": 1, "total": 99.99 },
{ "id": 2, "total": 150.00 }
]
}В Java массив JSON маппится на
List<Object>, Object[] или типизированный список List<Order>, если используется библиотека с поддержкой дженериков.Строки
Строка — это последовательность из нуля или более символов Unicode, заключённая в двойные кавычки. JSON поддерживает escape-последовательности:
\n (перевод строки), \t (табуляция), \\ (обратный слеш), \" (кавычка), \uXXXX (символ Unicode в шестнадцатеричном виде).{
"description": "Line 1\nLine 2\tTabbed",
"emoji": "\uD83D\uDE00"
}Строки в JSON кодируются в UTF-8, UTF-16 или UTF-32. На практике UTF-8 доминирует, так как обеспечивает обратную совместимость с ASCII и экономит память для латинских текстов.
UTF-8 (Unicode Transformation Format, 8-bit) — это кодировка переменной длины, которая кодирует символы Unicode от 1 до 4 байтами. Символы ASCII (0–127) занимают 1 байт, что делает UTF-8 эффективной для англоязычных текстов.
Числа
Числа в JSON записываются в десятичной системе и могут быть целыми или с плавающей точкой. Поддерживается экспоненциальная нотация. Нет различия между целыми и вещественными числами на уровне синтаксиса — это одна грамматическая категория.
{
"integer": 42,
"negative": -17,
"float": 3.14159,
"exponential": 6.022e23,
"small": 1e-10
}Спецификация JSON не ограничивает точность или диапазон чисел. Однако на практике числа с плавающей точкой часто интерпретируются как IEEE 754 double-precision (64 бита), что даёт точность около 15–17 значащих цифр. Для финансовых расчётов, где критична точность до копеек, это может привести к ошибкам округления. В таких случаях числа передаются как строки.
IEEE 754 — это стандарт представления чисел с плавающей точкой в компьютерах. Double-precision использует 64 бита: 1 бит знака, 11 бит экспоненты и 52 бита мантиссы. Не все десятичные дроби точно представимы в двоичной системе, отсюда ошибки округления.
Булевы значения
Два литерала:
true и false. В Java маппятся на примитив boolean или объект Boolean.{
"enabled": true,
"debug": false
}Null
Литерал
null представляет отсутствие значения. В Java маппится на null.{
"middleName": null
}Преимущества JSON
Читаемость
JSON читается без специальных инструментов. Структура вложенности визуально очевидна благодаря отступам. Это снижает порог входа для разработчиков и упрощает отладку. В отличие от бинарных форматов, JSON можно открыть в любом текстовом редакторе или даже в терминале через
curl.Универсальная поддержка
Каждый современный язык программирования имеет библиотеку для работы с JSON. В Java экосистеме существуют Jackson, Gson, JSON-B, JSON-P, org.json и многие другие. В JavaScript JSON — нативный формат. В Python — модуль
json в стандартной библиотеке. В Go — пакет encoding/json. Это означает, что сервис на Java может бесшовно обмениваться данными с сервисом на Python, Go или Node.js.Простота
Грамматика JSON минимальна: всего два контейнерных типа и четыре скалярных. Нет пространств имён, нет схем, нет атрибутов, нет комментариев (официально — комментарии не поддерживаются, хотя некоторые парсеры их принимают). Эта простота снижает сложность парсеров и уменьшает вероятность ошибок при сериализации.
Интеграция с веб-стеком
JSON родственен JavaScript, что делает его естественным выбором для веб-приложений. Fetch API, XMLHttpRequest, WebSocket — все эти API работают с JSON напрямую. Браузер предоставляет нативный объект
JSON с методами parse() и stringify().Недостатки JSON
Размер и текстовая природа
JSON — текстовый формат. Каждое число, булево значение или null записывается символами, а не бинарными данными. Ключи объектов повторяются в каждом документе. Для массовых данных это приводит к значительному overhead.
{
"users": [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
}В этом примере ключи
"id" и "name" повторяются для каждого элемента. В бинарном формате (например, Protocol Buffers) ключи заменяются числовыми тегами, а строковые значения могут быть закодированы более эффективно. JSON также не поддерживает сжатие на уровне формата — для уменьшения размера применяется внешнее сжатие (gzip, brotli).Protocol Buffers (protobuf) — бинарный формат сериализации, разработанный Google. В отличие от JSON, protobuf требует предварительного определения схемы (proto-файл), но обеспечивает компактное бинарное представление и строгую типизацию.
Отсутствие строгой типизации
JSON не имеет схемы встроенной в сам документ. Поле
"age" может содержать число в одном запросе и строку в другом. Это создаёт риск ошибок при десериализации, если клиент и сервер не согласовали контракт. Для решения этой проблемы используются JSON Schema, OpenAPI/Swagger или кодогенерация по proto-файлам.JSON Schema — это спецификация (draft 2020-12) для описания структуры JSON-документов. Она позволяет определять типы полей, обязательность, диапазоны значений, регулярные выражения для строк и другие ограничения.
Отсутствие поддержки бинарных данных
JSON не имеет встроенного типа для бинарных данных. Для передачи бинарного контента (изображения, PDF, аудио) используется кодирование в Base64, что увеличивает размер данных примерно на 33%. Альтернативы — передача бинарных данных отдельно (multipart/form-data) или использование бинарных форматов.
Base64 — это схема кодирования бинарных данных в текстовый формат с использованием 64 символов ASCII (A–Z, a–z, 0–9, +, /). Каждые 3 байта (24 бита) кодируются 4 символами Base64 (по 6 бит каждый), отсюда overhead в 33%.
Отсутствие комментариев
Спецификация JSON явно запрещает комментарии. Это создаёт проблемы для конфигурационных файлов, где комментарии полезны для документирования. Решения: использование JSON5 (расширение JSON с комментариями), YAML или TOML для конфигурации, либо отдельные файлы документации.
Производительность парсинга
Парсинг JSON требует посимвольного разбора строки, построения дерева объектов в памяти и часто — рефлексии для маппинга на Java-объекты. Для высоконагруженных систем это может стать bottleneck. Оптимизации включают потоковый парсинг (Jackson Streaming API), бинарные форматы на основе JSON (BSON, MessagePack) или полный отказ от JSON в пользу protobuf/Avro.
Bottleneck — это узкое место в системе, которое ограничивает общую производительность. В контексте JSON-парсинга bottleneck часто возникает из-за аллокации объектов в куче и работы GC при обработке больших JSON-документов.
#Java #для_новичков #beginner #IO #NIO #Serialize #JSON
👍3
Путь байтов в памяти JVM при работе с JSON
1. Получение JSON-строки
JSON-данные обычно поступают в JVM одним из трёх способов: как строка из HTTP-ответа, как содержимое файла, считанное через
Если JSON приходит по HTTP, процесс начинается на уровне сетевого стека ОС. Пакеты TCP собираются в сегменты, данные копируются из kernel space (пространство ядра) в user space (пользовательское пространство) в буфер сокета. Servlet-контейнер (Tomcat, Jetty, Netty) или фреймворк (Spring Boot) читает байты из сокета в буфер — обычно это
2. Декодирование байтов в строку
Байты из сети или файла представляют JSON в кодировке UTF-8. JVM декодирует их в объект
В Java 9+ строки хранятся как
3. Парсинг JSON: аллокация объектов в куче
Парсер (Jackson, Gson или стандартный
Jackson создаст следующие объекты в куче:
Один
Один
Два
Четыре
Два
Строковые ключи
Все эти объекты создаются в Young Generation, в области Eden. Для JSON-документа размером 10 КБ количество объектов может достигать сотен. При обработке тысяч запросов в секунду это создаёт огромное давление на Minor GC.
4. Маппинг на POJO: рефлексия и дополнительные аллокации
При десериализации JSON в Java-объекты (POJO) парсер использует рефлексию — механизм Java для интроспекции классов во время выполнения.
Рефлексия требует:
Создания объекта
Вызова
Установки полей через
Jackson оптимизирует это через
5. Работа GC с JSON-объектами
После десериализации JSON-строка и промежуточные структуры парсера (токены, узлы дерева) становятся мусором. Если использовался Jackson в режиме потокового парсинга (Streaming API), дерево не строится целиком — объекты создаются по мере чтения и сразу собираются GC. В режиме древовидного парсинга (Tree Model) всё дерево JSON хранится в памяти до явного освобождения.
Для высоконагруженных систем критично минимизировать аллокации:
В этом примере
6. Сериализация: обратный путь
При сериализации Java-объекта в JSON происходит обратный процесс:
Jackson обходит поля объекта через кэшированные сериализаторы.
Генерирует JSON-текст как
Если вывод идёт в
Если вывод идёт в
При сериализации в HTTP-ответ через Spring Boot используется
7. Ключевые строки и пул строк
Ключи JSON-объектов (
Интернирование строк — это процесс помещения строки в пул строк JVM через метод
8. Бинарные JSON-форматы и память
Для снижения давления на GC и размера данных используются бинарные форматы на основе JSON:
BSON (Binary JSON) — используется в MongoDB. Содержит типовые теги и длины полей, что ускоряет навигацию без полного парсинга.
MessagePack — бинарный формат, совместимый с JSON по структуре, но более компактный.
CBOR (Concise Binary Object Representation) — стандарт RFC 8949, разработанный IETF как бинарная альтернатива JSON.
Эти форматы парсятся быстрее, так как не требуют посимвольного разбора текста, и создают меньше объектов в куче.
#Java #для_новичков #beginner #IO #NIO #Serialize #JSON
1. Получение JSON-строки
JSON-данные обычно поступают в JVM одним из трёх способов: как строка из HTTP-ответа, как содержимое файла, считанное через
Files.readString(), или как сообщение из брокера (Kafka, RabbitMQ).Если JSON приходит по HTTP, процесс начинается на уровне сетевого стека ОС. Пакеты TCP собираются в сегменты, данные копируются из kernel space (пространство ядра) в user space (пользовательское пространство) в буфер сокета. Servlet-контейнер (Tomcat, Jetty, Netty) или фреймворк (Spring Boot) читает байты из сокета в буфер — обычно это
byte[] или ByteBuffer в куче JVM.Kernel space и user space — это два режима работы процессора. Kernel space — привилегированный режим, в котором работает код операционной системы и драйверов. User space — непривилегированный режим, в котором работают обычные приложения, включая JVM. Переход между режимами (context switch) — дорогая операция.
2. Декодирование байтов в строку
Байты из сети или файла представляют JSON в кодировке UTF-8. JVM декодирует их в объект
String — это массив byte[] (начиная с Java 9; ранее char[]) в куче, плюс объект-обёртка String со ссылкой на массив, хэш-кодом и флагами.В Java 9+ строки хранятся как
byte[] с кодировкой LATIN1 (1 байт на символ) или UTF-16 (2 байта на символ), в зависимости от содержимого. Компактные строки (compact strings) экономят память для ASCII-текстов. Для JSON, состоящего преимущественно из ASCII, это означает экономию ~50% по сравнению со старым char[].Compact strings — оптимизация, введённая в Java 9 (JEP 254). Строки, содержащие только символы Latin-1 (коды 0–255), хранятся в массиве
byte[]по одному байту на символ. Если встречается символ вне Latin-1, массив переключается на UTF-16 (2 байта на символ).
3. Парсинг JSON: аллокация объектов в куче
Парсер (Jackson, Gson или стандартный
org.json) читает строку посимвольно и строит дерево объектов. Для JSON-документа:{
"users": [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
}Jackson создаст следующие объекты в куче:
Один
JsonNode (или LinkedHashMap при десериализации в Map) для корневого объекта.Один
ArrayNode (или ArrayList) для массива "users".Два
ObjectNode (или LinkedHashMap) для объектов пользователей.Четыре
IntNode / Integer для полей "id".Два
TextNode / String для полей "name".Строковые ключи
"users", "id", "name" — если они не интернированы, создаются как отдельные объекты String.Все эти объекты создаются в Young Generation, в области Eden. Для JSON-документа размером 10 КБ количество объектов может достигать сотен. При обработке тысяч запросов в секунду это создаёт огромное давление на Minor GC.
4. Маппинг на POJO: рефлексия и дополнительные аллокации
При десериализации JSON в Java-объекты (POJO) парсер использует рефлексию — механизм Java для интроспекции классов во время выполнения.
Рефлексия требует:
Создания объекта
Constructor через Class.getDeclaredConstructor().Вызова
Constructor.newInstance() — аллокация POJO в куче.Установки полей через
Field.set() или вызова сеттеров.Jackson оптимизирует это через
SerializerCache и DeserializerCache — кэши, хранящие скомпилированные сериализаторы и десериализаторы. После первого использования класса его метаданные кэшируются, и последующие операции не требуют полной рефлексии. Однако первый парсинг класса — дорогая операция, создающая множество временных объектов.5. Работа GC с JSON-объектами
После десериализации JSON-строка и промежуточные структуры парсера (токены, узлы дерева) становятся мусором. Если использовался Jackson в режиме потокового парсинга (Streaming API), дерево не строится целиком — объекты создаются по мере чтения и сразу собираются GC. В режиме древовидного парсинга (Tree Model) всё дерево JSON хранится в памяти до явного освобождения.
Для высоконагруженных систем критично минимизировать аллокации:
// Потоковый парсинг Jackson — минимум аллокаций
public void processLargeJson(InputStream in) throws IOException {
// JsonFactory — лёгкий фабричный объект для создания парсеров
JsonFactory factory = new JsonFactory();
// JsonParser — потоковый парсер, читает JSON без построения дерева
try (JsonParser parser = factory.createParser(in)) {
while (parser.nextToken() != JsonToken.END_OBJECT) {
String fieldName = parser.getCurrentName();
if ("users".equals(fieldName)) {
parser.nextToken(); // переходим к массиву
while (parser.nextToken() == JsonToken.START_OBJECT) {
// Обрабатываем каждый объект пользователя
int id = 0;
String name = null;
while (parser.nextToken() != JsonToken.END_OBJECT) {
String key = parser.getCurrentName();
parser.nextToken();
if ("id".equals(key)) {
id = parser.getIntValue();
} else if ("name".equals(key)) {
name = parser.getValueAsString();
}
}
// Обрабатываем пользователя без создания промежуточных объектов
processUser(id, name);
}
}
}
}
}
В этом примере
JsonParser читает JSON как поток токенов, не создавая дерево объектов. Память используется минимально, и давление на GC снижается на порядки по сравнению с mapper.readValue(json, UserList.class).6. Сериализация: обратный путь
При сериализации Java-объекта в JSON происходит обратный процесс:
Jackson обходит поля объекта через кэшированные сериализаторы.
Генерирует JSON-текст как
char[] или byte[] внутри StringWriter или OutputStream.Если вывод идёт в
OutputStream (HTTP-ответ, файл), байты записываются напрямую без промежуточной строки.Если вывод идёт в
String, создаётся объект String в куче, который затем передаётся дальше.При сериализации в HTTP-ответ через Spring Boot используется
MappingJackson2HttpMessageConverter, который пишет JSON напрямую в OutputStream ответа, минуя создание промежуточной строки. Это экономит аллокации и снижает нагрузку на GC.7. Ключевые строки и пул строк
Ключи JSON-объектов (
"id", "name", "users") — это строки, которые повторяются в каждом документе. Jackson может интернировать часто встречающиеся ключи через JsonFactory.Feature.INTERN_FIELD_NAMES. Интернирование помещает строки в пул строк (String Pool) в Metaspace, делая их доступными для повторного использования между запросами. Это снижает аллокации, но увеличивает потребление Metaspace — пул строк не собирается обычным GC.Интернирование строк — это процесс помещения строки в пул строк JVM через метод
String.intern(). Если строка с таким содержимым уже есть в пуле, возвращается существующий объект; иначе строка добавляется в пул. Интернирование экономит память при множестве одинаковых строк, но может привести к утечке памяти в Metaspace при интернировании уникальных строк.8. Бинарные JSON-форматы и память
Для снижения давления на GC и размера данных используются бинарные форматы на основе JSON:
BSON (Binary JSON) — используется в MongoDB. Содержит типовые теги и длины полей, что ускоряет навигацию без полного парсинга.
MessagePack — бинарный формат, совместимый с JSON по структуре, но более компактный.
CBOR (Concise Binary Object Representation) — стандарт RFC 8949, разработанный IETF как бинарная альтернатива JSON.
Эти форматы парсятся быстрее, так как не требуют посимвольного разбора текста, и создают меньше объектов в куче.
#Java #для_новичков #beginner #IO #NIO #Serialize #JSON
👍4
Что выведет код?
#Tasks
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Date;
public class Task040826 {
static class Event {
public String name;
public Date date;
Event(String name, Date date) {
this.name = name; this.date = date;
}
}
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Event event = new Event("Party", new Date(0));
System.out.println(mapper.writeValueAsString(event));
}
}
#Tasks
👍3
Что такое 🤓
Ответ:
Predicate.not(Predicate<T> predicate) (Java 11) возвращает предикат, являющийся логическим отрицанием переданного.
Позволяет упростить фильтрацию: stream.filter(Predicate.not(String::isBlank)) вместо stream.filter(s -> !s.isBlank()).
Это делает код более читаемым и функциональным. Работает со ссылками на методы, улучшая выразительность лямбда-выражений.
#собеседование
Predicate.not() в Java 11? Ответ:
Predicate.not(Predicate<T> predicate) (Java 11)
Позволяет упростить фильтрацию: stream.filter(Predicate.not(String::isBlank)) вместо stream.filter(s -> !s.isBlank()).
Это делает код более читаемым и функциональным. Работает со ссылками на методы, улучшая выразительность лямбда-выражений.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 05 августа
ℹ️ Кто родился в этот день
Нил О́лден А́рмстронг (англ. Neil Alden Armstrong; 5 августа 1930, Уопаконета, Огайо, США — 25 августа 2012, Цинциннати, Огайо, США) — американский астронавт НАСА, лётчик-испытатель, космический инженер, профессор университета, военно-морской лётчик США, первый человек, ступивший на Луну 21 июля 1969 года в ходе лунной экспедиции корабля «Аполлон-11».
Нильс Хе́нрик А́бель (норв. Niels Henrik Abel; 5 августа 1802, Финнёй — 6 апреля 1829, Фроланн) — норвежский математик. В теории специальных, особенно эллиптических и абелевых функций, Абель был признанным лидером-основателем наряду с Якоби. Он первый определил эллиптические функции как функции, обратные эллиптическим интегралам, распространил их определения на общий комплексный случай и глубоко исследовал их свойства.
🌐 Знаковые события
1921 — в американском Дейтоне (штат Огайо) успешно испытан первый в мире автомобиль, управляемый по радио.
1973 — стартовала советская космическая миссия «Марс-6».
#Biography #Birth_Date #Events #05августа
Нил О́лден А́рмстронг (англ. Neil Alden Armstrong; 5 августа 1930, Уопаконета, Огайо, США — 25 августа 2012, Цинциннати, Огайо, США) — американский астронавт НАСА, лётчик-испытатель, космический инженер, профессор университета, военно-морской лётчик США, первый человек, ступивший на Луну 21 июля 1969 года в ходе лунной экспедиции корабля «Аполлон-11».
Нильс Хе́нрик А́бель (норв. Niels Henrik Abel; 5 августа 1802, Финнёй — 6 апреля 1829, Фроланн) — норвежский математик. В теории специальных, особенно эллиптических и абелевых функций, Абель был признанным лидером-основателем наряду с Якоби. Он первый определил эллиптические функции как функции, обратные эллиптическим интегралам, распространил их определения на общий комплексный случай и глубоко исследовал их свойства.
1921 — в американском Дейтоне (штат Огайо) успешно испытан первый в мире автомобиль, управляемый по радио.
1973 — стартовала советская космическая миссия «Марс-6».
#Biography #Birth_Date #Events #05августа
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
[Совет по Java #068]
Тема:
Проблема: Фабричные методы
Они категорически запрещают передачу
Это сделано для упрощения контракта и предотвращения неоднозначности, связанной с
Решение: Если ваша бизнес-логика допускает использование
Для обеспечения неизменяемости с поддержкой
Объяснение:
Они не поддерживают
При выборе между
#Java #советы
Тема:
Set.of() и Map.of() не допускают null ключей/значений.Проблема: Фабричные методы
Set.of(), Map.of() и их перегрузки, появившиеся в Java 9, были спроектированы для создания неизменяемых коллекций с фиксированным набором элементов. Они категорически запрещают передачу
null в качестве ключа или значения, выбрасывая NullPointerException как на этапе создания, так и при попытке добавить null в уже существующую коллекцию. Это сделано для упрощения контракта и предотвращения неоднозначности, связанной с
null в неизменяемых структурах. Однако многие разработчики, привыкшие к HashMap и HashSet, которые позволяют хранить null (один null-ключ в Map и один null-элемент в Set), сталкиваются с неожиданными исключениями при переходе на новые API. Особенно это актуально в коде, где null может появляться из внешних источников (база данных, пользовательский ввод, внешние API). Ошибка проявляется на этапе создания коллекции, что может быть неочевидным при использовании Map.ofEntries или при передаче коллекций в методы.Решение: Если ваша бизнес-логика допускает использование
null в качестве ключей или значений, используйте традиционные изменяемые коллекции: HashMap, HashSet, TreeMap и т.д. Для обеспечения неизменяемости с поддержкой
null можно использовать Collections.unmodifiableMap(new HashMap<>(...)), но это не иммутабельная коллекция, а view с проверками во время выполнения. Для безопасной работы с null и неизменяемостью рассмотрите возможность обернуть null в специальный объект-маркер (например, Optional или пользовательский класс), либо использовать Map с типом значения Optional. import java.util.*;
public class NullInCollections {
public static void main(String[] args) {
//Антипаттерн: Set.of() с null
try {
Set<String> set = Set.of("a", null, "b"); // NullPointerException
} catch (NullPointerException e) {
System.err.println("Set.of() не допускает null");
}
//Map.of() с null ключом или значением
try {
Map<String, String> map = Map.of("key", null); // NullPointerException
} catch (NullPointerException e) {
System.err.println("Map.of() не допускает null-значений");
}
//Решение: использовать HashMap для поддержки null
Map<String, String> hashMap = new HashMap<>();
hashMap.put("key1", "value1");
hashMap.put("key2", null); // допустимо
hashMap.put(null, "value3"); // допустим null-ключ
System.out.println("HashMap with nulls: " + hashMap);
//Если нужна неизменяемость с null, используйте unmodifiable обертку
Map<String, String> unmodifiableWithNull = Collections.unmodifiableMap(hashMap);
// unmodifiableWithNull нельзя изменить, но null сохраняются
System.out.println("Unmodifiable with null: " + unmodifiableWithNull);
// Однако это не иммутабельная коллекция — исходный hashMap можно изменить, и view отразит
//Для безопасной работы с неизменяемыми коллекциями и null — избегайте null
// Используйте Optional или значения-заглушки
Map<String, Optional<String>> safeMap = Map.of(
"key1", Optional.of("value"),
"key2", Optional.empty() // представляет null
);
// При чтении: safeMap.get("key2").orElse("default");
//Использование checkedMap для типизированной проверки (не решает проблему null)
Map<String, String> checked = Collections.checkedMap(
new HashMap<>(), String.class, String.class);
checked.put("a", null); // допустимо, проверка типов не касается null
}
}
Объяснение:
Set.of и Map.of реализованы как специализированные внутренние классы, оптимизированные для хранения фиксированного числа элементов. Они не поддерживают
null, потому что это позволяет упростить реализацию (не нужны проверки на null при чтении) и избежать неоднозначности: в неизменяемой коллекции отсутствие элемента и null-значение — разные вещи, но при использовании null они могут смешиваться. HashMap и HashSet были спроектированы с поддержкой null (один null-ключ, неограниченное количество null-значений), и это поведение осталось для обратной совместимости. При выборе между
Map.of и HashMap учитывайте: если null может появиться в данных, используйте HashMap и управляйте неизменяемостью через обертки или явное копирование. В современных API (например, Stream API, Optional) предпочтительно полностью избегать null, заменяя его на Optional или пустые коллекции. #Java #советы
👍4
Что выведет код?
#Tasks
import java.util.Map;
public class Task050826 {
public static void main(String[] args) {
try {
Map<String, String> map = Map.of(
"key1", "value1",
"key2", null
);
System.out.println("size=" + map.size());
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
}
}
#Tasks
👍3
Варианты ответа:
Anonymous Quiz
67%
size=2
0%
size=1
25%
NullPointerException
8%
IllegalArgumentException
👍3
Что такое 🤓
Ответ:
Optional.isEmpty() (Java 11) — метод, возвращающий true, если Optional пуст (т.е. не содержит значения).
Ранее использовали !optional.isPresent(). Новый метод делает код более читаемым: if (optional.isEmpty()) { ... }. Это симметрично isPresent() и улучшает понимание намерения.
Позволяет писать более естественные условия.
#собеседование
Optional.isEmpty()? Ответ:
Optional.isEmpty() (Java 11)
Ранее использовали !optional.isPresent(). Новый метод делает код более читаемым: if (optional.isEmpty()) { ... }. Это симметрично isPresent() и улучшает понимание намерения.
Позволяет писать более естественные условия.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
История технологии сегодня — 06 августа
ℹ️ Кто родился в этот день
Ке́вин Дэ́вид Ми́тник (англ. Kevin David Mitnick; 6 августа 1963, Лос-Анджелес, Калифорния, США — 16 июля 2023, Лас-Вегас) — американский хакер, после отбывания наказания — консультант по компьютерной безопасности и писатель. Знаковая фигура в сфере информационной безопасности. В конце XX века был признан виновным в различных компьютерных и коммуникационных преступлениях.
Алекса́ндр Ю́льевич Ишли́нский (24 июля (6 августа) 1913, Москва — 7 февраля 2003, Москва) — советский и российский учёный-механик, организатор науки и педагог. Основные труды А. Ю. Ишлинского относятся к общей механике, динамике твёрдого тела и гироскопов, теории систем инерциальной навигации, теории упругости, теории пластичности, механике систем с трением. В каждой из перечисленных областей им были получены основополагающие результаты, решены важные для практики сложнейшие проблемы.
🌐 Знаковые события
1991 — в CERN на компьютере NeXT была создана первая в мире веб-страница с базовым набором HTML.
1996 — НАСА объявило о том, что на метеорите ALH 84001, отколовшемся от Марса и упавшем на Землю, найдены остатки микроорганизмов, существовавших 3 млрд лет назад.
#Biography #Birth_Date #Events #06августа
Ке́вин Дэ́вид Ми́тник (англ. Kevin David Mitnick; 6 августа 1963, Лос-Анджелес, Калифорния, США — 16 июля 2023, Лас-Вегас) — американский хакер, после отбывания наказания — консультант по компьютерной безопасности и писатель. Знаковая фигура в сфере информационной безопасности. В конце XX века был признан виновным в различных компьютерных и коммуникационных преступлениях.
Алекса́ндр Ю́льевич Ишли́нский (24 июля (6 августа) 1913, Москва — 7 февраля 2003, Москва) — советский и российский учёный-механик, организатор науки и педагог. Основные труды А. Ю. Ишлинского относятся к общей механике, динамике твёрдого тела и гироскопов, теории систем инерциальной навигации, теории упругости, теории пластичности, механике систем с трением. В каждой из перечисленных областей им были получены основополагающие результаты, решены важные для практики сложнейшие проблемы.
1991 — в CERN на компьютере NeXT была создана первая в мире веб-страница с базовым набором HTML.
1996 — НАСА объявило о том, что на метеорите ALH 84001, отколовшемся от Марса и упавшем на Землю, найдены остатки микроорганизмов, существовавших 3 млрд лет назад.
#Biography #Birth_Date #Events #06августа
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)
Глава 3. Сериализация и форматы обмена
YAML — формат для конфигураций
YAML (YAML Ain't Markup Language) — это текстовый формат сериализации данных, ориентированный на максимальную читаемость для человека. Первая спецификация YAML 1.0 была выпущена в 2001 году, текущая актуальная версия — YAML 1.2.2 (октябрь 2021). В отличие от JSON, YAML использует отступы для обозначения структуры вместо фигурных скобок и квадратных скобок, что делает документы визуально чище и ближе к естественному языку.
YAML не является языком разметки в строгом смысле: он не содержит тегов разметки текста, как HTML или XML. Вместо этого YAML — это формат представления графа данных (data graph), где узлами являются скаляры, последовательности и отображения (маппинги).
Где используется YAML
YAML стал де-факто стандартом для конфигурационных файлов в современной инфраструктуре и разработке:
Spring Boot: файл
Docker Compose: файл
Kubernetes: манифесты ресурсов (pods, deployments, services) пишутся в YAML.
Ansible: playbooks и inventories — YAML-файлы, описывающие сценарии автоматизации конфигурации серверов.
GitHub Actions: workflow-файлы
Helm: чарты Kubernetes используют YAML для шаблонов и values-файлов.
Во всех этих случаях ключевое преимущество YAML — читаемость для человека, которая критична, когда конфигурацию пишут и ревьюят разработчики и DevOps-инженеры.
Библиотека SnakeYAML
В Java-экосистеме стандартной библиотекой для работы с YAML является SnakeYAML. Она разработана сообществом и широко используется как самостоятельно, так и как транзитивная зависимость Spring Boot. На момент 2026 года актуальные версии:
SnakeYAML 2.x — классическая реализация, поддерживающая YAML 1.1 и частично YAML 1.2. Используется в Spring Boot 3.x.
SnakeYAML Engine — отдельный проект, полностью реализующий YAML 1.2. Более строгий парсер, но менее распространён в прикладной разработке.
SnakeYAML 2.x (выпущенная в 2023 году) внесла критическое изменение безопасности: по умолчанию отключена возможность десериализации произвольных Java-объектов через теги
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
Глава 3. Сериализация и форматы обмена
YAML — формат для конфигураций
YAML (YAML Ain't Markup Language) — это текстовый формат сериализации данных, ориентированный на максимальную читаемость для человека. Первая спецификация YAML 1.0 была выпущена в 2001 году, текущая актуальная версия — YAML 1.2.2 (октябрь 2021). В отличие от JSON, YAML использует отступы для обозначения структуры вместо фигурных скобок и квадратных скобок, что делает документы визуально чище и ближе к естественному языку.
Сериализация — это процесс преобразования структуры данных или объекта в формат, пригодный для хранения или передачи, с возможностью обратного восстановления (десериализации).
YAML не является языком разметки в строгом смысле: он не содержит тегов разметки текста, как HTML или XML. Вместо этого YAML — это формат представления графа данных (data graph), где узлами являются скаляры, последовательности и отображения (маппинги).
Граф данных — это абстрактная структура, состоящая из узлов (данных) и рёбер (связей между данными). В YAML узел может быть скаляром (строка, число, булево, null), последовательностью (упорядоченный список) или отображением (ассоциативный массив, пары ключ-значение).
Где используется YAML
YAML стал де-факто стандартом для конфигурационных файлов в современной инфраструктуре и разработке:
Spring Boot: файл
application.yml — альтернатива application.properties. Spring Boot использует SnakeYAML под капотом для парсинга YAML-конфигураций и маппинга их на @ConfigurationProperties.Docker Compose: файл
docker-compose.yml описывает мультиконтейнерные приложения: сервисы, сети, тома, переменные окружения.Kubernetes: манифесты ресурсов (pods, deployments, services) пишутся в YAML.
kubectl apply -f deployment.yml — стандартная операция развёртывания.Ansible: playbooks и inventories — YAML-файлы, описывающие сценарии автоматизации конфигурации серверов.
GitHub Actions: workflow-файлы
.github/workflows/ci.yml описывают пайплайны CI/CD.Helm: чарты Kubernetes используют YAML для шаблонов и values-файлов.
Во всех этих случаях ключевое преимущество YAML — читаемость для человека, которая критична, когда конфигурацию пишут и ревьюят разработчики и DevOps-инженеры.
Библиотека SnakeYAML
В Java-экосистеме стандартной библиотекой для работы с YAML является SnakeYAML. Она разработана сообществом и широко используется как самостоятельно, так и как транзитивная зависимость Spring Boot. На момент 2026 года актуальные версии:
SnakeYAML 2.x — классическая реализация, поддерживающая YAML 1.1 и частично YAML 1.2. Используется в Spring Boot 3.x.
SnakeYAML Engine — отдельный проект, полностью реализующий YAML 1.2. Более строгий парсер, но менее распространён в прикладной разработке.
SnakeYAML 2.x (выпущенная в 2023 году) внесла критическое изменение безопасности: по умолчанию отключена возможность десериализации произвольных Java-объектов через теги
!! (tag handles). Это закрыло классическую уязвимость YAML deserialization remote code execution (RCE), когда злоумышленник мог внедрить в YAML тег !!javax.script.ScriptEngineManager и выполнить произвольный код. В SnakeYAML 2.x для загрузки произвольных классов требуется явная настройка LoaderOptions.RCE (Remote Code Execution) — это класс уязвимостей, позволяющий атакующему выполнить произвольный код на целевой системе удалённо. В контексте десериализации RCE возникает, когда формат данных позволяет указать класс для инстанцирования, и этот класс имеет опасный конструктор или сеттер.
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
👍4
Чтение YAML в Java
Базовое чтение в Map
Простейший способ — загрузить YAML как вложенную структуру
Метод
Типы маппинга по умолчанию:
Отображения (маппинги) →
Последовательности →
Строки →
Целые числа →
Числа с плавающей точкой →
Булевы →
Null →
Потенциальная проблема: ClassCastException
Базовый подход с
Чтение в объекты: loadAs
SnakeYAML поддерживает маппинг YAML напрямую на Java-объекты через механизм JavaBeans. Для этого используется метод
YAML-файл конфигурации:
Чтение в объект:
SnakeYAML автоматически:
Маппит ключ
Маппит вложенный объект
Маппит YAML-последовательность на
Преобразует строковое значение
Кастомные конструкторы для сложных типов
Когда стандартного маппинга JavaBeans недостаточно — например, нужно преобразовать строку в специфический доменный объект — используются кастомные конструкторы SnakeYAML.
Использование в конфигурации:
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
Базовое чтение в Map
Простейший способ — загрузить YAML как вложенную структуру
Map и List:import org.yaml.snakeyaml.Yaml;
import java.io.InputStream;
import java.util.Map;
import java.util.List;
public class YamlReader {
public Map<String, Object> loadConfig(InputStream input) {
// Yaml — лёгкий объект, но не thread-safe
// Для многопоточного использования создавайте Yaml на каждый поток
Yaml yaml = new Yaml();
// load возвращает Object, который для корневого отображения
// приводится к Map<String, Object>
return yaml.load(input);
}
public void printNestedValue(Map<String, Object> config) {
// YAML-отображения маппятся на LinkedHashMap (сохраняет порядок ключей)
// YAML-последовательности маппятся на ArrayList
Map<String, Object> server = (Map<String, Object>) config.get("server");
Integer port = (Integer) server.get("port");
System.out.println("Port: " + port);
}
}
Метод
load(InputStream) читает YAML-документ и возвращает корневой объект. Типы маппинга по умолчанию:
Отображения (маппинги) →
LinkedHashMap<String, Object>Последовательности →
ArrayList<Object>Строки →
StringЦелые числа →
Integer или Long (в зависимости от размера)Числа с плавающей точкой →
DoubleБулевы →
BooleanNull →
nullLinkedHashMap — это реализация интерфейса Map в Java, которая, в отличие от обычного HashMap, сохраняет порядок вставки элементов. Это важно для YAML, так как порядок ключей в конфигурации часто семантически значим.
Потенциальная проблема: ClassCastException
Базовый подход с
Map<String, Object> требует постоянного приведения типов и не даёт статической типизации. Для глубоко вложенных конфигураций это приводит к громоздкому и ошибкоёмкому коду:// Многоуровневое извлечение значения без типобезопасности
Map<String, Object> db = (Map<String, Object>) config.get("database");
Map<String, Object> pool = (Map<String, Object>) db.get("connectionPool");
Integer maxSize = (Integer) pool.get("maxSize"); // ClassCastException, если тип другой
Чтение в объекты: loadAs
SnakeYAML поддерживает маппинг YAML напрямую на Java-объекты через механизм JavaBeans. Для этого используется метод
loadAs(InputStream, Class<T>).JavaBeans — это соглашение в Java, согласно которому класс должен иметь конструктор без параметров, публичные геттеры и сеттеры для свойств, и реализовывать интерфейс Serializable (опционально). SnakeYAML использует рефлексию для вызова сеттеров по именам ключей YAML.
import org.yaml.snakeyaml.Yaml;
public class LibraryConfig {
private String storagePath;
private int maxBooks;
private DatabaseConfig database;
private List<String> allowedFormats;
// Конструктор по умолчанию обязателен для SnakeYAML
public LibraryConfig() {}
// Геттеры и сеттеры — SnakeYAML вызывает сеттеры по имени ключа
public String getStoragePath() { return storagePath; }
public void setStoragePath(String storagePath) { this.storagePath = storagePath; }
public int getMaxBooks() { return maxBooks; }
public void setMaxBooks(int maxBooks) { this.maxBooks = maxBooks; }
public DatabaseConfig getDatabase() { return database; }
public void setDatabase(DatabaseConfig database) { this.database = database; }
public List<String> getAllowedFormats() { return allowedFormats; }
public void setAllowedFormats(List<String> allowedFormats) { this.allowedFormats = allowedFormats; }
// Вложенный класс конфигурации
public static class DatabaseConfig {
private String url;
private String username;
private String password;
private int connectionTimeout;
public DatabaseConfig() {}
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getConnectionTimeout() { return connectionTimeout; }
public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; }
}
}
YAML-файл конфигурации:
storagePath: /var/lib/library
maxBooks: 10000
allowedFormats:
- epub
- mobi
database:
url: jdbc:postgresql://localhost:5432/library
username: lib_admin
password: secret
connectionTimeout: 30
Чтение в объект:
public class ConfigLoader {
public LibraryConfig loadLibraryConfig(InputStream input) {
Yaml yaml = new Yaml();
// loadAs маппит YAML на объект указанного класса через JavaBeans
return yaml.loadAs(input, LibraryConfig.class);
}
}SnakeYAML автоматически:
Маппит ключ
storagePath на вызов setStoragePath()Маппит вложенный объект
database на инстанцирование LibraryConfig.DatabaseConfigМаппит YAML-последовательность на
List<String>Преобразует строковое значение
"10000" в int через соответствующий сеттерКастомные конструкторы для сложных типов
Когда стандартного маппинга JavaBeans недостаточно — например, нужно преобразовать строку в специфический доменный объект — используются кастомные конструкторы SnakeYAML.
public class CustomDurationConstructor extends Constructor {
public CustomDurationConstructor(Class<?> theRoot) {
super(theRoot);
// Регистрируем кастомный конструктор для тега !duration
this.yamlConstructors.put(new Tag("!duration"), new DurationConstruct());
}
// AbstractConstruct — базовый класс для пользовательских конструкторов узлов YAML
private class DurationConstruct extends AbstractConstruct {
@Override
public Object construct(Node node) {
// ScalarNode представляет скалярное значение (строка, число и т.д.)
String value = ((ScalarNode) node).getValue();
try {
// ISO-8601 формат: PT30S — 30 секунд, PT5M — 5 минут
return Duration.parse(value);
} catch (DateTimeParseException e) {
throw new YAMLException("Невалидный формат Duration: " + value, e);
}
}
}
}Использование в конфигурации:
storagePath: /var/lib/library
maxBooks: 10000
sessionTimeout: !duration PT30M
public class AdvancedConfig {
private Duration sessionTimeout;
public Duration getSessionTimeout() { return sessionTimeout; }
public void setSessionTimeout(Duration sessionTimeout) { this.sessionTimeout = sessionTimeout; }
}
// Загрузка с кастомным конструктором
Yaml yaml = new Yaml(new CustomDurationConstructor(AdvancedConfig.class));
AdvancedConfig config = yaml.load(input);Тег (tag) в YAML — это метка, явно указывающая тип узла. Стандартные теги: !!str , !!int, !!float, !!bool, !!null, !!map, !!seq. Пользовательские теги начинаются с ! (локальные) или !! (глобальные). Теги позволяют YAML-документу нести информацию о типах, необходимую для корректной десериализации.
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
👍4
Запись YAML из Java
Метод
Поддержка списков, вложенных объектов и многострочных строк
Списки (последовательности)
Вложенные объекты (отображения)
Многострочные строки
YAML предоставляет два оператора для многострочных строк:
Literal block scalar (
Folded block scalar (
Сравнение YAML и JSON для конфигураций
Читаемость
YAML превосходит JSON в читаемости за счёт отсутствия скобок и кавычек. Сравним одинаковую конфигурацию:
JSON:
YAML:
В YAML отсутствуют запятые после каждого элемента, нет обрамляющих скобок, строки не требуют кавычек (кроме случаев, когда значение может быть интерпретировано как другой тип — например,
Комментарии
YAML поддерживает комментарии двумя способами:
YAML не поддерживает многострочные комментарии напрямую, но можно использовать несколько строк с
JSON официально не поддерживает комментарии (RFC 8259). Некоторые парсеры допускают комментарии как расширение, но это нарушает стандарт. Для конфигураций, где комментарии критичны (объяснение значения параметра, временное отключение опции, TODO), YAML является единственным разумным выбором из двух.
Строгость и предсказуемость
JSON более строг и предсказуем: каждый документ — ровно один объект или массив, синтаксис однозначен. YAML имеет сложную спецификацию с множеством особенностей: неявная типизация (
Производительность
JSON парсится быстрее, так как грамматика проще и не требует отслеживания отступов. YAML-парсер должен вычислять отступы каждой строки, обрабатывать сложные правила скаляров и тегов. Для конфигураций, которые читаются один раз при старте приложения, эта разница несущественна. Для высокочастотного обмена данными (миллионы сообщений в секунду) JSON предпочтительнее.
Вывод
Используйте YAML для конфигураций, которые пишут и читают люди:
Используйте JSON для машинно-генерируемых данных, API-контрактов, логов и сценариев, где производительность парсинга критична.
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
Метод
dump(Object data, Writer writer) сериализует Java-объект в YAML-представление.public class YamlWriter {
public String writeConfig(LibraryConfig config) {
// DumperOptions — настройки форматирования вывода
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // блочный стиль вместо inline
options.setPrettyFlow(true); // красивые отступы
options.setIndent(2); // отступ в 2 пробела
Yaml yaml = new Yaml(options);
StringWriter writer = new StringWriter();
yaml.dump(config, writer);
return writer.toString();
}
}Flow style и block style — это два способа записи коллекций в YAML. Block style использует отступы (как Python), flow style использует JSON-подобный синтаксис со скобками. Для конфигураций block style предпочтителен из-за читаемости.
Поддержка списков, вложенных объектов и многострочных строк
Списки (последовательности)
allowedFormats:
- epub
- mobi
# Альтернативный flow-style (менее читаемый)
allowedFormats: [pdf, epub, mobi]
Вложенные объекты (отображения)
database:
url: jdbc:postgresql://localhost:5432/library
pool:
min: 5
max: 20
Многострочные строки
YAML предоставляет два оператора для многострочных строк:
Literal block scalar (
|): сохраняет переводы строки как есть.Folded block scalar (
>): заменяет одиночные переводы строки пробелами, сохраняя только пустые строки как разделители абзацев.description: |
Это многострочный текст.
Каждая строка сохраняет свой перевод.
Итоговая строка содержит \n между строками.
license: >
Это текст, который в исходном YAML
разбит на строки для читаемости,
но в результирующей строке будет
представлен одним абзацем с пробелами
вместо переводов строк.
Block scalar — это способ записи скалярных значений (строк), занимающих несколько строк, в YAML. Оператор (pipe) используется для verbatim (дословного) сохранения строк, оператор > (greater-than) — для сворачивания строк в один абзац.
Сравнение YAML и JSON для конфигураций
Читаемость
YAML превосходит JSON в читаемости за счёт отсутствия скобок и кавычек. Сравним одинаковую конфигурацию:
JSON:
{
"server": {
"port": 8080,
"ssl": {
"enabled": true,
"certificate": "/etc/ssl/cert.pem"
}
},
"features": ["auth", "logging", "metrics"]
}YAML:
server:
port: 8080
ssl:
enabled: true
certificate: /etc/ssl/cert.pem
features:
- auth
- logging
- metrics
В YAML отсутствуют запятые после каждого элемента, нет обрамляющих скобок, строки не требуют кавычек (кроме случаев, когда значение может быть интерпретировано как другой тип — например,
"true" как строка vs true как булево). Это снижает визуальный шум и упрощает ревью изменений в системах контроля версий.Комментарии
YAML поддерживает комментарии двумя способами:
# — однострочный комментарий до конца строки.YAML не поддерживает многострочные комментарии напрямую, но можно использовать несколько строк с
#.JSON официально не поддерживает комментарии (RFC 8259). Некоторые парсеры допускают комментарии как расширение, но это нарушает стандарт. Для конфигураций, где комментарии критичны (объяснение значения параметра, временное отключение опции, TODO), YAML является единственным разумным выбором из двух.
Строгость и предсказуемость
JSON более строг и предсказуем: каждый документ — ровно один объект или массив, синтаксис однозначен. YAML имеет сложную спецификацию с множеством особенностей: неявная типизация (
yes может быть интерпретировано как булево true в YAML 1.1), якоря и алиасы (&anchor и *alias), сложные правила отступов. Это делает YAML более подверженным ошибкам при ручном редактировании: лишний пробел может изменить структуру, а неявная типизация — привести к неожиданным значениям.Якорь (anchor) и алиас (alias) — это механизм YAML для повторного использования узлов. &name создаёт якорь для узла, *name создаёт ссылку (алиас) на этот узел. Это позволяет избежать дублирования, но усложняет документ.
Производительность
JSON парсится быстрее, так как грамматика проще и не требует отслеживания отступов. YAML-парсер должен вычислять отступы каждой строки, обрабатывать сложные правила скаляров и тегов. Для конфигураций, которые читаются один раз при старте приложения, эта разница несущественна. Для высокочастотного обмена данными (миллионы сообщений в секунду) JSON предпочтительнее.
Вывод
Используйте YAML для конфигураций, которые пишут и читают люди:
application.yml, CI/CD пайплайны, Kubernetes-манифесты.Используйте JSON для машинно-генерируемых данных, API-контрактов, логов и сценариев, где производительность парсинга критична.
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
👍4
Путь байтов в памяти JVM при работе с YAML
1. Загрузка YAML-файла
Когда приложение читает
Файл считывается через
SnakeYAML использует
Парсер SnakeYAML читает символы и строит внутреннее представление документа — дерево узлов (Node graph). Каждый узел YAML (скаляр, последовательность, отображение) представлен объектом в куче:
2. Маппинг на Java-объекты
После построения дерева узлов начинается фаза конструирования:
Для каждого ключа отображения
Значения узлов преобразуются в целевые типы: строки остаются строками, числа конвертируются через
Промежуточные структуры — дерево узлов, массивы символов, объекты рефлексии — становятся мусором после завершения конструирования. Если конфигурация читается один раз при старте приложения, все эти объекты собираются при первой Minor GC после инициализации.
3. Работа GC с конфигурационными объектами
Объект конфигурации (например,
Промежуточные объекты парсинга (дерево узлов,
4. Потенциальная утечка: кэш классов SnakeYAML
SnakeYAML кэширует
5. Запись YAML: обратный путь
При сериализации Java-объекта в YAML:
Байты уходят в файловую систему или сетевой сокет через системные вызовы ОС.
Все промежуточные структуры (дерево узлов, буферы символов) создаются в Young Generation и собираются при следующей Minor GC.
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
1. Загрузка YAML-файла
Когда приложение читает
application.yml из classpath или файловой системы, данные проходят следующий путь:Файл считывается через
InputStream — байты передаются из кэша страниц ОС через нативную память в буфер byte[] в куче JVM (аналогично Files.readAllBytes(), описанному в предыдущих уроках).SnakeYAML использует
Reader (обычно InputStreamReader с кодировкой UTF-8) для декодирования байтов в символы char[]. Этот массив создаётся в Young Generation, в Eden.Парсер SnakeYAML читает символы и строит внутреннее представление документа — дерево узлов (Node graph). Каждый узел YAML (скаляр, последовательность, отображение) представлен объектом в куче:
ScalarNode, MappingNode, SequenceNode. Для конфигурации размером 5 КБ количество узлов может достигать нескольких десятков.Node graph — это промежуточное представление YAML-документа в памяти парсера перед маппингом на Java-объекты. Оно отражает структуру документа независимо от целевого типа данных.
2. Маппинг на Java-объекты
После построения дерева узлов начинается фаза конструирования:
Constructor обходит дерево узлов. Для каждого MappingNode он создаёт целевой Java-объект через рефлексию: вызов Class.newInstance() (или Constructor.newInstance() в современных версиях) аллоцирует объект в куче.Для каждого ключа отображения
Constructor ищет соответствующий сеттер через Introspector (механизм JavaBeans introspection). Introspector анализирует класс и кэширует PropertyDescriptor — дескрипторы свойств. Эти дескрипторы создаются при первом обращении к классу и хранятся в кэше, но первое использование класса порождает множество временных объектов рефлексии.Introspection (интроспекция) — это механизм Java, позволяющий во время выполнения анализировать структуру классов: получать список методов, полей, конструкторов, аннотаций. java.beans.Introspector — стандартный класс для JavaBeans-интроспекции.
Значения узлов преобразуются в целевые типы: строки остаются строками, числа конвертируются через
Integer.parseInt() или аналогичные методы, вложенные отображения рекурсивно конструируются.Промежуточные структуры — дерево узлов, массивы символов, объекты рефлексии — становятся мусором после завершения конструирования. Если конфигурация читается один раз при старте приложения, все эти объекты собираются при первой Minor GC после инициализации.
3. Работа GC с конфигурационными объектами
Объект конфигурации (например,
LibraryConfig) обычно сохраняется в Old Generation (Tenured), так как он живёт всё время жизни приложения. Ссылки на него хранятся в контексте Spring (singleton-бины) или в статических полях.Singleton — это паттерн проектирования, гарантирующий, что у класса есть только один экземпляр, и предоставляющий глобальную точку доступа к нему. В Spring Boot все бины по умолчанию — singleton.
Промежуточные объекты парсинга (дерево узлов,
char[] исходного файла, временные объекты рефлексии) создаются в Young Generation и уничтожаются Minor GC. Важно, что SnakeYAML не использует пул строк для ключей YAML — каждый ключ создаётся как новый объект String в куче. При большом количестве ключей это создаёт давление на Eden, но так как конфигурация читается редко, это не критично.4. Потенциальная утечка: кэш классов SnakeYAML
SnakeYAML кэширует
TypeDescription и конструкторы классов во внутренних Map. Эти кэши растут по мере обработки новых классов. В долгоживущих приложениях, которые динамически загружают множество различных YAML-схем, кэш может занимать значительный объём Old Generation. Это не утечка в классическом смысле (кэш необходим), но требует мониторинга через heap dump.Heap dump — это снимок всей кучи JVM в определённый момент времени. Он содержит все объекты, их размеры, ссылки между ними и классы. Анализ heap dump позволяет находить утечки памяти и понимать распределение объектов по поколениям.
5. Запись YAML: обратный путь
При сериализации Java-объекта в YAML:
Representer обходит поля объекта и строит дерево узлов в памяти (аллокации в Young Generation).Emitter преобразует дерево узлов в поток символов char[].Writer кодирует символы в UTF-8 байты и записывает в OutputStream.Байты уходят в файловую систему или сетевой сокет через системные вызовы ОС.
Все промежуточные структуры (дерево узлов, буферы символов) создаются в Young Generation и собираются при следующей Minor GC.
#Java #для_новичков #beginner #IO #NIO #Serialize #YAML
👍4
Всем привет!
Запустил тестово нового бота @Review_Trainer_Bot. Если интересно - можете пощупать руками)))
Запустил тестово нового бота @Review_Trainer_Bot. Если интересно - можете пощупать руками)))
🔥5
Что выведет код?
#Tasks
import org.yaml.snakeyaml.Yaml;
import java.util.Map;
public class Task060826 {
public static void main(String[] args) {
String yaml = "enabled: on\nvalue: 010";
Yaml yamlParser = new Yaml();
Map<String, Object> data = yamlParser.load(yaml);
System.out.println(data.get("enabled") + ", " + data.get("value"));
}
}
#Tasks
👍2
👍2