Java. Сериализация объектов. Ключевое слово transient. Примеры




Сериализация объектов. Ключевое слово transient. Примеры


Содержание


Поиск на других ресурсах:

1. Что такое сериализация? Преимущества сериализации

Язык программирования Java имеет мощные средства для обеспечения механизма сериализации. Сериализация – возможность объекта сохранять собственные данные (состояние объекта) в байтовом потоке с целью дальнейшего восстановления этих данных (состояния) при следующих запусках программы. Другими словами, текущие значения внутренних переменных в объекте можно сохранить (например, в файл), а затем восстановить в нужный момент.

Механизм сериализации дает следующие преимущества:

  • объект, для которого применяется сериализация, существует между запусками программы. Этот механизм называется легковесным долгосрочным сохранением объектов (lightweight persistence). Объект сначала сохраняется на диске, затем восстанавливается в начальное состояние при следующем запуске программы;
  • обеспечивается удаленный вызов методов Java, что позволяет работать с объектами, которые «размещаются» на удаленных компьютерах так, если бы они существовали на вашем компьютере;
  • необходимость сохранения текущей информации о состоянии визуальных компонент JavaBean;
  • использование в объектах классов, которые требуют сохранения текущего состояния.

 

2. Реализация сериализации в программах на Java

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

class MySerializeClass implements Serializable {
    ...
}

так же класс, экземпляр которого сериализуется также должен реализовать интерфейс Serializable:

class MyDataClass implements Serializable {
    ...
}

 

2.1. Запись объекта в файл

Если создан объект dataObj некоторого класса DataClass, то чтобы сохранить этот объект нужно выполнить следующие действия.

1. Создать исходный поток fOut типа OutputStream. В случае сохранения объекта в файле создается исходный поток типа FileOutputStream

FileOutputStream fOut = new FileOutputStream(filename);

здесь filename – имя файла, в котором будет сохранен объект.

Класс FileOutputStream есть подклассом абстрактного класса OutputStream.

2. Упаковать файловый объект fOut в другой объект типа ObjectOutputStream

ObjectOutputStream objOut = new ObjectOutputStream(fOut);

В результате создается поток objOut.

3. Записать экземпляр dataObj в поток objOut. Для этого используется встроенный метод writeObject()

objOut.writeObject(dataObj);

4. Закрыть оба созданных потока

objOut.close();
fOut.close();

 

2.2. Чтение объекта из файла

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

1. Создать экземпляр fInput класса FileInputStream

FileInputStream fInput = new FileInputStream(filename);

здесь filename – имя файла (тип String), в котором предварительно был сохранен объект.

2. Создать экземпляр objInput класса ObjectInputStream, в который упаковать экземпляр fInput класса FileInputStream

ObjectInputStream objInput = new ObjectInputStream(fInput);

3. Прочитать экземпляр с помощью метода readObject(). Например, если нужно прочитать экземпляр класса DataClass, то код чтения будет следующим

DataClass dObj = (DataClass)objInput.readObject();

4. Закрыть потоки fInput и objInput

objInput.close();
fInput.close();

 

3. Какое назначение модификатора transient?

Модификатор transient используется, если возникает необходимость сохранить объект (экземпляр) класса (выполнить сериализацию). Модификатор transient устанавливается перед объявлением внутренней переменной класса, значение которой не должно сохраняться в случае сохранения объекта. С помощью модификатора transient можно программно указать данные, которые не нужно сохранять при сериализации объекта.

Например. В нижеследующем классе Book переменная price объявлена с модификатором transient. Это значит, что при записи в файл любого экземпляра класса Book, записываться будут значения переменных title, author, year кроме переменной price.

class Book {
  String title;
  String author;
  int year;
  transient double price; // Эта переменная сохраняться не будет
}

 

4. Пример сериализации и использования модификатора transient. Классы ObjectInputStream и ObjectOutputStream

В примере объявляется два класса:

  • класс DataClass, содержащий данные, которые будут сохранены. Сохраняется экземпляр этого класса;
  • класс SerializeClass, который содержит методы записи экземпляра типа DataClass.

Оба класса реализуют интерфейс Serializable.

В классе DataClass объявляются:

  • внутренние переменные a, b. Переменная b объявлена как transient-переменная. Это означает, что при записи в файл экземпляра типа DataClass, значение переменной b записываться не будет;
  • конструктор DataClass(int, int);
  • методы доступа к внутренним переменным GetA(), GetB(), SetA(), SetB();
  • метод Print(), выводящий значение внутренних переменных.

В классе SerializeClass объявляются:

  • метод SaveData(), реализующий запись экземпляра типа DataClass в файл;
  • метод ReadData(), возвращающий экземпляр типа DataClass, который был ранее записан в файл.
// Сериализация объектов

import java.io.*;

// Класс, экземпляр которого нужно будет записать/считать из файла
class DataClass implements Serializable {

  // Внутренняя переменная
  private int a;
  private transient int b; // эта переменная сохраняться не будет

  // Конструктор
  DataClass(int a, int b) {
    this.a = a;
    this.b = b;
  }

  // Методы доступа
  int GetA() {
    return a;
  }

  int GetB() {
    return b;
  }

  void SetA(int a) {
    this.a = a;
  }

  void SetB(int b) {
    this.b = b;
  }

  // Метод, выводящий данные
  void Print() {
    System.out.println("a = " + a + ",   b = " + b);
  }
}

// Класс, который содержит методы сериализации (записи/чтения данных) класса DataClass
public class SerializeClass implements Serializable {

