1. 簡介java
在java中除了基本的數據類型外, 還存在類實例對象的引用數據類型。在使用「=」做賦值操做時,對於基本數據類型,拷貝的是它的值;express
對於引用數據類型,拷貝的是對這個對象的引用,拷貝對象與原對象仍然指向了同一對象。淺拷貝與深拷貝是在上述基礎上進行區分。ide
在拷貝對象的過程當中,若是對其基本數據類型進行拷貝,但對其引用數據類型只進行了引用傳遞(即沒有建立新的對象),則認爲該拷ui
貝是淺拷貝(shallow copy);在拷貝對象的過程當中,若是對其基本數據類型拷貝,且在拷貝其引用數據類型時建立了新對象,則認this
爲該拷貝是深拷貝(deep copy)。spa
2. 示例3d
下面展現了clone方法和Cloneable接口。code
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 淺拷貝orm
構建一個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. 總結
綜上所述可知,淺拷貝指在拷貝對象時,對於基本數據類型的變量會從新複製一份,而對於引用類型的變量只是對引用進行拷貝,
沒有對引用指向的對象進行拷貝(新建一個對象)。而深拷貝是指在拷貝對象時,同時會對引用指向的對象進行拷貝。
!!!