Библиотека Java разработчика
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
Show more📈 Analytical overview of Telegram channel Библиотека Java разработчика
Channel Библиотека Java разработчика (@bookjava) in the Russian language segment is an active participant. Currently, the community unites 10 265 subscribers, ranking 12 016 in the Technologies & Applications category and 63 847 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 10 265 subscribers.
According to the latest data from 13 June, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by 13 over the last 30 days and by 3 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 8.59%. Within the first 24 hours after publication, content typically collects 4.32% reactions from the total number of subscribers.
- Post reach: On average, each post receives 882 views. Within the first day, a publication typically gains 443 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 7.
- Thematic interests: Content is focused on key topics such as string, интерфейс, строка, boot, api.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.
По всем вопросам @evgenycarter
РКН clck.ru/3KoGeP”
Thanks to the high frequency of updates (latest data received on 14 June, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
Serializable;
5. В методе writeReplace оригинального класса инстанциируется и возвращается прокси от this;
6. Симметричные действия совершаются для чтения – в классе-посреднике реализуется метод readResolve;
7. В основном классе добавляется readObject, который выбрасывает исключение – это защитит от чтения без прокси.
Пример кода реализации.
#СериализацияSerializable класса весь контроль над сериализацией достается JVM. С помощью определения специальных методов можно кастомизировать его части. Метод readObject при этом обычно начинается с вызова стандартной части сериализации – ObjectInputStream.defaultReadObject().
Интерфейс Externalizable расширяет Serializable и добавляет методы записи и чтения writeExternal и readExternal. Входной и выходной потоки-аргументы в них представлены более абстрактно чем в специальных методах – интерфейсами ObjectInput и ObjectOutput.
Этот интерфейс позволяет реализовать полностью свой механизм сериализации, стандартно запишется только идентификатор класса. Никакой автоматической работы с классом-родителем также не предусмотрено. Методы readObject и writeObject игнорируются. Ключевое слово transient эффекта на Externalizable не имеет.
Externalizable объект в отличие от Serializable десерализуется не в обход конструктора, так что должен иметь конструктор без аргументов.
Подробнее читайте в соответствующем вопросе на stackoverflow.
#СериализацияSerializable класс имеет цепочку родителей, пока эти родители тоже Serializable, десериализация объекта идет от родителя к наследнику, в обход конструктора. Вместо него вызываются методы readObject (readObjectNoData). Но как только встречается первый предок, не реализующий интерфейс Serializable, инициализация для него возвращается в нормальное русло – вместо readObject вызывается конструктор без аргументов. Если такого конструктора нет, или он объявлен private, исполнение выбросит InvalidClassException.
При сериализации несериализуемые предки просто игнорируются.
Если класс несериализуемый и не предоставляет достаточного доступа к своему логическому состоянию для наследников, правильно реализовать его наследника сериализуемым может быть невозможно.
Популярный вопрос на тему – как когда сериализуешь объект класса-наследника, избежать сериализации его родительской части. Единственный способ добиться этого – кастомизировать сериализационную форму, определив собственную реализацию writeObject(), либо используя интерфейс Externalizable.
Открытость класса для наследования делает неприменимым паттерн serialization proxy (который рассмотрим позднее).
#СериализацияSerializable пуст, но есть ряд методов, добавив которые в сериализуемый класс можно добиться изменения этапов процесса сериализации и десериализации.
readObjectNoData() используется для инициализации класса-родителя, который при сериализации оригинального объекта еще не был родителем. Подробное объяснение.
readObject(ObjectInputStream s) переопределяет стандартную десериализацию.
Методы readObject* похожи на конструктор. Как и конструктор, они подвержены проблеме виртуального вызова. Как и конструктор, они используются для поддержания инвариантов класса (только для случая десериализации).
writeObject(ObjectOutputStream s) используется для записи собственной сериализационной формы.
Object readResolve() может использоваться для реализации какого-либо порождающего паттерна при десериализации. Даже при его использовании объект сначала будет десериализован, поэтому рекомендуется вместе с этим методом помечать все поля transient. Видимость этого метода для наследника определяет, будет ли наследник вызывать его.
Для подмены объекта при записи добавляется симметричный метод Object writeReplace().
Интерфейс Externalizable дает инструмент полной подмены реализации сериализации. Рассмотрим его в следующих постах.
#СериализацияserialVersionUID. Это число типа long, которое представляет собой «версию» сериализационной формы класса. Если при сериализации/десериализации значения serialVersionUID не совпадают – будет выброшено InvalidClassException.
Для совпадающих версий работает мощная поддержка эволюции класса – совместимые изменения, такие как добавление или удаление полей, не приводят к InvalidClassException.
Неявное значение вычисляется автоматически в рантайме, и включает в себя информацию о имени типа, списке родителей и полей (с точностью до коллизии). По смыслу это похоже на хэш-сумму класса. Соответственно, при любом изменении класса значение изменится, и поддержка эволюции окажется бесполезной.
Всегда лучше явно указывать любое значение serialVersionUID, и изменять только в тех редких случаях, когда требуется сломать совместимость с предыдущими версиями. Стандартная утилита JDK serialver умеет «угадывать» авто-генерированное значение. Она используется чтобы зафиксировать значение для включения поддержки эволюции созданного ранее класса.
Явное значение устанавливается в переменную static final long serialVersionUID.
#СериализацияSerializable (Externalizable), и цель записи/источник чтения ObjectInputStream/ObjectOutputStream (ObjectInput/ObjectOutput).
Класс сериализуем, если:
🔘 Реализует маркерный интерфейс Serializable;
🔘 Все поля сериализуемые или помечены модификатором transient (иначе рантайм выбросит NotSerializableException).
Протокол стандартной сериализации описан в документации.
Сериализационная форма может, и в некоторых случаях для корректной работы должна быть кастомизирована. Правильная форма содержит только логическое представление данных, без деталей о физической реализации.
Для описания полей сериализационной формы в javadoc-документации используется тэг @serial. Для документации генерирующего нестандартную сериализационную форму метода используется @serialData. Эти тэги имеют смысл и для приватных членов, так как эффективно такие члены – часть публичного API.
Нестатические внутренние классы не должны быть сериализуемыми. Статические поля как поля класса а не инстанса несериализуемы.
Сериализации посвящен целый раздел Effective Java. Доклад для ознакомления с темой.
#Сериализацияchar[] value и int hash. В поле hash кэшируется хэш-сумма при первом подсчете для соблюдения контракта метода hashCode.
До Java 7 были еще поля offset и count – чтобы переиспользовать без пересоздания массивы из билдеров и других строк. В современной джаве от этого отказались в угоду меньшего потребления памяти.
Изначально все строки хранились в кодировке UTF-16, каждый символ занимал по два байта и умещался в char. Однако выяснилось, что статистически большинство строк содержит только ASCII-символы, которые вмещаются в один байт и составляют кодировку LATIN-1. То есть старший байт в большинстве char остается нулевым, и строки наполовину состоят из пустоты. А примерно четверть памяти приложений состоит из одних только строк.
В Java 6 была введена экспериментальная фича Compressed Strings – способность строки хранить строки в LATIN-1 в массиве byte вместо char. С этой фичей был ряд проблем, и позднее ее убрали.
Снова сжатие строк появилось в Java 9 – теперь оно называется Compact Strings и включено по умолчанию. В классе String появилось поле coder, которое переключает кодировки, и статический флаг COMPACT_STRINGS, выключающий фичу вообще. Тип массива value окончательно изменился с char[] на byte[].
Для полного обзора особенностей класса String стоит посмотреть доклады Алексея Шипилёва (1, 2).
#Строкиchar. Значит, корректный ответ нужно начать с уточнения: подразумевается ли обход значений char или code point.
Из этого следует, что и длина строки, верхняя граница итерации – тоже понятие неоднозначное. Метод length() на самом деле возвращает не количество Unicode-символов, а количество значений char. Реальную логическую длину (количество кодовых точек) покажет метод codePointCount(). Нужно помнить, что чтобы обнаружить суррогатные пары, внутри ему придется проитерировать по символам.
Итерироваться по char можно либо получая по одному символу с помощью метода charAt(), либо со всем массивом сразу, методом toCharArray(). В случае с массивом создается копия внутреннего значения – это естественно медленнее, зато этот массив-копию можно мутировать. Работать с суррогатными парами придется вручную, для этого в классе Character есть специальные методы.
Другой вариант – сразу оперировать методами codePointAt() и codePointBefore(). Параметром указываются индексы массива char, но результатом будут кодовые точки типа int.
В Java 8 появился новый удобный способ обхода – методы chars() и codePoints(). Они возвращают IntStream из символов и кодовых точек соответственно. Носителем результирующих стримов выступает оригинальное внутреннее значение строки, копирование массива не требуется.
#Строки
Available now! Telegram Research 2025 — the year's key insights 
