Рубрики
Без рубрики

Введение в сериализацию Java

Мы узнаем, как сериализовать и десериализовать объекты в Java.

Автор оригинала: baeldung.

1. введение

Сериализация-это преобразование состояния объекта в поток байтов; десериализация делает обратное. Иными словами, сериализация-это преобразование объекта Java в статический поток (последовательность) байтов, который затем может быть сохранен в базе данных или передан по сети.

2. Сериализация и десериализация

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

Оба ObjectInputStream и ObjectOutputStream являются классами высокого уровня, которые расширяют java.io.InputStream и java.io.OutputStream соответственно. ObjectOutputStream может записывать примитивные типы и графики объектов в OutputStream в виде потока байтов. Эти потоки впоследствии могут быть прочитаны с помощью ObjectInputStream .

Наиболее важным методом в ObjectOutputStream является:

public final void writeObject(Object o) throws IOException;

Который берет сериализуемый объект и преобразует его в последовательность (поток) байтов. Аналогично, наиболее важным методом в ObjectInputStream является:

public final Object readObject() 
  throws IOException, ClassNotFoundException;

Который может считывать поток байтов и преобразовывать его обратно в объект Java. Затем он может быть возвращен к исходному объекту.

Давайте проиллюстрируем сериализацию с помощью класса Person . Обратите внимание, что статические поля принадлежат классу (в отличие от объекта) и не сериализуются . Кроме того, обратите внимание, что мы можем использовать ключевое слово transient для игнорирования полей класса во время сериализации:

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    static String country = "ITALY";
    private int age;
    private String name;
    transient int height;

    // getters and setters
}

В приведенном ниже тесте показан пример сохранения объекта типа Person в локальный файл, а затем считывания этого значения обратно в:

@Test 
public void whenSerializingAndDeserializing_ThenObjectIsTheSame() () 
  throws IOException, ClassNotFoundException { 
    Person person = new Person();
    person.setAge(20);
    person.setName("Joe");
    
    FileOutputStream fileOutputStream
      = new FileOutputStream("yourfile.txt");
    ObjectOutputStream objectOutputStream 
      = new ObjectOutputStream(fileOutputStream);
    objectOutputStream.writeObject(person);
    objectOutputStream.flush();
    objectOutputStream.close();
    
    FileInputStream fileInputStream
      = new FileInputStream("yourfile.txt");
    ObjectInputStream objectInputStream
      = new ObjectInputStream(fileInputStream);
    Person p2 = (Person) objectInputStream.readObject();
    objectInputStream.close(); 
 
    assertTrue(p2.getAge() == p.getAge());
    assertTrue(p2.getName().equals(p.getName()));
}

Мы использовали ObjectOutputStream для сохранения состояния этого объекта в файл с помощью FileOutputStream . Файл “yourfile.txt” создается в каталоге проекта. Затем этот файл загружается с помощью FileInputStream. ObjectInputStream |/подбирает этот поток и преобразует его в новый объект с именем p2 .

Наконец, мы проверяем состояние загруженного объекта, и оно совпадает с состоянием исходного объекта.

Обратите внимание, что загруженный объект должен быть явно приведен к типу Person .

3. Предостережения о сериализации Java

Есть некоторые предостережения, которые касаются сериализации в Java.

3.1. Наследование и состав

Когда класс реализует интерфейс java.io.Serializable , все его подклассы также сериализуемы. Напротив, когда объект имеет ссылку на другой объект, эти объекты должны реализовывать интерфейс Serializable отдельно, иначе будет вызвано исключение NotSerializableException :

public class Person implements Serializable {
    private int age;
    private String name;
    private Address country; // must be serializable too
}

Если одно из полей в сериализуемом объекте состоит из массива объектов, то все эти объекты также должны быть сериализуемыми, иначе будет выдано исключение NotSerializableException .

3.2. UID серийной версии

JVM связывает номер версии ( long ) с каждым сериализуемым классом. Он используется для проверки того, что сохраненные и загруженные объекты имеют одинаковые атрибуты и, следовательно, совместимы при сериализации.

Это число может быть автоматически сгенерировано большинством IDE и основано на имени класса, его атрибутах и связанных модификаторах доступа. Любые изменения приводят к другому числу и могут вызвать исключение InvalidClassException .

Если сериализуемый класс не объявляет serialVersionUID , JVM автоматически сгенерирует его во время выполнения. Однако настоятельно рекомендуется, чтобы каждый класс объявлял свой serialVersionUID , поскольку сгенерированный класс зависит от компилятора и, следовательно, может привести к неожиданным InvalidClassExceptions .

3.3. Пользовательская сериализация в Java

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

private void writeObject(ObjectOutputStream out) throws IOException;

и

private void readObject(ObjectInputStream in) 
  throws IOException, ClassNotFoundException;

С помощью этих методов мы можем сериализовать эти несериализуемые атрибуты в другие формы, которые могут быть сериализованы:

public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    private transient Address address;
    private Person person;

    // setters and getters

    private void writeObject(ObjectOutputStream oos) 
      throws IOException {
        oos.defaultWriteObject();
        oos.writeObject(address.getHouseNumber());
    }

    private void readObject(ObjectInputStream ois) 
      throws ClassNotFoundException, IOException {
        ois.defaultReadObject();
        Integer houseNumber = (Integer) ois.readObject();
        Address a = new Address();
        a.setHouseNumber(houseNumber);
        this.setAddress(a);
    }
}
public class Address {
    private int houseNumber;

    // setters and getters
}

Следующий модульный тест проверяет эту пользовательскую сериализацию:

@Test
public void whenCustomSerializingAndDeserializing_ThenObjectIsTheSame() 
  throws IOException, ClassNotFoundException {
    Person p = new Person();
    p.setAge(20);
    p.setName("Joe");

    Address a = new Address();
    a.setHouseNumber(1);

    Employee e = new Employee();
    e.setPerson(p);
    e.setAddress(a);

    FileOutputStream fileOutputStream
      = new FileOutputStream("yourfile2.txt");
    ObjectOutputStream objectOutputStream 
      = new ObjectOutputStream(fileOutputStream);
    objectOutputStream.writeObject(e);
    objectOutputStream.flush();
    objectOutputStream.close();

    FileInputStream fileInputStream 
      = new FileInputStream("yourfile2.txt");
    ObjectInputStream objectInputStream 
      = new ObjectInputStream(fileInputStream);
    Employee e2 = (Employee) objectInputStream.readObject();
    objectInputStream.close();

    assertTrue(
      e2.getPerson().getAge() == e.getPerson().getAge());
    assertTrue(
      e2.getAddress().getHouseNumber() == e.getAddress().getHouseNumber());
}

В этом коде мы видим, как сохранить некоторые сериализуемые атрибуты, сериализуя Адрес с помощью пользовательской сериализации. Обратите внимание, что мы должны пометить сериализуемые атрибуты как transient , чтобы избежать исключения NotSerializableException.

4. Заключение

В этом кратком руководстве мы рассмотрели сериализацию Java, обсудили важные вещи, которые следует иметь в виду, и показали, как выполнять пользовательскую сериализацию.

Как всегда, исходный код, используемый в этом учебнике, доступен на GitHub .