Serialization and deserialization
Используя сериализацию объектов, мы сможем разложить свои объекты на последовательность байтов и затем использовать их наиболее эффективным образом (обычно используется в Hibernate, JMS, JPA и EJB). Для чего? Чтобы создаваемые нами объекты могли существовать за пределами жизненного цикла виртуальной машины.
Десериализация - это процесс обратного преобразования потока данных байтового типа в объект в памяти.
Существует три способа выполнения сериализации {Используя протокол по умолчанию, Изменяя протокол по умолчанию, Создавая собственный протокол}.
В данной статье рассмотрим первый способ.
Для того, чтобы отметить наш объект, как сериализумый, мы должны реализовать интерфейс java.io.Serializable, что и будет являться для API знаком того, что объект может быть разложен на байты и затем вновь восстановлен.
Интерфейс Serializable является лишь маркерным интерфейсом и позволяет механизму сериализации определить возможность сохранения данного класса.
Класс, на котором будем демонстрировать примеры, выглядит следующим образом:
import lombok.Getter; import java.util.Calendar; import java.util.Date; import java.io.Serializable; @Getter public class PersistentTime implements Serializable { private Date time; public PersistentTime() { time = Calendar.getInstance().getTime(); }}
Механизм используемый по умолчанию:
Далее, что нужно для сохранения объекта? Сохранения выполняется при помощи класса java.io.ObjectOutputStream. Этот класс является фильтрующим потоком, он окружает низкоуровневый поток байтов(называемый узловым потоком(node stream))(?) и предоставляет нам поток сериализации. Узловые потоки могут быть использованы для записи в файловую систему или в сокеты. Это позволяет нам передавать разложенные на байты объекты по сети и затем восстанавливать их на других устройствах.
Посмотрим код, который демонстрирует процесс сериализации:
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializationProcess { public static void main(String[] args) { String fileName = "time.ser"; if(args.length > 0){ fileName = args[0]; } PersistentTime time = new PersistentTime(); FileOutputStream fileOutputStream = null; ObjectOutputStream objectOutputStream = null; try { fileOutputStream = new FileOutputStream(fileName); objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(time); objectOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } }}
Метод writeObject() - запускает механизм сериализации и объект разлагается на байты (в данном случай файл).
Теперь стоит посмотреть, на код, выполняющий процесс десириализации:
public class DeserializationProcess { public static void main(String[] args) { String fileName = "time.ser"; if(args.length > 0){ fileName = args[0]; } PersistentTime time = null; FileInputStream fileInputStream = null; ObjectInputStream objectInputStream = null; try { fileInputStream = new FileInputStream(fileName); objectInputStream = new ObjectInputStream(fileInputStream); time = (PersistentTime) objectInputStream.readObject(); objectInputStream.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } System.out.println("Время разложения: " + time.getTime()); System.out.println("Текущее время: " + Calendar.getInstance().getTime()); }} Консоль: Время разложения: Sun Sep 17 21:27:42 YEKT 2023 Текущее время: Sun Sep 17 21:39:53 YEKT 2023
Метод readObject() - восстанавливает объект. Метод считывает последовательность байтов, которую мы перед этим сохранили в файле и создает объект, полностью повторяющий оригинал. Поскольку метод считывает любой сериализуемый объект, необходимо его присвоить соответствующему типу. Из системы, в которой происходит десериализация, должен быть доступен файл класса (при сериализации не сохраняется ни файл класса, ни его методы, сохраняется лишь состояние! объекта).
Основные правила:
1. Сохраняемый объект должен реализовать интерфейс Serializable или унаследовать эту реализацию от вышестоящего по иерархии объекта.
2. Вы должны помечать как transient все поля, которые либо не могут быть сериализованы, либо те, которые вы не хотите сериализовать. Сериализация не заботится о модификаторах доступа, таких как private. Все резидентные поля рассматриваются как части состояния сохраняемого объекта, предназначенные для сохранения.
Что такое Hibernate, JMS, JPA и EJB? Hibernate — это популярный framework, цель которого связать ООП и реляционную базу данных.
JMS (Java Message Service) является стандартом обмена сообщениями между приложениями.
Java Persistence API (JPA) — спецификация API Java EE, предоставляет возможность сохранять в удобном виде Java-объекты в базе данных.
Enterprise JavaBeans (также часто употребляется в виде аббревиатуры EJB) — спецификация технологии написания и поддержки серверных компонентов, содержащих бизнес-логику. Является частью Java EE.
Что такое маркерный интерфейс? Маркерный интерфейс — это интерфейс , внутри которого нет методов или констант . Он предоставляет информацию о типах объектов во время выполнения, поэтому компилятор и JVM имеют дополнительную информацию об объекте.
Что такое сокеты? Сокет — это виртуальная конструкция из IP-адреса и номера порта. Её придумали для того, чтобы разработчикам было проще писать код, а программы могли передавать данные друг другу даже в пределах одного компьютера. Сокеты используют для двух вещей: для передачи данных по сети и для связи между приложениями.
Что такое файловая система? Благодаря файловой системе мы сохраняем всю необходимую нам информацию на своих устройствах. Есть различные типы подобных накопителей. От особенностей того или иного вида зависят способ шифрования данных, объем их сжатия, а также качество самого хранения. Наиболее распространенные файловые системы – это FAT32 и NTFS.
Полезные ссылки:
http://www.ccfit.nsu.ru/~deviv/courses/oop/java_ser_rus.html
https://www.simplilearn.com/tutorials/java-tutorial/serialization-in-java
https://metanit.com/java/tutorial/6.10.php