A ação de copiar os atributos de um objeto em outro do mesmo tipo de dados é chamada de cópia do objeto. Em Java, temos as seguintes abordagens de cópia de um objeto em outro:

  • Shallow Copy: aqui se o campo que deve ser copiado é um tipo primitivo, então o valor é copiado mais se o campo que deve ser copiado é um endereço de memória (ou um objeto em si), o endereço é copiado. Assim, se o endereço é alterado por um objeto, a alteração fica refletida em todos os lugares.
  • Deep Copy: aqui os dados são copiados em ambas as situações. Essa abordagem é mais carregada e mais lenta.
  • Lazy Copy: Esta é uma combinação das duas abordagens acima. Inicialmente a abordagem cópia superficial (Shallow Copy) é usada e depois verifica se os dados são compartilhados por muitos objetos e se o programa precisa para modificar um objeto, é utilizada a abordagem de copiar.

Assim podemos selecionar o tipo de cópia com base em duas condições a seguir:

  • Use Shallow Copy quando o encapsulamento não é necessário.
  • Use a Deep Copy quando o encapsulamento é necessário.

Em Shallow Copy, um novo objeto é criado que contém a cópia exata dos valores no objeto original. Shallow Copy segue a abordagem de cópia bit a bit. Em cópia superficial se o campo for um endereço de memória, em seguida, o endereço é copiado. Assim, se o endereço é alterado por um objeto, a alteração fica refletida em todos os lugares.

Fluxograma que descreve Shallow Copy

Figura 1: Fluxograma que descreve Shallow Copy

Nessa figura, o objeto - mainObj1 tem campos field1 de um tipo primitivo int, e um objeto do tipo String . Quando fazemos uma cópia superficial do mainObj1, mainObj2 está criado com field2 do tipo int que contém o valor copiado de field1 mas o objeto do tipo String em mainObj2 - ainda aponta para objStr itself. Desde field1é um tipo de dado primitivo, o valor dele é copiado para field2. Mas desde objStr é um objeto, mainObj2 está apontando para o mesmo endereço de objStr. Então as alterações feitas para objStr via mainObj1 se reflete em mainObj2.

Implementação:

Listagem 1: Classe SubjectVO.java descreve o objeto de valor para os indivíduos

package com.home.objectCopy;
 
public class SubjectVO {
 
    private String name;
 
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
 
    /**
     * @param name 
     * the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
 
    public SubjectVO(String name) {
        this.name = name;
    }
}

Listagem 2: Classe PupilVO.java descrevendo objeto de valor para o aluno

package com.home.objectCopy;
 
public class PupilVO implements Cloneable {
 
    // Contained object
    private SubjectVO subj;
    private String name;
 
    /**
     * @return the subj
     */
    public SubjectVO getSubj() {
        return subj;
    }
 
    /**
     * @param subj
     * the subj to set
     */
    public void setSubj(SubjectVO subj) {
        this.subj = subj;
    }
 
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
 
