V6083. Serialization order of fields should be preserved during deserialization.
Данное диагностическое правило позволяет обнаружить несоответствие последовательностей сериализации и десериализации полей объекта.
При использовании интерфейса 'java.io.Serializable' весь контроль над сериализацией достается JVM. При всём удобстве этого подхода, часто оказывается, что он является недостаточно гибким или производительным.
Альтернативным способом сериализации, предоставляемым JVM, является использование интерфейса 'java.io.Externalizable' с переопределением методов 'writeExternal' и 'readExternal'. При этом очень легко перепутать порядок записи и чтения полей, что может привести к появлению трудноуловимой ошибки.
Рассмотрим пример:
public class ExternalizableTest implements Externalizable
{
public String name;
public String host;
public int port;
....
@Override
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(port); // <=
out.writeUTF(name);
out.writeUTF(host);
}
@Override
public void readExternal(ObjectInput in) throws IOException
{
this.name = in.readUTF(); // <=
this.host = in.readUTF();
this.port = in.readInt();
}
}
В данном примере порядок сериализации полей объекта: port, name, host, type. Порядок десериализации: name, host, port, type. При сериализации первым полем идет целочисленное значение, а при десериализации – строка. Такое нарушение порядка десериализации приведет к исключению 'java.io.EOFException'. Можно сказать, что "повезло", так как эта ошибка проявит себя при первой же попытке десериализовать объект.
А что если чуть-чуть не "повезет"? Например, так:
public class ExternalizableTest implements Externalizable
{
public String name;
public String host;
public int port;
....
@Override
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(port);
out.writeUTF(name); // <=
out.writeUTF(host);
}
@Override
public void readExternal(ObjectInput in) throws IOException
{
this.port = in.readInt();
this.host = in.readUTF(); // <=
this.name = in.readUTF();
}
}
Последовательность десериализации опять не соответствует последовательности сериализации: перепутаны местами строковые поля 'name' и 'host'. В этом случае падения никакого не будет и объект успешно восстановится, но значения этих полей будут перепутаны. Такое обнаружить уже будет не так легко.
Исправленный вариант:
public class ExternalizableTest implements Externalizable
{
public String name;
public String host;
public int port;
....
@Override
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(port);
out.writeUTF(name);
out.writeUTF(host);
}
@Override
public void readExternal(ObjectInput in) throws IOException
{
this.port = in.readInt();
this.name = in.readUTF();
this.host = in.readUTF();
}
}