Java предоставляет механизм, называемый сериализацией объектов, в котором объект может быть представлен в виде последовательности байтов, которая включает в себя данные об объекте, а также информацию о типе объекта и типах данных, хранящихся в объекте.

После того, как сериализованный объект был записан в файл, он может быть прочтен из файла и десериализован, то есть информацию о типе и байты, которые представляют объект и его данные, можно использовать для воссоздания объекта в памяти.

Больше всего впечатляет то, что весь процесс независим от JVM (виртуальной машины Java), то есть объект может быть сериализован на одной платформе и десериализован на совершенно другой платформе.

Классы ObjectInputStream (поток входных данных объекта) и ObjectOutputStream (поток выходных данных объекта) - это высокоуровневые потоки, которые содержат методы для осуществления сериализации и десериализации объекта.

Класс ObjectOutputStream содержит много методов записи для осуществления записи различных типов данных, но среди них в особенности выделяется один метод:

public final void writeObject(Object x) throws IOException

Вышеуказанный метод осуществляет сериализацию Объекта и отправляет его в поток выходных данных. Аналогично, класс ObjectInputStream содержит следующий метод осуществления десериализации объекта:

public final Object readObject() throws IOException, ClassNotFoundException

Этим методом извлекается следующий Объект из потока данных и осуществляется его десериализация. Возвращенное значение - это Объект, поэтому необходимо привести его к соответствующему типу данных.

Чтобы продемонстрировать, каким образом сериализация работает в Java, я собираюсь использовать класс Employee (сотрудник), который обсуждался в начале книги. Предположим, что мы располагаем следующим классом Employee, который внедряет сериализируемый интерфейс.

Пример

public class Employee implements java.io.Serializable {
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   
   public void mailCheck() {
      System.out.println("Отправка чека на " + name + " " + address);
   }
}

Обратите внимание, что для успешного выполнения сериализации класса должны быть выполнены два условия:

  • Класс должен реализовывать интерфейс java.io.Serializable.
  • Все поля в классе должны быть сериализуемыми. Если поле не сериализуемо, оно должно быть помечено как промежуточное.

Если вам интересно узнать, можно ли выполнить сериализацию стандартного класса Java, проверьте документацию по данному классу. Тест прост: если класс реализует java.io.Serializable, то он может быть подвергнут сериализации; в противном случае - не может.

Сериализация объекта

Класс ObjectOutputStream используется для выполнения сериализации объекта. Следующая программа SerializeDemo создает экземпляр объекта Employee и сериализует его в файл.

По завершению выполнения программы создается файл с именем employee.ser. Программа не генерирует никаких выходных данных, но изучает код и пытается определить, что совершает программа.

Примечание: в Java при сериализации объекта в файл установленным архитектурным требованием Java является присвоение файлу расширения .ser.

Пример

import java.io.*;
public class SerializeDemo {

   public static void main(String [] args) {
      Employee e = new Employee();
      e.name = "Анастасия Крот";
      e.address = "Москва, Россия";
      e.SSN = 11122333;
      e.number = 101;
      
      try {
         FileOutputStream fileOut =
         new FileOutputStream("/tmp/employee.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(e);
         out.close();
         fileOut.close();
         System.out.printf("Сериализованные данные сохраняются в /tmp/employee.ser");
      } catch (IOException i) {
         i.printStackTrace();
      }
   }
}

Десериализация объекта

Следующая программа DeserializeDemo выполняет в Java десериализацию объекта Employee, созданного в программе SerializeDemo. Изучите программу и попытайтесь определить ее выводимые данные.

Пример

import java.io.*;
public class DeserializeDemo {

   public static void main(String [] args) {
      Employee e = null;
      try {
         FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      } catch (IOException i) {
         i.printStackTrace();
         return;
      } catch (ClassNotFoundException c) {
         System.out.println("Класс Employee не найден");
         c.printStackTrace();
         return;
      }
      
      System.out.println("Десериализованный Employee...");
      System.out.println("Имя: " + e.name);
      System.out.println("Адрес: " + e.address);
      System.out.println("SSN: " + e.SSN);
      System.out.println("Номер: " + e.number);
   }
}

Это даст следующий результат:

Десериализованный Employee...
Имя: Анастасия Крот
Адрес: Москва, Россия
SSN: 0
Номер: 101

Следующие важные моменты, которые следует отметить, представлены ниже:

  • Блок попытка-перехват пытается перехватить исключение ClassNotFoundException (класс не найден исключение), описываемый методом readObject() (чтение Объекта()). Чтобы JVM могла провести десериализацию объекта, она должна найти байт-код для класса. Если JVM не может найти класс во время выполнения десериализации объекта, она генерирует исключение ClassNotFoundException.
  • Обратите внимание, что возвращаемое значение readObject() приведено к ссылке Employee.
  • Значение поля SSN было 11122333, когда объект был сериализован, но поскольку поле является промежуточным, это значение не было отправлено в поток выходных данных. Поле SSN десериализованного объекта Employee равно 0.

Оглавление