By
    
      yusijia
    
  
    
    Updated:
    
  
	
		
		
		
		
		http://blog.csdn.net/tounaobun/article/details/8491392
假如说你想复制一个简单变量。很简单:
- int apples = 5; 
 
- int pears = apples; 
 
- 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。
但是如果你复制的是一个对象,情况就有些复杂了。
假设说我是一个beginner,我会这样写: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | 1.	class Student {  2.	    private int number;  3.	  4.	    public int getNumber() {  5.	        return number;  6.	    }  7.	  8.	    public void setNumber(int number) {  9.	        this.number = number;  10.	    }  11.	      12.	}  13.	public class Test {  14.	      15.	    public static void main(String args[]) {  16.	          17.	        Student stu1 = new Student();  18.	        stu1.setNumber(12345);  19.	        Student stu2 = stu1;  20.	          21.	        System.out.println("学生1:" + stu1.getNumber());  22.	        System.out.println("学生2:" + stu2.getNumber());  23.	    }  24.	}  打印结果:   1.	学生1:12345  2.	学生2:12345
  | 
 
这里我们自定义了一个学生类,该类只有一个number字段。
我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)
再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,
难道真的是这样吗?
我们试着改变stu2实例的number字段,再打印结果看看:
1 2 3 4 5 6 7
   | 1.	stu2.setNumber(54321);  2.	  3.	System.out.println("学生1:" + stu1.getNumber());  4.	System.out.println("学生2:" + stu2.getNumber());  打印结果: 1.	学生1:54321  2.	学生2:54321
   | 
 
这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?
原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,
这样,stu1和stu2指向内存堆中同一个对象。
那么,怎样才能达到复制一个对象呢?
是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。
该方法的签名是:
protected native Object clone() throws CloneNotSupportedException;
因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。
要想对一个对象进行复制,就需要对clone方法覆盖。
一般步骤是(浅复制):
被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
 
覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
下面对上面那个方法进行改造:
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
   | 1.	class Student implements Cloneable{  2.	    private int number;  3.	  4.	    public int getNumber() {  5.	        return number;  6.	    }  7.	  8.	    public void setNumber(int number) {  9.	        this.number = number;  10.	    }  11.	      12.	    @Override  13.	    public Object clone() {  14.	        Student stu = null;  15.	        try{  16.	            stu = (Student)super.clone();  17.	        }catch(CloneNotSupportedException e) {  18.	            e.printStackTrace();  19.	        }  20.	        return stu;  21.	    }  22.	}  23.	public class Test {  24.	      25.	    public static void main(String args[]) {  26.	          27.	        Student stu1 = new Student();  28.	        stu1.setNumber(12345);  29.	        Student stu2 = (Student)stu1.clone();  30.	          31.	        System.out.println("学生1:" + stu1.getNumber());  32.	        System.out.println("学生2:" + stu2.getNumber());  33.	          34.	        stu2.setNumber(54321);  35.	      36.	        System.out.println("学生1:" + stu1.getNumber());  37.	        System.out.println("学生2:" + stu2.getNumber());  38.	    }  39.	}  40.	学生1:12345  41.	学生2:12345  42.	学生1:12345  43.	学生2:54321
  | 
 
上面的复制被称为浅复制(Shallow Copy),还有一种稍微复杂的深度复制(deep copy):
我们在学生类里再加一个Address类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
   | 1.	class Address  {  2.	    private String add;  3.	  4.	    public String getAdd() {  5.	        return add;  6.	    }  7.	  8.	    public void setAdd(String add) {  9.	        this.add = add;  10.	    }  11.	      12.	}  13.	  14.	class Student implements Cloneable{  15.	    private int number;  16.	  17.	    private Address addr;  18.	      19.	    public Address getAddr() {  20.	        return addr;  21.	    }  22.	  23.	    public void setAddr(Address addr) {  24.	        this.addr = addr;  25.	    }  26.	  27.	    public int getNumber() {  28.	        return number;  29.	    }  30.	  31.	    public void setNumber(int number) {  32.	        this.number = number;  33.	    }  34.	      35.	    @Override  36.	    public Object clone() {  37.	        Student stu = null;  38.	        try{  39.	            stu = (Student)super.clone();  40.	        }catch(CloneNotSupportedException e) {  41.	            e.printStackTrace();  42.	        }  43.	        return stu;  44.	    }  45.	}  46.	public class Test {  47.	      48.	    public static void main(String args[]) {  49.	          50.	        Address addr = new Address();  51.	        addr.setAdd("杭州市");  52.	        Student stu1 = new Student();  53.	        stu1.setNumber(123);  54.	        stu1.setAddr(addr);  55.	          56.	        Student stu2 = (Student)stu1.clone();  57.	          58.	        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  59.	        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  60.	    }  61.	}  62.	学生1:123,地址:杭州市  63.	学生2:123,地址:杭州市
  | 
 
