English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Dettagli della meccanica di copia profonda e copia superficiale Java
Summary:
In Java, copying is divided into deep copy and shallow copy. Java implements a method called clone in the public superclass Object, and the new object cloned by this method is a shallow copy, while the deep copy is achieved by defining a clone method by yourself.
(一)Object中的clone方法
如果我们new出一个新对象,用一个声明去引用它,之后又用另一个声明去引用前一个声明,那么最后的结果是:这两个声明的变量将指向同一个对象,一处被改全部被改。如果我们想创建一个对象的copy,这个copy和对象的各种属性完全相同,而且修改这个copy和原对象毫无关系,那么这个时候我们就要用到clone方法。
package Clone; import java.util.Date; /** * * @author QuinnNorris * java中的两种拷贝机制 */ public class Clone { /** * @param args * @throws CloneNotSupportedException */ public static void main(String[] args) throws CloneNotSupportedException { // TODO 自动生成的方法存根 ClassA valA = new ClassA(1, "old", new Date()); // 声明一个新的ClassA对象,我们不需要太关注ClassA的功能 ClassA valB = valA; // 将valA引用的对象赋给valB valA.setObject("new"); // 更改valA中的值,此时valB也被更改了,因为valA和valB指向同一个对象 valB = valA.clone(); // 通过clone方法制造副本 } }
ClassA类中关于clone方法的重写部分:
// 需要实现Cloneable接口 public class ClassA implements Cloneable { public ClassA clone() throws CloneNotSupportedException { return (ClassA) super.clone(); // 调用父类(Object)的clone方法 } }
1.如何使用Object中的clone方法
Qualcuno ha sintetizzato quattro regole per l'uso del metodo clone, condividiamo insieme:
2. Il metodo clone protetto
In java.lang.Object, imposta il metodo clone come protetto, che è una situazione molto speciale. L'area di applicazione di protected è: visibile nel pacchetto + ereditabile. La ragione per cui è impostato così è che il metodo deve restituire l'oggetto clonato, quindi il tipo del tipo da clonare dal metodo clone è sconosciuto, non è possibile determinare il tipo del valore di ritorno, quindi naturalmente solo i discendenti possono implementarlo e riscriverlo. Per permettere ai discendenti di ereditare senza diventare troppo aperto, è impostato come tipo protected.
3. Implementare il metodo clone richiede di implementare l'interfaccia Cloneable
Allora perché dobbiamo implementare l'interfaccia Cloneable quando riscriviamo il metodo clone? In realtà, l'interfaccia Cloneable è un'interfaccia di etichetta in Java, l'interfaccia di etichetta è un'interfaccia che non ha metodi e attributi, esiste solo per far sapere alcune informazioni, e può essere giudicata quando si utilizza: xxx instanceof Cloneable. L'uso dell'interfaccia Cloneable è per far sapere ai designer che devono eseguire il processo di clonazione. Se un oggetto deve essere clonato ma non ha implementato (in realtà, sostituire “implementato” con “scritto” è più accurato) l'interfaccia Cloneable, allora si genererà un'eccezione verificata.
4. Implementare il metodo clone richiede di chiamare il metodo clone della superclasse
Per raggiungere l'obiettivo di creare un oggetto identico al questo oggetto chiamante il metodo, dobbiamo utilizzare il metodo clone della superclasse, e la superclasse fa lo stesso, fino a raggiungere il metodo clone di Object, allora qual è l'uso del metodo clone di Object? La descrizione dell'API è questa:
protected Object clone( ) throws CloneNotSupportedException
creare e restituire una copia di questo oggetto.
Il significato esatto di “copia” potrebbe dipendere dalla classe dell'oggetto. Lo scopo di fare questo è, per qualsiasi oggetto x,
L'espressione: x.clone() != x è true.
L'espressione: x.clone().getClass() == x.getClass() è anche true.
Ma questi non sono requisiti necessari da soddisfare.
Di solito:
x.clone().equals(x) è true, ma non è un requisito necessario.
Per convenzione, l'oggetto restituito dovrebbe essere ottenuto chiamando super.clone.
Se una classe e tutte le sue superclassi (eccetto Object) rispettano questo accordo, allora x.clone().getClass() == x.getClass().
Questo è una parte della spiegazione di base di API per clone. Possiamo trarre la conclusione che, se si chiama super.clone in modo ragionevole, tornerà un oggetto clonato. Durante l'esecuzione, il clone() di Object riconosce quale oggetto si sta copiando, assegna spazio per questo oggetto e esegue la copia, copiando il contenuto dell'oggetto originale in ogni spazio di archiviazione dell'oggetto nuovo. In questo oggetto clonato, tutte le proprietà sono le stesse dell'oggetto originale, e queste proprietà sono di due tipi:
Primo: gli otto tipi primitivi e gli oggetti immutabili (ad esempio String)
Secondo: oggetti di altre classi
Per il primo caso, il metodo clone imposta i loro valori al valore dell'oggetto originale, senza problemi. Per il secondo caso, il metodo clone punta semplicemente la nuova copia dell'oggetto alla referenza che l'oggetto originale punta, e l'oggetto del secondo tipo sarà modificato da entrambi gli oggetti. A questo punto entra in gioco il concetto di copia profonda e superficiale.
(Due) Copia superficiale
Copia superficiale: tutte le variabili dell'oggetto copiato contengono valori identici a quelli dell'oggetto originale, mentre tutte le referenze ad altri oggetti puntano ancora all'oggetto originale. In altre parole, la copia superficiale copia solo l'oggetto considerato, non copia gli oggetti che esso cita.
Per esempio, supponiamo che una classe A abbia una variabile di tipo B. Quando A sovrascrive il metodo clone chiamando super.clone, l'oggetto nuovo creato e la variabile di tipo B nell'oggetto originale sono gli stessi, e puntano allo stesso tipo di variabile B. Se si modifica la variabile B in A, la variabile B nella copia nuova verrà modificata allo stesso modo.
Ricorda, tutti i metodi clone chiamati direttamente con super.clone sono copie superficiali.
(Tre) Copia profonda
Copia profonda: tutte le variabili dell'oggetto copiato contengono valori identici a quelli dell'oggetto originale, eccetto quelle che riferiscono ad altri oggetti. Quelle che riferiscono ad altri oggetti puntano agli oggetti nuovi copiati, non più agli oggetti originali citati. In altre parole, la copia profonda copia tutti gli oggetti citati dall'oggetto da copiare.
In modo通俗,se si parla di copia superficiale, all'inizio ci sono due linee, se all'ultimo c'è una variabile di un altro tipo, queste due linee alla fine si fonderanno in una sola, che punta a questa variabile, e a entrambe è possibile eseguire operazioni su di essa. La copia profonda è completamente due linee, che non si intromettono l'una nell'altra, perché ha copiato tutti gli oggetti delle variabili interne.
Per fare una copia profonda nel codice, è necessario scrivere più chiamate al metodo clone di altre classi nel metodo clone.
(IV) Copia profonda serializzata
A volte, all'interno di una struttura di framework, non troviamo la sovrascrittura del metodo clone, quindi come operiamo quando abbiamo bisogno di copiare un oggetto? La risposta è che spesso utilizziamo il metodo di serializzazione, implementando l'interfaccia Serializable.
Cercare altri metodi per sostituire la copia profonda è una cosa inevitabile, se si utilizza la copia profonda tradizionale, non dovresti copiare un oggetto cercando di copiare tutte le variabili di oggetto a più livelli? Senza parlare del consumo di tempo, solo scrivere questo tipo di codice può essere spaventoso. La serializzazione della copia profonda è un metodo relativamente semplice.
Il processo di scrivere un oggetto su un flusso è chiamato serializzazione (Serialization), ma nel cerchio degli sviluppatori Java è anche chiamato processi di 'congelamento' o 'fermaggio (picking)'; mentre il processo di lettura di un oggetto dal flusso è chiamato deserializzazione (Deserialization) e viene chiamato 'sciogliere il ghiaccio' o 'riportare alla vita (depicking)'. Occorre notare che ciò che viene scritto sul flusso è una copia dell'oggetto, mentre l'oggetto originale rimane nel JVM, quindi 'fermato in salamoia' è solo una copia dell'oggetto, e il salame Java può essere riportato alla vita.
Questa è una spiegazione professionale trovata online, non intendo addentrarmi ulteriormente in questo argomento. In Java, per fare una copia profonda di un oggetto, spesso si può prima far implementare all'oggetto l'interfaccia Serializable, poi scrivere l'oggetto (in realtà solo una copia dell'oggetto) su un flusso (fermato in salamoia), e poi leggerlo dal flusso (riportato alla vita), così si può ricostruire l'oggetto.
public Object deepClone() { //写入对象 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this); //读取对象 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); }
Anche se il codice accademico sembra molto complesso, in realtà è solo mettere l'oggetto in un flusso e prenderlo fuori. In confronto con l'analisi e il giudizio di无数的clone, questo è semplicemente molto semplice. Fare questo richiede che l'oggetto e tutti gli oggetti referenziati internamente siano serializzabili, altrimenti, è necessario esaminare attentamente se gli oggetti non serializzabili sono impostati come transient.
transient: un oggetto che ha implementato l'interfaccia Serilizable può essere serializzato (la serializzazione è il processo di scrittura del codice java in forma di sequenza di byte, ovvero le prime tre righe del codice sopra), questo modello di serializzazione di Java fornisce molti vantaggi ai sviluppatori, non è necessario preoccuparsi del processo di serializzazione specifico, basta che la classe implementi l'interfaccia Serilizable, tutte le proprietà e i metodi di questa classe verranno serializzati automaticamente. Tuttavia, c'è una situazione in cui alcune proprietà non necessitano di un numero di serie, quindi si utilizza questa parola chiave. Basta implementare l'interfaccia Serilizable e aggiungere la parola chiave transient davanti alla proprietà che non necessita di serializzazione, durante la serializzazione dell'oggetto, questa proprietà non verrà serializzata nel destinazione specificato.
(V) Conclusione
Nell'applicazione pratica, deep copy e shallow copy sono solo due concetti, non necessariamente uno è migliore dell'altro, è necessario determinare come copiare un oggetto in base al lavoro effettivo. Nel caso delle operazioni di database, per estrarre una tabella senza coinvolgere altre tabelle, è necessario utilizzare una copia superficiale. Nel framework Serializable, anche se richiede più tempo, la copia profonda è assolutamente necessaria.
Grazie per la lettura, speriamo di essere stati utili a tutti, grazie per il supporto al nostro sito!
Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il copyright è dell'autore originale, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una e-mail a notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito eliminerà immediatamente il contenuto sospetto di violazione del copyright.