Скопируйте объект в Java

У меня есть объект, который мне нужно скопировать в Java. Мне нужно создать копию и запустить несколько тестов, не меняя сам исходный объект.

Я предположил, что мне нужно использовать метод clone(), но это защищено. Проведя некоторое исследование в сети, я вижу, что это может быть переопределено публичным методом в моем классе. Но я не могу найти объяснение того, как это сделать. Как это можно сделать?

Кроме того, это лучший способ достижения того, что мне нужно?

7 ответов

Другой вариант с использованием конструктора копирования (из практики Java):

public final class Galaxy {

    public Galaxy (double aMass, String aName) {
        fMass = aMass;
        fName = aName;
    }

    /**
    * Copy constructor.
    */
    public Galaxy(Galaxy aGalaxy) {
        this(aGalaxy.getMass(), aGalaxy.getName());
        //no defensive copies are created here, since 
        //there are no mutable object fields (String is immutable)
    }

    /**
    * Alternative style for a copy constructor, using a static newInstance
    * method.
    */
    public static Galaxy newInstance(Galaxy aGalaxy) {
        return new Galaxy(aGalaxy.getMass(), aGalaxy.getName());
    }

    public double getMass() {
        return fMass;
    }

    /**
    * This is the only method which changes the state of a Galaxy
    * object. If this method were removed, then a copy constructor
    * would not be provided either, since immutable objects do not
    * need a copy constructor.
    */
    public void setMass( double aMass ){
        fMass = aMass;
    }

    public String getName() {
        return fName;
    }

    // PRIVATE /////
    private double fMass;
    private final String fName;

    /**
    * Test harness.
    */
    public static void main (String... aArguments){
        Galaxy m101 = new Galaxy(15.0, "M101");

        Galaxy m101CopyOne = new Galaxy(m101);
        m101CopyOne.setMass(25.0);
        System.out.println("M101 mass: " + m101.getMass());
        System.out.println("M101Copy mass: " + m101CopyOne.getMass());

        Galaxy m101CopyTwo = Galaxy.newInstance(m101);
        m101CopyTwo.setMass(35.0);
        System.out.println("M101 mass: " + m101.getMass());
        System.out.println("M101CopyTwo mass: " + m101CopyTwo.getMass());
    }
} 

Есть два популярных подхода. Одним из них является предоставление clone метод, как вы упомянули, вроде так.

public class C implements Cloneable {
    @Override public C clone() {
        try {
            final C result = (C) super.clone();
            // copy fields that need to be copied here!
            return result;
        } catch (final CloneNotSupportedException ex) {
            throw new AssertionError();
        }
}

Обратите внимание на "скопировать поля... здесь!" часть. Начальный result является только поверхностной копией, что означает, что если есть ссылка на объект, как оригинал, так и result поделится тем же объектом. Например, если C содержит private int[] data Вы, вероятно, захотите скопировать это.

...
final C result = (C) super.clone();
result.data = data.clone();
return result;
...

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

Второй подход заключается в предоставлении конструктора копирования.

public class C {
    public C(final C c) {
        // initialize this with c
    }
}

Или копировальная фабрика.

public class C {
    public static C newInstance(final C c) {
        return new C(c);
    }

    private C(final C c) {
        // initialize this with c
    }
}

Оба подхода имеют свои соответствующие свойства. clone это хорошо, потому что это метод, поэтому вам не нужно знать точный тип. В конце концов, вы всегда должны получить "идеальную" копию. Конструктор копирования хорош, потому что у вызывающей стороны есть возможность принять решение, как это видно из Коллекций Java.

final List c = ... 
// Got c from somewhere else, could be anything.
// Maybe too slow for what we're trying to do?

final List myC = new ArrayList(c);
// myC is an ArrayList, with known properties

Я рекомендую выбрать любой подход, который вам больше подходит.

Я бы использовал другие подходы, такие как рефлексивное копирование или немедленная сериализация / десериализация, только в модульных тестах. Мне они кажутся менее подходящими для производственного кода, в основном из-за проблем с производительностью.

Некоторые варианты:

  • Вы можете реализовать Cloneable для вашего объекта и положить clone() метод как публичный. Смотрите полное объяснение здесь: http://www.cafeaulait.org/course/week4/46.html
    Тем не менее, это создает мелкую копию и может быть не то, что вы хотите.
  • Вы можете сериализовать и десериализовать свой объект. Вам нужно будет реализовать Serializable интерфейс для объекта и всех его полей.
  • Ты можешь использовать XStream выполнить сериализацию через XML - вам не нужно ничего реализовывать здесь.

Для тестового кода Serialization, возможно, самый безопасный ответ, особенно если объект уже Serializable, попробуйте Apache Commons SerializationUtils для реализации.

Вы можете использовать org.apache.commons.lang3.SerializationUtils класс для клонирования объектов, - класс должен реализовывать интерфейс Serializable.

  • ClassName copyobject = SerializationUtils.clone (classobjectTocopy)

SerializationUtils.clone также поддерживается в экземпляре Google App Engine

Есть несколько способов скопировать объект в Java(мелкий или глубокий).
Этот ответ поможет вам.

У Джошуа Блоха есть кое-что интересное о клонировании. В зависимости от размера / конструкции объекта я бы добавил конструктор копирования к объекту или сериализовал / десериализовал, используя одно из решений, упомянутых выше.

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