  // Метод, осуществляющий запись экземпляра класса DataClass в файл filename
  static void SaveData(DataClass dataObj, String filename)
      throws ClassNotFoundException, IOException {

    // 1. Создать экземпляр класса FileOutputStream - создать файловый поток
    FileOutputStream fOut = new FileOutputStream(filename);

    // 2. Создать экземпляр objOut класса ObjectOutputStream - поток записи объектов
    ObjectOutputStream objOut = new ObjectOutputStream(fOut);

    // 3. Записать экземпляр dataObj в поток objOut
    // 3.1. Записать комментарий
    objOut.writeObject("Storage the dataObj instance\n");

    // 3.2. Записать экземпляр
    objOut.writeObject(dataObj);

    // 4. Закрыть поток objOut
    objOut.close();

    // Закрыть файловый поток
    fOut.close();
  }

  static DataClass ReadData(String filename)
      throws ClassNotFoundException, IOException      {

    // 1. Создать экземпляр класса FileInputStream
    FileInputStream fInput = new FileInputStream(filename);

    // 2. Создать экземпляр класса ObjectInputStream
    ObjectInputStream objInput = new ObjectInputStream(fInput);

    // 3. Прочитать данные из файла filename в экземпляр dataObj
    // 3.1. Прочитать строку комментария
    String s = (String)objInput.readObject();

    // 3.2. Прочитать экземпляр типа DataClass
    DataClass dObj = (DataClass)objInput.readObject();

    // 4. Закрыть потоки
    objInput.close();
    fInput.close();

    // 5. Вернуть результат
    return dObj;
  }

  public static void main(String[] args) 
      throws ClassNotFoundException, IOException {

    // 1. Записать экземпляр класса DataClass в файл "myFile.dat"
    // 1.1. Создать объект класса DataClass
    DataClass objData = new DataClass(5, 8);

    // 1.2. Записать объект objData в файл "myFile.dat"
    SaveData(objData, "myFile.dat");

    // 2. Прочитать экземпляр класса DataClass из файла "myFile.dat"
    // 2.1. Объявить ссылку на DataClass
    DataClass objData2;

    // 2.2. Прочитать данные в objData2
    objData2 = ReadData("myFile.dat");

    // 2.3. Вывести данные
    objData2.Print(); // a = 5,   b = 0 - значение переменной b не записывалось
  }
}

 

5. Сохраняются ли методы класса во время сериализации?

Нет. При использовании механизма сериализации, сохраняются только данные (внутренние переменные).

 

6. Пример класса, который содержит методы сериализации экземпляра данного класса

В примере разработан класс Sphere, в котором определенны методы:

  • записи в файл текущего экземпляра класса;
  • чтение данных из файла текущего класса.

Класс содержит методы сериализации экземпляра данного класса.

Текст класса следующий

// Клас, содержащий методы сериализации объекта данного класса

import java.io.*;

class Sphere implements Serializable {

  // Внутренняя переменная - радиус шара
  private double radius;

  // Методы доступа
  public double GetR() {
    return radius;
  }

  public void SetR(double radius) {
    this.radius = radius;
  }

  // Методы сериализации объекта данного класса
  public void SaveToFile(String filename)
      throws ClassNotFoundException, IOException {

    // 1. Создать экземпляр класса FileOutputStream - создать файловый поток
    FileOutputStream fOut = new FileOutputStream(filename);

    // 2. Создать экземпляр objOut класса ObjectOutputStream - поток записи объектов
    ObjectOutputStream objOut = new ObjectOutputStream(fOut);

    // 3. Записать текущий экземпляр this в поток objOut
    objOut.writeObject(this);

    // 4. Закрыть поток objOut
    objOut.close();

    // 5. Закрыть файловый поток
    fOut.close();
  }

  public Sphere ReadFromFile(String filename)
      throws ClassNotFoundException, IOException {

    // 1. Создать экземпляр класса FileInputStream
    FileInputStream fInput = new FileInputStream(filename);

    // 2. Создать экземпляр класса ObjectInputStream
    ObjectInputStream objInput = new ObjectInputStream(fInput);

    // 3. Прочитать данные из файла filename в экземпляр dObj
    Sphere dObj = (Sphere)objInput.readObject();

    // 4. Закрыть потоки
    objInput.close();
    fInput.close();

    // 5. Вернуть результат
    return dObj;
  }

  // Метод вычисления объема шара
  public double Volume() {
    if (radius > 0)
      return 4.0 / 3.0 * Math.PI*radius*radius*radius;
    return 0.0;
  }
}

public class DemoSerialization implements Serializable {

  public static void main(String[] args)
      throws ClassNotFoundException, IOException {

    // 1. Записать экземпляр класса Sphere в файл "myfile.dat"
    // 1.1. Создать экземпляр sp
    Sphere sp = new Sphere();

    // 1.2. Установить радиус
    sp.SetR(25.5);

    // 1.3. Записать sp в файл
    sp.SaveToFile("myfile.dat");

    // 2. Прочитать ранее записанный экземпляр из файла
    // 2.1. Объявить новую ссылку sp2 типа Sphere
    Sphere sp2 = new Sphere();

    // 2.2. Прочитать sp2
    sp2 = sp2.ReadFromFile("myfile.dat");

    // 2.3. Вывести значение в sp2
    System.out.println("sp2.radius = " + sp2.GetR());
  }
}

Результат работы программы

sp2.radius = 25.5

 


Связанные темы