In Java wird das Kopieren in zwei Arten unterteilt: Deep Copy und Shallow Copy. Java implementiert eine Methode namens clone in der öffentlichen Oberklasse Object. Das von dieser Methode geklonte neue Objekt ist eine flache Kopie, und die von Ihnen definierte Klonmethode ist eine tiefe Kopie.
Wenn wir ein neues Objekt erstellen, verwenden Sie eine Anweisung, um darauf zu verweisen, und verwenden Sie dann eine andere Anweisung, um auf die vorherige Deklaration zu verweisen , dann ist das Endergebnis: Die beiden deklarierten Variablen zeigen auf dasselbe Objekt, und wenn eine Stelle geändert wird, werden beide geändert. Wenn wir eine Kopie eines Objekts erstellen möchten, die genau mit den verschiedenen Eigenschaften des Objekts übereinstimmt, und die Änderung der Kopie nichts mit dem Originalobjekt zu tun hat, müssen wir zu diesem Zeitpunkt die Klonmethode verwenden.
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 Auto-generated method stub 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方法制造副本 } }
Der überschreibende Teil der Klonmethode in der ClassA-Klasse:
//需要实现Cloneable接口public class ClassA implements Cloneable { public ClassA clone() throws CloneNotSupportedException { return (ClassA) super.clone(); //调用父类(Object)的clone方法 } }
Jemand hat die vier Regeln für die Verwendung der Klonmethode zusammengefasst:
Um eine Kopie des Objekts zu erhalten, können wir clone() verwenden. Methode der Object-Klasse.
Überschreiben Sie die clone()-Methode der Basisklasse in der abgeleiteten Klasse und deklarieren Sie sie als öffentlich.
Rufen Sie in der clone()-Methode der abgeleiteten Klasse super.clone() auf.
Implementieren Sie die Cloneable-Schnittstelle in der abgeleiteten Klasse.
In java.lang.Object setzt er die Klonmethode auf protected Modifikation, dies ist eine ganz besondere Situation. Der Schutzumfang ist: Paket sichtbar + vererbbar. Der Grund für diese Einstellung liegt darin, dass diese Methode ein geklontes Objekt zurückgibt, d. h. der Typ der Klonmethode ist unbekannt. Es gibt keine Möglichkeit, den Typ des Rückgabewerts zu bestimmen. Zukünftige Generationen können ihn natürlich nur implementieren und neu schreiben. Damit Nachkommen erben können, ohne zu offen zu sein, wird es auf einen geschützten Typ festgelegt.
Warum müssen wir also die Cloneable-Schnittstelle implementieren, wenn wir die Klonmethode neu schreiben? Tatsächlich handelt es sich bei der klonbaren Schnittstelle um eine markierte Schnittstelle in Java. Markierte Schnittstellen beziehen sich auf Schnittstellen ohne Methoden und Attribute. Sie existieren nur, um jedem einige Informationen mitzuteilen, und sie können beurteilt werden, wenn sie verwendet werden: xxx Instanz von Cloneable. Das Aufkommen der klonbaren Schnittstelle soll Designer darüber informieren, dass Klonen erforderlich ist. Wenn ein Objekt geklont werden muss, aber die klonbare Schnittstelle nicht implementiert (eigentlich wird „Implementierung“ hier durch „Schreiben“ ersetzt), wird eine geprüfte Ausnahme generiert.
Um ein Objekt zu kopieren, das genau mit dem Objekt übereinstimmt Wenn die Methode aufgerufen wird, müssen wir die Klonmethode der übergeordneten Klasse usw. für die übergeordnete Klasse verwenden, bis die Klonmethode von Object erreicht ist. Wozu dient dann die Klonmethode von Object? Das sagt die API:
protected Object clone( ) throws CloneNotSupportedException
Erstellt eine Kopie dieses Objekts und gibt sie zurück.
Die genaue Bedeutung von „kopieren“ kann von der Klasse des Objekts abhängen. Dies bewirkt, dass für jedes Objekt x
gilt Ausdruck: x.clone() != x ist wahr,
Ausdruck: x.clone().getClass() == x.getClass() ist auch wahr,
Dies sind jedoch keine Anforderungen, die erfüllt werden müssen.
Normalerweise gilt:
x.clone().equals(x) ist wahr, aber dies ist keine Anforderung, die erfüllt werden muss.
Konventionell sollte das zurückgegebene Objekt durch Aufruf von super.clone abgerufen werden.
Wenn eine Klasse und alle ihre Superklassen (außer Object) dieser Konvention entsprechen, dann x.clone().getClass() == x.getClass().
Das Obige ist eine grundlegende Erklärung des Klonens in der API. Daraus können wir schließen, dass spuer.clone() ein geklontes Objekt zurückgibt, solange es ordnungsgemäß aufgerufen wird. Zur Laufzeit identifiziert clone() in Object, welches Objekt Sie kopieren möchten, weist diesem Objekt dann Speicherplatz zu, kopiert das Objekt und kopiert den Inhalt des ursprünglichen Objekts nacheinander in den Speicherplatz des neuen Objekts. In diesem geklonten Objekt sind alle Attribute mit denen des geklonten Objekts identisch, und diese gleichen Attribute sind in zwei Typen unterteilt:
Der erste: acht primitive Typen und unveränderliche Objekte (z. B. String )
Zweiter Typ: Andere Klassenobjekte
Beim ersten Typ setzt die Klonmethode ihren Wert problemlos auf den Wert des Originalobjekts. Für den zweiten Typ verweist die Klonmethode einfach auf die Referenz des kopierten neuen Objekts auf die Referenz, auf die das ursprüngliche Objekt zeigt. Der zweite Typ von Klassenobjekten wird durch die beiden Objekte geändert. Diesmal geht es also um das Konzept der tiefen und flachen Kopien.
Flache Kopie: Alle Variablen des kopierten Objekts enthalten dieselben Werte wie das Originalobjekt und alle Verweise auf andere Objekte sind weiterhin vorhanden zeigt auf das Originalobjekt. Mit anderen Worten: Eine flache Kopie kopiert nur das betreffende Objekt, nicht die Objekte, auf die sie verweist.
比如举个例子,一个类A中有另外一个类B类型的变量。在A重写clone函数调用super.clone的时候,创建的新对象和原来对象中的类B类型的变量是同一个,他们指向了同一个B的类型变量。如果在A中对B的变量做了修改,在新的拷贝出来的对象中B的变量也会被同样的修改。
请记住,直接调用super.clone实现的clone方法全部都是浅拷贝。
深拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
通俗的说,如果说浅拷贝,开始的时候是两条线,如果在最后有一个其他类的变量,那么这两条线最后会合二为一,共同指向这变量,都能对他进行操作。深拷贝则是完完全全的两条线,互不干涉,因为他已经把所有的内部中的变量的对象全都复制一遍了。
深拷贝在代码中,需要在clone方法中多书写调用这个类中其他类的变量的clone函数。
在框架中,有的时候我们发现其中并没有重写clone方法,那么我们在需要拷贝一个对象的时候是如何去操作的呢?答案是我们经常会使用串行化方法,实现Serializable接口。
去寻找其他的方法来替代深拷贝也是无可奈何的事情,如果采用传统的深拷贝,难道你拷贝一个对象的时候向其中追无数层来拷贝完所有的对象变量么?先不谈这么做的时间消耗,仅仅是写这样的代码都会让人望而生畏。串行化深拷贝就是这样一个相对简单的方法。
把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。
上面是网上的专业解释,我也不在这里班门弄斧了。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。
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()); }
虽然这种学院派的代码看起来很复杂,其实只是把对象放到流里,再拿出来。相比较分析判断无数的clone,这样简直是再简单不过了。这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient。
transient:一个对象只要实现了Serilizable接口,这个对象就可以被序列化(序列化是指将java代码以字节序列的形式写出,即我们上面代码前三行写入对象),Java的这种序列化模式为开发者提供了很多便利,可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个的所有属性和方法都会自动序列化。但是有种情况是有些属性是不需要序列号的,所以就用到这个关键字。只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
在实际的应用中,深拷贝和浅拷贝只是两个概念,不一定谁比谁好,要按照实际的工作来确定如何去拷贝一个对象。如果在数据库操作方面,为了取出一张表时不涉及其他的表,肯定需要使用浅拷贝,而在框架的Serializable中,虽然耗时,但是深拷贝是非常有必要的。
在java中,拷贝分为深拷贝和浅拷贝两种。java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝。
以上就是java拷贝机制详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!