In Java, copying is divided into two types: deep copy and shallow copy. Java implements a method called clone in the public superclass Object. The new object cloned by this method is a shallow copy, and the clone method defined by yourself is a deep copy.
If we new a new object, use a statement to reference it, and then use another statement to reference the previous one declaration, then the final result is: the two declared variables will point to the same object, and if one place is changed, both will be changed. If we want to create a copy of an object that is exactly the same as the various properties of the object, and modifying the copy has nothing to do with the original object, then we need to use the clone method at this time.
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方法制造副本 } }
The overriding part of the clone method in the ClassA class:
//需要实现Cloneable接口public class ClassA implements Cloneable { public ClassA clone() throws CloneNotSupportedException { return (ClassA) super.clone(); //调用父类(Object)的clone方法 } }
Someone summarized the use of clone Let’s share the four rules of methods:
In order to obtain a copy of the object, we can use the clone() method of the Object class.
Override the clone() method of the base class in the derived class and declare it as public.
In the clone() method of the derived class, call super.clone().
Implement the Cloneable interface in the derived class.
In java.lang.Object, he sets the clone method to protected Modification, this is a very special situation. The scope of protected is: package visible + inheritable. The reason for this setting is because this method returns a cloned object. That is, the type of clone method is unknown. There is no way to determine the type of the return value. Naturally, future generations can only implement it and rewrite it. , in order to allow descendants to inherit without being too open, it is set to a protected type.
So why should we implement the Cloneable interface when we rewrite the clone method? In fact, the Cloneable interface is a marked interface in Java. Marked interfaces refer to interfaces without methods and attributes. They exist only to let everyone know some information, and they can be judged when using: xxx instanceof Cloneable. The emergence of the Cloneable interface is to let designers know that cloning is required. If an object needs to be cloned but does not implement (actually, "implementation" here is replaced by "write" is more accurate) the Cloneable interface, a checked exception will be generated.
In order to copy an object that is exactly the same as the object that calls the method, we need to use the parent class The clone method of a class, and so on for the parent class, until the clone method of Object is reached, then what is the use of the clone method of Object? This is what the API says:
protected Object clone( ) throws CloneNotSupportedException
Creates and returns a copy of this object.
The precise meaning of "copy" may depend on the object's class. The purpose of this is that, for any object x,
Expression: x.clone() != x is true,
Expression: x.clone().getClass() == x.getClass() is also true,
But these are not requirements that must be met.
Under normal circumstances:
x.clone().equals(x) is true, but this is not a requirement that must be met.
By convention, the returned object should be obtained by calling super.clone.
If a class and all its superclasses (except Object) comply with this convention, then x.clone().getClass() == x.getClass().
The above is a basic explanation of clone in the API. What we can conclude is that as long as spuer.clone() is called properly, it will return a cloned object. At runtime, clone() in Object identifies which object you want to copy, then allocates space for this object, copies the object, and copies the contents of the original object one by one to the storage space of the new object. In this cloned object, all attributes are the same as those of the cloned object, and these same attributes are divided into two types:
The first type: eight primitive types and immutable objects (such as String)
The second type: other class objects
For the first type, the clone method sets their values to the values of the original objects without any problems. For the second type, the clone method simply points the reference of the copied new object to the reference pointed by the original object. The second type of class object will be modified by the two objects. So this time it involves the concept of deep and shallow copies.
Shallow copy: All variables of the copied object contain the same values as the original object, and all references to other objects Still points to the original object. In other words, a shallow copy only copies the object in question, not the objects it refers to.
比如举个例子,一个类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)!