    /**
     * @param name
     * the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
 
    public PupilVO(String name, String sub) {
        this.name = name;
        this.subj = new SubjectVO(sub);
    }
 
    public Object clone() {
        // shallow copy
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

Listagem 3: Classe ShallowCopyTest.java descrevendo o processo de cópia

package com.home.objectCopy;
 
public class ShallowCopyTest {
 
    public static void main(String[] args) {
        // Original Object
        PupilVO stud = new PupilVO("Johnathan", "Algebra");
        System.out.println("Objeto original: " + stud.getName() + " - "
                + stud.getSubj().getName());
        // Clone Object
        PupilVO clonedStud = (PupilVO) stud.clone();
        System.out.println("Objeto clonado: " + clonedStud.getName() + " - "
                + clonedStud.getSubj().getName());
        stud.setName("Daniel");
        stud.getSubj().setName("Physics");
        System.out.println("Objeto original depois que ele é atualizado: "
                + stud.getName() + " - " + stud.getSubj().getName());
        System.out.println("Objeto clonado depois de atualizar o objeto original: "
                        + clonedStud.getName() + " - "
                        + clonedStud.getSubj().getName());
 
    }
 
}

Saída: A saída deste programa é como abaixo:

Objeto original: Johnathan - Algebra
Objeto clonado: Johnathan - Algebra
Objeto original depois que ele é atualizado: Daniel - Physics
Objeto clonado depois de atualizar o objeto original: Johnathan - Physics

Aqui vemos que o valor do nome do campo fica alterado após a operação de cópia, mas o valor do sujeito objeto permanece o mesmo, como ele está apontando para o mesmo endereço de memória.

Portanto, torna-se o assunto para Jonathan 'Physics' onde como deve ser 'Algebra' como o objeto para SubjectVO no objeto clonado permanece inalterado.

Copiar

Em Deep Copy, não só todos os campos de um objeto são copiados, todo o endereço de memória alocada dinamicamente que são apontados por esse objeto também é copiado.

Diagrama descreve o processo de copiar

Figura 2: Diagrama descreve o processo de copiar

Nesta figura, o objeto mainObj1 tem campos field1, um tipo de dados primitivos dizem int e um objeto do tipo String quando fazemos uma cópia em profundidade de mainObj1,mainObj2 é criado com field2 contendo o valor copiado field1 e objStr2 é criado e contém o valor copiado de objStr1. Então todas as alterações feitas para objStr1 em mainObj1 não refletirá em mainObj2.

Implementação

Listagem 4: Classe descrevendo Deep Copy

package com.home.DeepCopy;
 
public class PupilVO implements Cloneable {
 
    // Contained object
    private SubjectVO subj;
    private String name;
 
    /**
     * @return the subj
     */
    public SubjectVO getSubj() {
        return subj;
    }
 
    /**
     * @param subj
     * the subj to set
     */
    public void setSubj(SubjectVO subj) {
        this.subj = subj;
    }
 
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
 
    /**
     * @param name
     * the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
 
    public PupilVO(String name, String sub) {
        this.name = name;
        this.subj = new SubjectVO(sub);
    }
 
    public Object clone() {
        // deep copy
        PupilVO pupil = new PupilVO(name, subj.getName());
        return pupil;
    }
 
}

A única diferença entre essa abordagem e da abordagem anterior é que o método clone no PupilVO retorna um objeto PupilVO recém-criado. Isso garante que sempre que o mecanismo de cópia no iniciado, o objeto SubjectVO também será mudado. Deep Copy tem uma abordagem alternativa - serialização. Na serialização, o gráfico de objeto inteiro é escrito em um armazenamento persistente e lido quando necessário.

Então, sempre que lemos o objeto do armazenamento persistente, o objeto original é conhecido.

Uso da cópia superficial e profunda

Não há nenhuma regra padrão e rápida definida para selecionar entre cópia superficial e profunda, mas normalmente devemos manter em mente que se um objeto tem apenas campos primitivos, então obviamente devemos ir para cópia superficial (Shallow Copy), mas se o objeto tem referências a outros objetos, em seguida, baseada na exigência, cópia profunda ou cópia superficial deve ser feita. Se as referências não são atualizadas então não adianta iniciar um Deep Copy.

Explicando Lazy Copy

A Lazy Copy pode ser definida como uma combinação de ambos, cópia superficial e profunda. O mecanismo segue uma abordagem simples - em estado inicial, abordagem cópia superficial é usada. Um contador também é usado para manter um controle sobre quantos objetos compartilham os dados. Quando o programa quer modificar o objeto original, ele verifica se o objeto é compartilhado ou não. Se o objeto é compartilhado, em seguida, o mecanismo de cópia profunda é iniciado.

Conclusão

Em Shallow Copy, somente os campos de tipo de dados primitivo são copiados enquanto as referências de objetos não são copiadas. Deep Copy envolve a cópia de tipo de dados primitivo, bem como objetos de referências. Não há nenhuma regra definitiva e rápida para quando usar Shallow Copy e quando usar um Deep Copy. Lazy Copy é uma combinação de ambas essas abordagens.

Artigo traduzido e originalmente publicado em: http://mrbool.com/what-is-deep-copy-and-shallow-copy-in-java/28569