Clonagem, acredito que todo mundo já ouviu falar disso. As primeiras ovelhas clonadas do mundo, Dolly, usam a tecnologia de transplante nuclear para cultivar novos indivíduos em células somáticas adultas de mamíferos, o que é muito mágico. De fato, há também o conceito de clonagem em Java, isto é, para realizar a cópia de objetos.
Este artigo tentará apresentar algumas perguntas detalhadas sobre a clonagem em Java, na esperança de ajudá-lo a entender melhor a clonagem.
Suponha que você queira copiar uma variável simples. Muito simples:
int maçãs = 5; int pérolas = maçãs;
Não apenas o tipo int, os outros sete tipos de dados primitivos (boolean, char, byte, curto, float, duplo.long) também são aplicáveis a esse tipo de situação.
Mas se você copiar um objeto, a situação será um pouco complicada.
Suponha que eu sou iniciante, eu escrevia isso:
Classe Student {Private Int Number; public int getNumber () {Número de retorno; } public void setNumber (int número) {this.number = número; }} public class Test {public static void main (string args []) {Student stu1 = new Student (); stu1.setNumber (12345); Aluno stu2 = stu1; System.out.println ("Student 1:" + Stu1.getNumber ()); System.out.println ("Student 2:" + Stu2.getNumber ()); }}resultado:
Aluno 1: 12345
Aluno 2: 12345
Aqui, personalizamos uma aula de estudante, que tem apenas um campo numérico.
Criamos uma nova instância do aluno e atribuímos o valor à instância do STU2. (Student Stu2 = Stu1;)
Vejamos os resultados da impressão. Como novato, taptrei meu peito e abdômen, mas o objeto foi copiado assim.
Esse é realmente o caso?
Tentamos alterar o campo numérico da instância do STU2 e, em seguida, imprimir o resultado e ver:
Stu2.SetNumber (54321); System.out.println ("Student 1:" + Stu1.getNumber ()); System.out.println ("Student 2:" + Stu2.getNumber ());resultado:
Aluno 1: 54321
Aluno 2: 54321
Isso é estranho. Por que o número do aluno do aluno 2 mudou e o número do aluno do aluno 1 também mudou?
O motivo está na frase (STU2 = STU1). O objetivo desta declaração é atribuir a referência do STU1 ao STU2.
Dessa forma, o STU1 e o STU2 apontam para o mesmo objeto na pilha de memória. Como mostrado na imagem:
Então, como podemos copiar um objeto?
Você se lembra do objeto rei de todas as idades? Possui 11 métodos e existem dois métodos protegidos, um dos quais é o método do clone.
Em Java, todas as classes são herdadas da classe de objeto no pacote de idiomas Java por padrão. Verifique seu código -fonte. Você pode copiar src.zip no seu diretório JDK para outro lugar e descomprimir -o, e todo o código -fonte está incluído. Descobri que existe um método com o qualificador de acesso Clone ():
/*Cria e retorna uma cópia deste objeto. O significado de precisão de "cópia" pode depender da classe do objeto. A intenção geral é que, para qualquer objeto x, a expressão: 1) x.clone ()! = X será verdadeiro2) x.clone (). GetClass () == x.getclass () será verdadeiro, mas esses requisitos não são absolutos. CLONENOTSUPPORTEDEXCECTION;
Se você olhar de perto, ainda é um método nativo. Todo mundo sabe que os métodos nativos são o código implementado em idiomas não Java e são para programas de Java. Como os programas Java são executados em máquinas virtuais da JVM, não há como acessar as relacionadas ao sistema operacional subjacente e elas só podem ser implementadas por idiomas próximos ao sistema operacional.
A primeira declaração garante que o objeto clonado tenha uma alocação de endereço de memória separada.
A segunda declaração mostra que os objetos originais e clonados devem ter o mesmo tipo de classe, mas não é obrigatório.
A terceira declaração mostra que os objetos originais e clonados devem ser igualmente utilizados pelo método iguals (), mas não é obrigatório.
Como a classe pai de cada classe é um objeto, todos eles contêm um método clone (), mas como o método é protegido, nenhum deles pode ser acessado fora da classe.
Para copiar um objeto, você precisa substituir o método clone.
Por que clonar?
Vamos pensar em uma pergunta primeiro, por que você precisa clonar objetos? Não há problema em apenas um objeto novo?
A resposta é: o objeto clonado pode conter algumas propriedades modificadas, e as propriedades do novo objeto ainda são os valores no momento da inicialização; portanto, quando um novo objeto é necessário para salvar o "estado" do objeto atual, o método de clone depende do método clone. Então, não é bom para mim atribuir as propriedades temporárias desse objeto ao meu novo objeto um por um? Tudo bem, mas não vamos falar sobre isso primeiro. Em segundo lugar, através do código -fonte acima, todos descobriram que o clone é um método nativo, que é rápido e implementado na parte inferior.
Deixe -me lembrá -lo de que nosso objeto comum a = novo objeto (); objeto b; b = a; Essa forma de código copia as referências, ou seja, o endereço do objeto na memória e os objetos A e B ainda apontam para o mesmo objeto.
O objeto atribuído através do método clone existe independentemente do objeto original.
Como implementar a clonagem
Deixe -me introduzir dois métodos de clonagem diferentes, raso e clonagem profunda.
No idioma Java, os tipos de dados são divididos em tipos de valor (tipos de dados básicos) e tipos de referência. Os tipos de valor incluem tipos de dados simples, como INT, Double, Byte, Boolean, Char, etc. e tipos de referência incluem tipos complexos, como classes, interfaces, matrizes, etc. A principal diferença entre clonagem superficial e clonagem profunda é se ele suporta a cópia de variáveis de membros dos tipos de referência. Os dois serão introduzidos em detalhes abaixo.
As etapas gerais são (clonagem superficial):
1. A classe copiada precisa implementar a interface clonenable (se você não a implementar, uma clonenotsupportedException será lançada ao chamar o método clone). Esta interface é uma interface de tags (sem nenhum método)
2. Substitua o método clone () e defina o modificador de acesso ao público. Ligue para o método super.Clone () no método para obter o objeto de cópia necessário. (Nativo é um método local)
A seguir, é apresentada uma modificação do método acima:
classe aluno implementa clonável {private int number; public int getNumber () {retorna número;} public void SetNumber (int number) {this.number = number;}@substituir public Object clone () {Student stu = null; tente {stu = (student) super.cone ();} capt (catch (clonenotsupportExcentExcembexcentExcembexexcept. Stu;}} Teste de classe pública {public static void main (string args []) {Student stu1 = new Student (); Stu1.setNumber (12345); Student Stu2 = (Student) Stu1.Clone (); System.out.println ("Student1:" Stu1.getNumber (); Stu2.getNumber ()); Stu2.SetNumber (54321); System.out.println ("Student1:" + Stu1.getNumber ()); System.out.println ("Student2:" + Stu2.getNumber ());}}}resultado:
Aluno 1: 12345
Aluno 2: 12345
Aluno 1: 12345
Aluno 2: 54321
Se você não acredita que esses dois objetos não são o mesmo objeto, pode dar uma olhada nesta frase:
System.out.println (Stu1 == STU2); // false
A cópia acima é chamada de clonagem superficial.
Há também uma cópia profunda um pouco mais complexa:
Vamos adicionar uma classe de endereço à aula de estudante.
classe de endereço {private string add; public string getadd () {return add;} public void setadd (string add) {this.add = add;}} classe Student implementa clonável {private int number; private endereço addr; public theetaddr () {return addr;} public void SetAddr (endereço) {this.dr (this) {addr; setNumber (int number) {this.number = number;}@substituir public objeto clone () {Student stu = null; tente {stu = (student) super.clone ();} catch (clonenotsupportEdException e) {e.printStacktrace ();} stu;}}) Endereço (); addR.setAdd ("Hangzhou City"); Student Stu1 = New Student (); Stu1.SetNumber (123); Stu1.setAddr (Addr); Student Stu2 = (Student) Stu1.Clone (); stu1.getaddr (). getadd ()); system.out.println ("aluno 2:" + stu2.getNumber () + ", add:" + stu2.getaddr (). getadd ());}}}resultado:
Aluno 1: 123, Endereço: Hangzhou Student 2: 123, Endereço: Hangzhou
À primeira vista, não há problema, esse é realmente o caso?
Tentamos alterar o endereço da instância do ADDR no método principal.
addr.setAdd ("distrito XIHU"); System.out.println ("Student 1:" + stu1.getNumber () + ", add:" + stu1.getaddr (). Getadd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", add:" + stu2.getaddr (). Getadd ());resultado:
Aluno 1: 123, Endereço: Hangzhou Student 2: 123, Endereço: Hangzhou Student 1: 123, Endereço: Xihu Distrito Student 2: 123, Endereço: Distrito Xihu
Isso é estranho, por que os endereços de ambos os alunos mudaram?
O motivo é que a cópia superficial copia apenas a referência da variável ADDR e não abre realmente outra peça de espaço. Depois de copiar o valor, retorne a referência ao novo objeto.
Portanto, para alcançar objetos de cópia verdadeiros, não referenciam puramente cópia. Precisamos copiar a classe de endereço e modificar o método clone. O código completo é o seguinte:
pacote ABC; classe endereço implementa clonável {private string add; public String getAdd () {return add; } public void setAdd (string add) {this.add = add; } @Override public Object clone () {endereço addr = null; tente {addr = (endereço) super.clone (); } catch (clonenotsupportEdException e) {e.printStackTrace (); } retornar addr; }} classe Student implementa clonável {private int número; endereço privado addr; endereço público getaddr () {return addr; } public void setAddr (endereço addr) {this.addr = addr; } public int getNumber () {retornar número; } public void setNumber (int número) {this.number = número; } @Override public Object clone () {Student stu = null; tente {stu = (aluno) super.clone (); // cópia rasa} catch (clonenotsupportedException e) {e.printStackTrace (); } stu.addr = (endereço) addr.clone (); // cópia profunda retorna stu; }} public class Test {public static void main (string args []) {endereço addr = novo endereço (); addr.setAdd ("Hangzhou City"); Estudante stu1 = novo aluno (); stu1.setNumber (123); stu1.setAddr (addr); Aluno stu2 = (aluno) stu1.clone (); System.out.println ("Student 1:" + stu1.getNumber () + ", endereço:" + stu1.getaddr (). Getadd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", endereço:" + stu2.getaddr (). Getadd ()); addr.setAdd ("distrito XIHU"); System.out.println ("Student 1:" + stu1.getNumber () + ", endereço:" + stu1.getaddr (). Getadd ()); addr.SetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", endereço:" + stu2.getaddr (). Getadd ()); }}resultado:
Aluno 1: 123, Endereço: Hangzhou Student 2: 123, Endereço: Hangzhou Student 1: 123, Endereço: Xihu District Student 2: 123, Endereço: Hangzhou City
Este resultado está de acordo com nossas idéias.
Finalmente, podemos dar uma olhada em uma das aulas da API que implementa o método do clone:
java.util.date:
/*** Retorne uma cópia deste objeto. */ public Object clone () {date d = null; tente {d = (date) super.clone (); if (cdate! = null) {d.cdate = (basecalendar.date) cdate.clone (); }} catch (clonenotsupportedException e) {} // não acontecerá retornar d; }Esta categoria é na verdade uma cópia profunda.
Clones rasos e clones profundos
1. Clonagem superficial
Na clonagem rasa, se a variável de membro do objeto protótipo for de um tipo de valor, uma cópia será copiada para o objeto clonado; Se a variável de membro do objeto protótipo for de um tipo de referência, o endereço do objeto de referência será copiado para o objeto clonado, ou seja, a variável de membro do objeto do protótipo e o objeto clonado apontar para o mesmo endereço de memória.
Simplificando, na clonagem superficial, quando o objeto é copiado, apenas a variável de membro do tipo de valor do objeto é copiada e o objeto membro do tipo de referência não é copiado.
No idioma Java, a clonagem superficial pode ser implementada substituindo o método clone () da classe de objeto.
2. Clonagem profunda
Na clonagem profunda, não importa se a variável de membro do objeto de protótipo é um tipo de valor ou um tipo de referência, uma cópia será copiada para o objeto clonado. A clonagem profunda também copia todos os objetos referenciados do objeto protótipo para o objeto clonado.
Simplificando, na clonagem profunda, exceto pelo próprio objeto que está sendo copiado, todas as variáveis de membro contidas no objeto também serão copiadas.
No idioma Java, se você precisar implementar a clonagem profunda, ela pode ser implementada sobrescrevendo o método clone () da classe de objeto, ou pode ser implementado por serialização, etc.
(Se o tipo de referência contiver muitos tipos de referência, ou a classe de tipo de referência interna contiver tipos de referência, será muito problemático usar o método clone. Nesse momento, podemos usar a serialização para implementar a clonagem profunda do objeto.)
A serialização é o processo de escrever objetos para um fluxo. O objeto gravado no fluxo é uma cópia do objeto original, e o objeto original ainda existe na memória. A cópia implementada por serialização pode não apenas copiar o próprio objeto, mas também copiar os objetos do membro que ele faz referência. Portanto, ao serializar o objeto para um fluxo e depois lendo -o para fora do fluxo, a clonagem profunda pode ser alcançada. Deve -se notar que a classe de um objeto que pode implementar a serialização deve implementar a interface serializável, caso contrário, a operação de serialização não pode ser implementada.
Estendido
O código da interface clonável e interface serializável fornecida pelo idioma Java é muito simples. Ambos são interfaces vazias. Essa interface vazia também é chamada de interface de identificação. Não há definição de qualquer método na interface de identificação. Sua função é dizer a Jre se as classes de implementação dessas interfaces têm uma certa função, como se elas suportam a clonagem, se apoiam a serialização etc.
Resolva problemas de clonagem de várias camadas
Se o tipo de referência contiver muitos tipos de referência, ou a classe de tipo de referência interna contiver tipos de referência, será muito problemático usar o método clone. Neste momento, podemos usar a serialização para implementar a clonagem profunda do objeto.
classe pública implementos externos serializáveis {private estático final serialversionuid = 369285298572941L; // É melhor declarar explicitamente o ID Public Interior; // Descrição: [Método de cópia profunda, requer o objeto e todas as propriedades do objeto a serem serializadas] Public Outer MyClone () {External Exterter = null; tente {// serialize o objeto em um fluxo, porque o que está escrito no fluxo é uma cópia do objeto, e o objeto original ainda existe na JVM. Portanto, usando esse recurso, você pode obter uma cópia profunda do objeto bytearrayOutputStream Baos = new ByteArrayOutputStream (); ObjectOutputStream ooS = new ObjectOutputStream (BAOS); OOS.WriteObject (this); // serialize o fluxo em um objeto bytearrayInputStream Bais = new ByteArrayInputStream (baos.tobytearray ()); ObjectInputStream ois = new ObjectInputStream (Bais); externo = (externo) ois.readObject (); } catch (ioexception e) {e.printStackTrace (); } catch (classNotFoundException e) {e.printStackTrace (); } retornar externo; }}O interior também deve implementar serializável, caso contrário, não pode ser serializado:
Public Classe Inteligentes Interior Serializável {private estático final serialversionuid = 872390113109L; // É melhor declarar explicitamente o nome de string público de identificação = ""; público interno (nome da string) {this.name = name; } @Override public string tostring () {return "O valor do nome do interior é:" + nome; }}Isso também pode permitir que os dois objetos existam completamente independentemente no espaço da memória sem afetar os valores um do outro.
Resumir
Existem duas maneiras de implementar a clonagem de objetos:
1). Implementar a interface clonável e substituir o método clone () na classe de objeto;
2). Implemente a interface serializável e implemente a clonagem por meio de serialização e desserialização do objeto, que podem realizar a verdadeira clonagem profunda.
Nota: A clonagem com base na serialização e deserialização não é apenas uma clonagem profunda, mas, mais importante, através da limitação genérica, pode -se verificar se o objeto a ser clonado suporta a serialização. Esta verificação é feita pelo compilador e não joga exceções em tempo de execução. Esta solução é obviamente melhor do que a clonagem de objetos usando o método clone da classe de objeto. É sempre melhor deixar o problema para o tempo de execução, expondo -o ao compilar.
O exposto acima é toda a explicação detalhada do código de clonagem de objetos de implementação de programação Java (cópia), espero que seja útil para todos. Amigos interessados podem continuar se referindo a outros tópicos relacionados neste site. Se houver alguma falha, deixe uma mensagem para apontá -la.