Ответ 1
Предположим, что он (логически) можно преобразовать старый объект в новый объект, используя некоторую умную стратегию для установки неэксентирующих полей и т.д. и т.д. Как я могу упорядочить исходный код?
Я вижу два способа справиться с этим. Во-первых, вы никогда не должны менять serialVersionUID
, если вы не хотите, чтобы InvalidClassException
был сброшен. Второе правило состоит в том, чтобы не изменять типы полей, а только добавлять или удалять поля, которые автоматически обрабатываются сериализацией. Например, если сериализованный файл имеет версию класса с boolean sharpTeeth;
, но класс не имеет этого поля, то при десериализации он будет игнорироваться. Если класс deserialized имеет поле sharpTeeth
, но файл не будет тогда, он будет инициализирован по умолчанию, false
в этом случае.
Это особенно важно для распределенных систем, в которых вы хотите попробовать как передовую, так и обратную совместимость. Вы не хотите обновлять версию приложения A и прерывать другое приложение B, которое зависит от A. Не изменяя serialVersionUID
, а просто добавляя или удаляя поля, вы можете это сделать. Более поздние версии вашего объекта должны поддерживать более старые версии без значений в новых полях, но более старые объекты не будут возражать, если будут доступны новые поля. Это также означает, что вы также не должны изменять масштаб поля.
Сериализация довольно умна, но она не обрабатывает изменения типов в полях. Вы не должны просто изменять paws
от int
до long
. Вместо этого я бы рекомендовал добавить long pawsLong
или некоторые из них и написать свой код, чтобы обрабатывать возможность наличия int paws
или long pawsLong
со значением.
public long getPaws() {
if (pawsLong > 0) {
return pawsLong;
} else {
// paws used to be an integer
return paws;
}
}
Вы также можете написать свой собственный метод readObject
для преобразования при де-сериализации:
private void readObject(java.io.ObjectInputStream in) {
super.readObject(in);
// paws used to be an integer
if (pawsLong == 0 && paws != 0) {
pawsLong = paws;
}
}
Если это не сработает для вас, тогда обычная сериализация - это путь. Сначала вам нужно начинать с этого делать и определять собственные методы readObject(...)
и writeObject(...)
с внутренним идентификатором версии. Что-то вроде:
// never change this
private static final long serialVersionUID = 3375159358757648792L;
// only goes up
private static final int INTERNAL_VERSION_ID = 2;
...
// NOTE: in version #1, this was an int
private long paws;
private void readObject(java.io.ObjectInputStream in) {
int version = in.readInt();
switch (version) {
case 1 :
paws = in.readInt();
...
case 2 :
paws = in.readLong();
...
private void writeObject(java.io.ObjectOutputStream out) {
out.writeInt(INTERNAL_VERSION_ID);
out.writeLong(paws);
...
Но этот метод не поможет вам с форвардной совместимостью. Читатель версии 1 не понимает ввод сериализации версии 2.
Должен ли я выполнять десериализацию в одном загрузчике классов, и если это не удается, попробуйте использовать другой загрузчик классов, который использует более старую версию (и так далее), или есть ли лучшие способы?
Я бы не предложил ни один из этих методов. Звучит очень сложно.