Перейти к основному содержимому

Проверка сериализации в Java

· 4 мин. чтения

1. Обзор

В этом кратком руководстве мы покажем, как проверить сериализуемый объект в Java .

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

Сериализация — это процесс преобразования состояния объекта в поток байтов . Сериализованные объекты в основном используются в технологиях Hibernate, RMI, JPA, EJB и JMS.

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

3. Проверка сериализации

Мы можем проверить сериализацию, используя различные методы. Давайте посмотрим на некоторые из них.

3.1. Проверка реализует сериализацию

Самый простой способ определить, является ли объект сериализуемым, — это проверить, является ли этот объект экземпляром java.io.Serializable или java.io.Externalizable . Однако этот метод не гарантирует, что мы сможем сериализовать объект.

Допустим, у нас есть объект Address , который не реализует интерфейс Serializable :

public class Address {
private int houseNumber;

//getters and setters
}

При попытке сериализовать объект Address может возникнуть исключение NotSerializableException :

@Test(expected = NotSerializableException.class)
public void whenSerializing_ThenThrowsError() throws IOException {
Address address = new Address();
address.setHouseNumber(10);
FileOutputStream fileOutputStream = new FileOutputStream("yofile.txt");
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
objectOutputStream.writeObject(address);
}
}

Теперь предположим, что у нас есть объект Person , который реализует интерфейс Serializable :

public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private int age;
private String name;

// getters and setters
}

В этом случае мы сможем сериализовать и десериализовать, чтобы воссоздать объект обратно:

Person p = new Person();
p.setAge(20);
p.setName("Joe");
FileOutputStream fileOutputStream = new FileOutputStream("yofile.txt");
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
objectOutputStream.writeObject(p);
}

FileInputStream fileInputStream = new FileInputStream("yofile.txt");
try ( ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
Person p2 = (Person) objectInputStream.readObject();
assertEquals(p2.getAge(), p.getAge());
assertEquals(p2.getName(), p.getName());;
}

3.2. Apache Commons SerializationUtils

Другой способ проверить сериализацию объекта — использовать метод сериализации из Apache Commons SerializationUtils . Этот метод не примет несериализуемый объект.

Что, если мы попытаемся сериализовать несериализуемый объект Address , явно приведя тип для компиляции кода? Во время выполнения мы столкнемся с ClassCastException :

Address address = new Address();
address.setHouseNumber(10);
SerializationUtils.serialize((Serializable) address);

Давайте используем приведенное выше для проверки сериализуемого объекта Person :

Person p = new Person();
p.setAge(20);
p.setName("Joe");
byte[] serialize = SerializationUtils.serialize(p);
Person p2 = (Person)SerializationUtils.deserialize(serialize);
assertEquals(p2.getAge(), p.getAge());
assertEquals(p2.getName(), p.getName());

3.3. Spring Core SerializationUtils

Теперь мы рассмотрим метод SerializationUtils из spring-core , который похож на метод из Apache Commons. Этот метод также не принимает несериализуемый объект Address .

Такой код вызовет исключение ClassCastException во время выполнения:

Address address = new Address();
address.setHouseNumber(10);
org.springframework.util.SerializationUtils.serialize((Serializable) address);

Давайте попробуем с сериализуемым объектом Person :

Person p = new Person();
p.setAge(20);
p.setName("Joe");
byte[] serialize = org.springframework.util.SerializationUtils.serialize(p);
Person p2 = (Person)org.springframework.util.SerializationUtils.deserialize(serialize);
assertEquals(p2.getAge(), p.getAge());
assertEquals(p2.getName(), p.getName());

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

В качестве третьего варианта мы создадим собственную пользовательскую утилиту для сериализации или десериализации в соответствии с нашими требованиями. Чтобы продемонстрировать это, мы напишем два отдельных метода для сериализации и десериализации.

Первый пример проверки объекта для процесса сериализации:

public static  byte[] serialize(T obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
return baos.toByteArray();
}

Мы также напишем метод для выполнения процесса десериализации:

public static  T deserialize(byte[] b, Class cl) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
return cl.cast(o);
}

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

Давайте реализуем этот метод:

public static boolean isSerializable(Class<?> it) {
boolean serializable = it.isPrimitive() || it.isInterface() || Serializable.class.isAssignableFrom(it);
if (!serializable) {
return false;
}
Field[] declaredFields = it.getDeclaredFields();
for (Field field : declaredFields) {
if (Modifier.isVolatile(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) ||
Modifier.isStatic(field.getModifiers())) {
continue;
}
Class<?> fieldType = field.getType();
if (!isSerializable(fieldType)) {
return false;
}
}
return true;
}

Давайте теперь проверим наш служебный метод:

assertFalse(MySerializationUtils.isSerializable(Address.class));
assertTrue(MySerializationUtils.isSerializable(Person.class));
assertTrue(MySerializationUtils.isSerializable(Integer.class));

4. Вывод

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

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