В предыдущей статье Все, что Вам нужно знать о сериализации Java , мы обсудили, как сериализуемость класса обеспечивается за счет реализации Сериализуемого
интерфейса. Если наш класс не реализует Сериализуемый
интерфейс или если он содержит ссылку на класс, не Сериализуемый
, то JVM выдаст исключение NotSerializableException
.
Все подтипы сериализуемого класса сами по себе сериализуемы, и Экстернализуемый
интерфейс также расширяет сериализуемый. Таким образом, даже если мы настроим наш процесс сериализации с помощью Externalizable наш класс все равно будет Сериализуемым
.
Сериализуемый
интерфейс – это интерфейс маркера, в котором нет методов или полей, и он работает как флаг для JVM. Процесс сериализации Java, предоставляемый ObjectInputStream
и ObjectOutputStream
классы полностью контролируются JVM.
Но что, если мы захотим добавить некоторую дополнительную логику для улучшения этого обычного процесса, например, мы можем захотеть зашифровать/расшифровать нашу конфиденциальную информацию перед ее сериализацией/десериализацией. Java предоставляет нам некоторые дополнительные методы для этой цели, которые мы собираемся обсудить в этом блоге.
Методы writeObject и readObject
Сериализуемые классы, которые хотят настроить или добавить некоторую дополнительную логику для улучшения обычного процесса сериализации/десериализации, должны предоставить writeObject
и readObject
методы с этими точными сигнатурами:
-
частный пустой объект записи(java.io . ObjectOutputStream out) вызывает исключение IOException
-
частный пустой объект чтения(java.io . ObjectInputStream в) вызывает исключение IOException, Исключение ClassNotFoundException
Эти методы уже подробно обсуждаются в статье Все, что Вам Нужно Знать О Сериализации Java .
Метод readObjectNoData для чтения объектов
Как описано в документах Java класса Сериализуемый
, если мы хотим инициализировать состояние объекта для его конкретного класса в случае, если поток сериализации не перечисляет данный класс в качестве суперкласса десериализуемого объекта, мы должны предоставить writeObject
и readObject
методы с этими точными сигнатурами:
-
private void readObjectNoData() вызывает исключение ObjectStreamException
Это может произойти в случаях, когда принимающая сторона использует другую версию десериализованного класса экземпляра, чем отправляющая сторона, и версия получателя расширяет классы, которые не расширяются версией отправителя. Это также может произойти, если поток сериализации был изменен; следовательно, readObjectNoData полезен для правильной инициализации десериализованных объектов, несмотря на “враждебный” или неполный исходный поток.
Каждый сериализуемый класс может определять свой собственный метод readObjectNoData
. Если сериализуемый класс не определяет метод readObjectNoData
, то в перечисленных выше обстоятельствах поля класса будут инициализированы значениями по умолчанию.
Методы writeReplace и readResolve
Сериализуемые классы, которым необходимо назначить альтернативный объект, который будет использоваться при записи объекта в поток, должны предоставить этому специальному методу точную подпись:
-
Объект С ЛЮБЫМ МОДИФИКАТОРОМ ДОСТУПА writeReplace() вызывает исключение ObjectStreamException
И сериализуемые классы, которым необходимо назначить замену, когда ее экземпляр считывается из потока, должны предоставить этому специальному методу точную подпись:
-
Объект С ЛЮБЫМ МОДИФИКАТОРОМ ДОСТУПА readResolve() вызывает исключение ObjectStreamException
По сути, метод writeReplace
позволяет разработчику предоставить заменяющий объект, который будет сериализован вместо исходного. И метод readResolve
используется в процессе десериализации для замены десериализованного объекта другим из наших вариантов.
Одним из основных применений методов writeReplace и readResolve является реализация одноэлементного шаблона проектирования с сериализованными классами. Мы знаем, что процесс десериализации каждый раз создает новый объект и его также можно использовать в качестве метода для глубокого клонирования объекта , что нехорошо, если нам нужно сделать наш класс одноэлементным.
Вы можете прочитать больше о клонировании и сериализации Java на Темы клонирования Java и Сериализации Java .
Метод readResolve
вызывается после Объект чтения
вернулся (и наоборот Место записи
вызывается до writeObject
и, возможно, на другом объекте). Объект, возвращаемый методом, заменяет этот
объект, возвращенный пользователю ObjectInputStream.readObject
и любые дальнейшие обратные ссылки на объект в потоке. Мы можем использовать метод writeReplace для замены сериализующего объекта на null, чтобы ничего не было сериализовано, а затем использовать метод readResolve для замены десериализованного объекта экземпляром singleton.
Метод validateObject
Если мы хотим выполнить определенные проверки для некоторых наших полей, мы можем сделать это, реализовав интерфейс ObjectInputValidation
и переопределив в нем метод validateobject
.
Метод проверка объекта
будет автоматически вызван, когда мы зарегистрируем эту проверку, вызвав ObjectInputStream.registerValidation(это, 0)
из объекта чтения
метод. Очень полезно убедиться, что поток не был изменен или что данные имеют смысл, прежде чем передавать их обратно в ваше приложение.
Приведенный ниже пример охватывает код для всех вышеперечисленных методов
public class SerializationMethodsExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Employee emp = new Employee("Naresh Joshi", 25); System.out.println("Object before serialization: " + emp.toString()); // Serialization serialize(emp); // Deserialization Employee deserialisedEmp = deserialize(); System.out.println("Object after deserialization: " + deserialisedEmp.toString()); System.out.println(); // This will print false because both object are separate System.out.println(emp == deserialisedEmp); System.out.println(); // This will print false because both `deserialisedEmp` and `emp` are pointing to same object, // Because we replaced de-serializing object in readResolve method by current instance System.out.println(Objects.equals(emp, deserialisedEmp)); } // Serialization code static void serialize(Employee empObj) throws IOException { try (FileOutputStream fos = new FileOutputStream("data.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(empObj); } } // Deserialization code static Employee deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("data.obj"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Employee) ois.readObject(); } } } class Employee implements Serializable, ObjectInputValidation { private static final long serialVersionUID = 2L; private String name; private int age; public Employee(String name, int age) { this.name = name; this.age = age; } // With ObjectInputValidation interface we get a validateObject method where we can do our validations. @Override public void validateObject() { System.out.println("Validating age."); if (age < 18 || age > 70) { throw new IllegalArgumentException("Not a valid age to create an employee"); } } // Custom serialization logic, // This will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization. private void writeObject(ObjectOutputStream oos) throws IOException { System.out.println("Custom serialization logic invoked."); oos.defaultWriteObject(); // Calling the default serialization logic } // Replacing de-serializing object with this, private Object writeReplace() throws ObjectStreamException { System.out.println("Replacing serialising object by this."); return this; } // Custom deserialization logic // This will allow us to have additional deserialization logic on top of the default one e.g. performing validations, decrypting object after deserialization. private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println("Custom deserialization logic invoked."); ois.registerValidation(this, 0); // Registering validations, So our validateObject method can be called. ois.defaultReadObject(); // Calling the default deserialization logic. } // Replacing de-serializing object with this, // It will will not give us a full proof singleton but it will stop new object creation by deserialization. private Object readResolve() throws ObjectStreamException { System.out.println("Replacing de-serializing object by this."); return this; } @Override public String toString() { return String.format("Employee {name='%s', age='%s'}", name, age); } }
Вы можете найти полный исходный код этой статьи в этом репозитории Github и, пожалуйста, не стесняйтесь оставлять свои ценные отзывы.
Оригинал: “https://dev.to/njnareshjoshi/java-serialization-magic-methods-and-their-uses-with-example-4beo”