乍一看没什么问题,真的是这样吗?
我们在main方法中试着改变addr实例的地址。
1 2 3 4 5 6 7 8
   | 1.	addr.setAdd("西湖区");  2.	  3.	System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  4.	System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  5.	学生1:123,地址:杭州市  6.	学生2:123,地址:杭州市  7.	学生1:123,地址:西湖区  8.	学生2:123,地址:西湖区
  | 
 
这就奇怪了,怎么两个学生的地址都改变了?
原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
   | 1.	package abc;  2.	  3.	class Address implements Cloneable {  4.	    private String add;  5.	  6.	    public String getAdd() {  7.	        return add;  8.	    }  9.	  10.	    public void setAdd(String add) {  11.	        this.add = add;  12.	    }  13.	      14.	    @Override  15.	    public Object clone() {  16.	        Address addr = null;  17.	        try{  18.	            addr = (Address)super.clone();  19.	        }catch(CloneNotSupportedException e) {  20.	            e.printStackTrace();  21.	        }  22.	        return addr;  23.	    }  24.	}  25.	  26.	class Student implements Cloneable{  27.	    private int number;  28.	  29.	    private Address addr;  30.	      31.	    public Address getAddr() {  32.	        return addr;  33.	    }  34.	  35.	    public void setAddr(Address addr) {  36.	        this.addr = addr;  37.	    }  38.	  39.	    public int getNumber() {  40.	        return number;  41.	    }  42.	  43.	    public void setNumber(int number) {  44.	        this.number = number;  45.	    }  46.	      47.	    @Override  48.	    public Object clone() {  49.	        Student stu = null;  50.	        try{  51.	            stu = (Student)super.clone();    52.	        }catch(CloneNotSupportedException e) {  53.	            e.printStackTrace();  54.	        }  55.	        stu.addr = (Address)addr.clone();    56.	        return stu;  57.	    }  58.	}  59.	public class Test {  60.	      61.	    public static void main(String args[]) {  62.	          63.	        Address addr = new Address();  64.	        addr.setAdd("杭州市");  65.	        Student stu1 = new Student();  66.	        stu1.setNumber(123);  67.	        stu1.setAddr(addr);  68.	          69.	        Student stu2 = (Student)stu1.clone();  70.	          71.	        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  72.	        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  73.	          74.	        addr.setAdd("西湖区");  75.	          76.	        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  77.	        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  78.	    }  79.	}  80.	学生1:123,地址:杭州市  81.	学生2:123,地址:杭州市  82.	学生1:123,地址:西湖区  83.	学生2:123,地址:杭州市
   | 
 
这样结果就符合我们的想法了。
总结:
浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,没有对引用指向的对象进行拷贝。
 
但对于String类有的特殊,拷贝的也是一个地址,是个引用,但在修改时,他会从
字符串池中重写生成新的字符串,原有的字符串对象保持不变,所以在浅拷贝时可以认为String是一个基本类型。
 
而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。
区别就在于是否对  对象中的引用变量所指向的对象进行拷贝。
 
但还有个问题:
  要是类0里有类1,类1里面又有类2 .,类2里又有类3,如此类推。。这里的深层复制就显然会很麻烦,还有没有别的方法?    
答:
  有一个很简单的方法,就是先把对象序列化,然后再反序列化成对象,该对象保证每个引用都是崭新的。(如果类里有单例需要特殊处理)。