Когда имеет смысл, чтобы объект Java был сериализуемым, но не клонируемым?

Если класс Java реализует Serializable интерфейс, но не имеет публичного clone() Метод, как правило, можно создать глубокую копию, как это:

class CloneHelper {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            T copy = (T) ois.readObject();
            ois.close();
            return copy;
        } catch (ClassNotFoundException ex) {
            // Shouldn't happen
            throw new Error(ex);
        } catch (IOException ex) {
            // Probably a bug in T's custom serialization methods
            throw new RuntimeException(ex);
        }
    }
}

Я часто сталкиваюсь со сторонними библиотечными классами, такими как этот, и прибегаю к взломам, подобным приведенному выше. Я даже продлил ObjectOutputStream в некоторых случаях сделать копию меньше. Это никогда не вызывало серьезных проблем, кроме неэффективности (медленное кодирование / декодирование и временные графы сериализации могут занимать много памяти.)

И если использовать эту технику небезопасно, класс, вероятно, не должен был быть объявлен Serializable,

Так что я хотел бы знать, если ваш класс Serializableчто может помешать вам определить общественность clone() метод (используя либо Cloneable интерфейс или конструктор копирования?)


Связанный: Скопируйте объект в Java

7 ответов

Решение

Вы говорите, что механизм сериализации - это один из способов косвенного клонирования объектов. Это, конечно, не его основная функция. Обычно он используется для того, чтобы программы могли передавать объекты по сети или сохранять и затем читать их. Вы можете ожидать, что объект будет использоваться таким образом, и реализовать Serializable, не ожидая, что код будет клонировать объекты локально, и не реализует Cloneable.

Тот факт, что код работает вокруг этого через сериализацию, предполагает, что код использует объект не так, как задумал автор, что может быть как "ошибкой" автора, так и вызывающей стороны, но это не означает, что в общем случае Serializable и Клонируемые идут вместе.

Отдельно я не уверен, что clone() "сломан" настолько, насколько сложно реализовать правильно. Конструктор копирования более естествен для использования и получения правильного ИМХО.

Я бы предпочел использовать конструктор копирования, а не использовать вышеуказанный механизм. Вы можете более точно определить, что должно быть глубоко или поверхностно скопировано, и сделать копирование объекта отличным от сериализации объекта. Конструктор копирования может (например) позволить двум объектам совместно использовать ссылку на главный объект, тогда как это может не подходить для объекта, сериализованного и переданного по сети.

Обратите внимание, что Cloneable Этот метод в настоящее время считается сломленным. Смотрите эту статью с Джошуа Блохом для получения дополнительной информации. В частности это не имеет clone() метод!

Замечание Брайана о Cloneable очень хорошо, но даже если Cloneable сработал правильно, все еще есть случаи, когда вы хотите, чтобы объект был сериализуемым, но не клонируемым.

Если объект имеет уникальную идентичность вне области процесса, например представление в базе данных записи в памяти, вы не хотите, чтобы он был клонируемым, потому что это эквивалентно созданию новой записи с идентичными атрибутами, включая идентичность. связанные атрибуты, такие как ключ базы данных, что почти никогда не бывает правильным. В то же время у вас может быть система, разбитая на несколько процессов для стабильности или по другим причинам, поэтому у вас может быть один процесс, обращающийся к базе данных и генерирующий эти объекты "ENTITY" (см. "Проект на основе домена" Эрика Эванса для получения дополнительной информации). информация о поддержании согласованности идентификатора объекта в приложении с поддержкой данных), но отдельный процесс может использовать эти объекты для выполнения операций бизнес-логики. Объект сущности должен быть сериализуемым, чтобы его можно было передавать из одного процесса в другой.

Я думаю, что интерфейсы Serializable и Cloneable должны использоваться в совершенно разных целях. И если у вас есть сложный класс, то реализовать каждый из них не так просто. Так что в общем случае это зависит от целей.

Одна из самых больших проблем с Serializable заключается в том, что их нельзя легко сделать неизменяемыми. Сериализация заставляет вас идти на компромисс здесь.

Я бы соблазнился создать конструкторы копирования по графу объектов. Это немного более грубая работа.

Я просто подумал о другом случае - когда это перечисление.

Или, в целом, когда ваш класс реализует readResolve, Это означает, что объект, с которого вы возвращаетесь readObject это не то же самое, что было прочитано из потока, и, следовательно, это не обязательно копия объекта, который был первоначально записан в поток.

Это кажется мне довольно опасным, поскольку существует ряд ловушек при сериализации (хотя большинство из них маловероятны, я все же хотел бы проверить их на предмет сериализации объектов в сторонних библиотеках). Вряд ли это будет распространенной проблемой, но возможно иметь объект с изменчивой переменной как часть его состояния, который может быть частью операции клонирования (не то, что это хороший дизайн, просто это возможно), и такой поле не будет скопировано в процессе сериализации / десериализации. Еще одна проблема, которая приходит на ум, - это перечисления, константы и возможность получения нескольких копий таких вещей, если вы не имеете дело с ними во время десериализации.

Опять же, крайние случаи, но то, на что вы хотели бы обратить внимание.

Другие вопросы по тегам