1. 简介
在java中除了基本的数据类型外, 还存在类实例对象的引用数据类型。在使用“=”作赋值操作时,对于基本数据类型,拷贝的是它的值;
对于引用数据类型,拷贝的是对这个对象的引用,拷贝对象与原对象仍然指向了同一对象。浅拷贝与深拷贝是在上述基础上进行区分。
在拷贝对象的过程中,如果对其基本数据类型进行拷贝,但对其引用数据类型只进行了引用传递(即没有创建新的对象),则认为该拷
贝是浅拷贝(shallow copy);在拷贝对象的过程中,如果对其基本数据类型拷贝,且在拷贝其引用数据类型时创建了新对象,则认
为该拷贝是深拷贝(deep copy)。
2. 示例
下面展示了clone方法和Cloneable接口。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // Object.java 2 /** 3 * Creates and returns a copy of this object. The precise meaning 4 * of "copy" may depend on the class of the object. The general 5 * intent is that, for any object {@code x}, the expression: 6 * <blockquote> 7 * <pre> 8 * x.clone() != x</pre></blockquote> 9 * will be true, and that the expression: 10 * <blockquote> 11 * <pre> 12 * x.clone().getClass() == x.getClass()</pre></blockquote> 13 * will be {@code true}, but these are not absolute requirements. 14 * While it is typically the case that: 15 * <blockquote> 16 * <pre> 17 * x.clone().equals(x)</pre></blockquote> 18 * will be {@code true}, this is not an absolute requirement. 19 * <p> 20 * By convention, the returned object should be obtained by calling 21 * {@code super.clone}. If a class and all of its superclasses (except 22 * {@code Object}) obey this convention, it will be the case that 23 * {@code x.clone().getClass() == x.getClass()}. 24 * <p> 25 * By convention, the object returned by this method should be independent 26 * of this object (which is being cloned). To achieve this independence, 27 * it may be necessary to modify one or more fields of the object returned 28 * by {@code super.clone} before returning it. Typically, this means 29 * copying any mutable objects that comprise the internal "deep structure" 30 * of the object being cloned and replacing the references to these 31 * objects with references to the copies. If a class contains only 32 * primitive fields or references to immutable objects, then it is usually 33 * the case that no fields in the object returned by {@code super.clone} 34 * need to be modified. 35 * <p> 36 * The method {@code clone} for class {@code Object} performs a 37 * specific cloning operation. First, if the class of this object does 38 * not implement the interface {@code Cloneable}, then a 39 * {@code CloneNotSupportedException} is thrown. Note that all arrays 40 * are considered to implement the interface {@code Cloneable} and that 41 * the return type of the {@code clone} method of an array type {@code T[]} 42 * is {@code T[]} where T is any reference or primitive type. 43 * Otherwise, this method creates a new instance of the class of this 44 * object and initializes all its fields with exactly the contents of 45 * the corresponding fields of this object, as if by assignment; the 46 * contents of the fields are not themselves cloned. Thus, this method 47 * performs a "shallow copy" of this object, not a "deep copy" operation. 48 * <p> 49 * The class {@code Object} does not itself implement the interface 50 * {@code Cloneable}, so calling the {@code clone} method on an object 51 * whose class is {@code Object} will result in throwing an 52 * exception at run time. 53 * 54 * @return a clone of this instance. 55 * @throws CloneNotSupportedException if the object's class does not 56 * support the {@code Cloneable} interface. Subclasses 57 * that override the {@code clone} method can also 58 * throw this exception to indicate that an instance cannot 59 * be cloned. 60 * @see java.lang.Cloneable 61 */ 62 protected native Object clone() throws CloneNotSupportedException; 63 64 65 66 // Cloneable.java 67 68 package java.lang; 69 70 /** 71 * A class implements the <code>Cloneable</code> interface to 72 * indicate to the {@link java.lang.Object#clone()} method that it 73 * is legal for that method to make a 74 * field-for-field copy of instances of that class. 75 * <p> 76 * Invoking Object's clone method on an instance that does not implement the 77 * <code>Cloneable</code> interface results in the exception 78 * <code>CloneNotSupportedException</code> being thrown. 79 * <p> 80 * By convention, classes that implement this interface should override 81 * <tt>Object.clone</tt> (which is protected) with a public method. 82 * See {@link java.lang.Object#clone()} for details on overriding this 83 * method. 84 * <p> 85 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method. 86 * Therefore, it is not possible to clone an object merely by virtue of the 87 * fact that it implements this interface. Even if the clone method is invoked 88 * reflectively, there is no guarantee that it will succeed. 89 * 90 * @author unascribed 91 * @see java.lang.CloneNotSupportedException 92 * @see java.lang.Object#clone() 93 * @since JDK1.0 94 */ 95 public interface Cloneable { 96 }
2.1 浅拷贝
构建一个Student类,该类存在引用类型的属性Address addr。为了实现对Student类对象的拷贝(使用clone()方法),
需要实现Cloneable接口,并且重写clone()方法。
1 package CloneExample; 2 3 public class Student implements Cloneable{ 4 private int id; 5 private String name; 6 private Address addr; 7 8 public Student(int id, String name, Address addr) { 9 this.id = id; 10 this.name = name; 11 this.addr = addr; 12 } 13 14 public int getId() { 15 return id; 16 } 17 18 public void setId(int id) { 19 this.id = id; 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public Address getAddr() { 31 return addr; 32 } 33 34 public void setAddr(Address addr) { 35 this.addr = addr; 36 } 37 38 @Override 39 public Object clone(){ 40 Student stu = null; 41 try{ 42 stu = (Student)super.clone(); // 浅拷贝 43 }catch(CloneNotSupportedException e) { 44 e.printStackTrace(); 45 } 46 return stu; 47 } 48 49 @Override 50 public String toString() { 51 return "Student{" + 52 "id=" + id + 53 ", name='" + name + '\'' + 54 ", addr=" + addr + 55 '}'; 56 } 57 } 58 59 class Address { 60 private String addr; 61 62 public Address(String addr) { 63 this.addr = addr; 64 } 65 66 public String getAddr() { 67 return addr; 68 } 69 70 public void setAddr(String addr) { 71 this.addr = addr; 72 } 73 74 @Override 75 public String toString() { 76 return '\'' + addr + '\''; 77 } 78 }
查看结果如下,可以发现修改name属性时,两个对象没用同步修改;但是修改addr属性时,两个对象的addr属性同步改变。
此时由于对Student对象的Address addr属性只是引用传递,所以称对Student对象进行了浅拷贝。
1 public static void main(String[] args){ 2 Address addr = new Address("nanjing"); 3 Student stu1 = new Student(1, "stu1", addr); 4 Student stu2 = (Student) stu1.clone(); // 浅拷贝 5 System.out.println(stu1==stu2); // false 6 System.out.println(stu1.getAddr()==stu2.getAddr()); // true 7 stu2.setName("stu2"); 8 System.out.println(stu1.toString()); // Student{id=1, name='stu1', addr='nanjing'} 9 System.out.println(stu2.toString()); // Student{id=1, name='stu2', addr='nanjing'} 10 addr.setAddr("shanghai"); 11 System.out.println(stu1.toString()); // Student{id=1, name='stu1', addr='shanghai'} 12 System.out.println(stu2.toString()); // Student{id=1, name='stu2', addr='nanjing'} 13 }
2.2 深拷贝
那么如何实现对Student对象的深拷贝呢?
1. 对该对象内部的引用数据类型的类实现Cloneable接口和重写clone()方法。
2. 对该对象进行序列化,之后再进行反序列化。
2.2.1 Address类实现Cloneable接口和重写clone()方法
1 class Address implements Cloneable{ 2 private String addr; 3 4 public Address(String addr) { 5 this.addr = addr; 6 } 7 8 public String getAddr() { 9 return addr; 10 } 11 12 public void setAddr(String addr) { 13 this.addr = addr; 14 } 15 16 @Override 17 public Object clone() { 18 Address addr = null; 19 try{ 20 addr = (Address)super.clone(); 21 }catch(CloneNotSupportedException e) { 22 e.printStackTrace(); 23 } 24 return addr; 25 } 26 27 @Override 28 public String toString() { 29 return '\'' + addr + '\''; 30 } 31 }
2.2.2 继续重写Student类的clone()方法
1 @Override 2 public Object clone(){ 3 Student stu = null; 4 try{ 5 stu = (Student)super.clone(); // 浅拷贝 6 stu.addr = (Address) this.addr.clone(); // new add 7 }catch(CloneNotSupportedException e) { 8 e.printStackTrace(); 9 } 10 return stu; 11 }
查看结果:
可以发现,此时修改addr属性,只有stu1进行了改变,stu2没有发生变换。
1 public static void main(String[] args){ 2 Address addr = new Address("nanjing"); 3 Student stu1 = new Student(1, "stu1", addr); 4 Student stu2 = (Student) stu1.clone(); // 浅拷贝 5 System.out.println(stu1==stu2); // false 6 System.out.println(stu1.getAddr()==stu2.getAddr()); // false 7 stu2.setName("stu2"); 8 System.out.println(stu1.toString()); // Student{id=1, name='stu1', addr='nanjing'} 9 System.out.println(stu2.toString()); // Student{id=1, name='stu2', addr='nanjing'} 10 addr.setAddr("shanghai"); 11 System.out.println(stu1.toString()); // Student{id=1, name='stu1', addr='shanghai'} 12 System.out.println(stu2.toString()); // Student{id=1, name='stu2', addr='nanjing'} 13 }
2.2.3 Student和Address类通过Serializable接口序列化
1 class Student implements Serializable{ 2 private int id; 3 private String name; 4 private Address addr; 5 6 public Student(int id, String name, Address addr) { 7 this.id = id; 8 this.name = name; 9 this.addr = addr; 10 } 11 12 public void setName(String name) { 13 this.name = name; 14 } 15 16 public Address getAddr() { 17 return addr; 18 } 19 20 @Override 21 public String toString() { 22 return "Student{" + 23 "id=" + id + 24 ", name='" + name + '\'' + 25 ", addr=" + addr + 26 '}'; 27 } 28 29 public Student deepClone() throws IOException, ClassNotFoundException, OptionalDataException 30 { 31 //将对象写入流中 32 ByteArrayOutputStream bao=new ByteArrayOutputStream(); 33 ObjectOutputStream oos=new ObjectOutputStream(bao); 34 oos.writeObject(this); 35 36 //将对象从流中取出 37 ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray()); 38 ObjectInputStream ois=new ObjectInputStream(bis); 39 return (Student) ois.readObject(); 40 } 41 42 43 44 class Address implements Serializable { 45 private String addr; 46 47 public Address(String addr) { 48 this.addr = addr; 49 } 50 51 public void setAddr(String addr) { 52 this.addr = addr; 53 } 54 55 @Override 56 public String toString() { 57 return '\'' + addr + '\''; 58 } 59 }
查看结果:
可以发现,此时修改addr属性,只有stu1进行了改变,stu2没有发生变换。
1 public static void main(String[] args){ 2 Address addr = new Address("nanjing"); 3 Student stu1 = new Student(1, "stu1", addr); 4 Student stu2 = (Student) stu1.clone(); // 深拷贝 5 System.out.println(stu1==stu2); // false 6 System.out.println(stu1.getAddr()==stu2.getAddr()); // false 7 stu2.setName("stu2"); 8 System.out.println(stu1.toString()); // Student{id=1, name='stu1', addr='nanjing'} 9 System.out.println(stu2.toString()); // Student{id=1, name='stu2', addr='nanjing'} 10 addr.setAddr("shanghai"); 11 System.out.println(stu1.toString()); // Student{id=1, name='stu1', addr='shanghai'} 12 System.out.println(stu2.toString()); // Student{id=1, name='stu2', addr='nanjing'} 13 }
3. 总结
综上所述可知,浅拷贝指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,
没有对引用指向的对象进行拷贝(新建一个对象)。而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。
!!!