深拷貝 淺拷貝

當我們想複製一個簡單類型的數據時:

int a=5;
int b=a;

其他7種簡單類型的複製也可以通過這種方式進行。

但當我們想複製一個對象時,就沒有這麼簡單了,

有些初學者複製對象時應該會採用如下方法:

public class Test {
	public static void main(String[] args) {
		Student s1=new Student();
		s1.setStuId(1);
		s1.setStuName("caojie");		
		Student s2=s1;
		System.out.println(s1);
		System.out.println(s2);
	}
}
class Student{
	private int stuId;
	private String stuName;
	public int getStuId() {
		return stuId;
	}
	public void setStuId(int stuId) {
		this.stuId = stuId;
	}
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	@Override
	public String toString() {
		return "Student [stuId=" + stuId + ", stuName=" + stuName + "]";
	}	
}
如果這樣的話,當你改變s2的值得時候,s1的值也會跟着改變,這是因爲s2=s1這句話是將s1的引用賦給了s2,兩個引用指向同一個內存單元,所以改變其中一個的值,兩個對象的值都會發生改變。

那怎樣對一個對象進行有效的複製呢?

Object類有一個clone方法,protected native Object clone() throws CloneNotSupportedException,

每個類的直接父類或間接父類都是Object,所以每個類都有clone方法,但這個方法是protected的,類外不能訪問,所以每個類必須重寫clone方法進行對象的複製。
淺複製:一般 步驟

1.被複制的類需要實現cloneable接口(不實現會拋CloneNotSupportedException異常,該接口是個標記接口,不含任何方法

2.重寫clone方法(將訪問修飾符改成public,調用super.clone獲得需要的複製對象)

修改後的代碼是這樣的:

class Student implements Cloneable{
	private int stuId;
	private String stuName;
	public int getStuId() {
		return stuId;
	}
	public void setStuId(int stuId) {
		this.stuId = stuId;
	}
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Student s1=null;
		try{
			s1=(Student)super.clone();//調用super.clone獲得需要複製的對象
		}catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return s1;
	}
	
	@Override
	public String toString() {
		return "Student [stuId=" + stuId + ", stuName=" + stuName + "]";
	}	
}
public class Test {
	public static void main(String[] args) throws CloneNotSupportedException {
		Student s1=new Student();
		s1.setStuId(1);
		s1.setStuName("caojie");		
		Student s2=(Student)s1.clone();
		System.out.println(s1);
		System.out.println(s2);
		s2.setStuId(2);
		System.out.println(s1);
		System.out.println(s2);
	}
}
打印結果:

Student [stuId=1, stuName=caojie]
Student [stuId=1, stuName=caojie]
Student [stuId=1, stuName=caojie]
Student [stuId=2, stuName=caojie]
打印System.out.println(s1==s2);輸出結果爲false

深複製

當我們在Studennt類中加入另一個類的引用,代碼如下:

class Student implements Cloneable{
	private int stuId;
	private String stuName;
	private Address addr;	
	public Address getAddr() {
		return addr;
	}
	public void setAddr(Address addr) {
		this.addr = addr;
	}
	public int getStuId() {
		return stuId;
	}
	public void setStuId(int stuId) {
		this.stuId = stuId;
	}
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Student s1=null;
		try{
			s1=(Student)super.clone();//調用super.clone獲得需要複製的對象
		}catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return s1;
	}
	@Override
	public String toString() {
		return "Student [stuId=" + stuId + ", stuName=" + stuName + ", addr=" + addr + "]";
	}		
}
class Address{
	private String addr;
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	@Override
	public String toString() {
		return "Address [addr=" + addr + "]";
	}
}
public class Test {
	public static void main(String[] args) throws CloneNotSupportedException {
		Address addr=new Address();
		addr.setAddr("beijjj");
		Student s1=new Student();
		s1.setStuId(1);
		s1.setStuName("caojie");
		s1.setAddr(addr);
		Student s2=(Student)s1.clone();
		System.out.println(s1);
		System.out.println(s2);
		addr.setAddr("haha");
	}
}

打印結果如下:

Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]

這樣看複製結果是沒有任何問題的

當我們改變重新改變地址時:s2.setAddr(addr);

class Student implements Cloneable{
	private int stuId;
	private String stuName;
	private Address addr;	
	public Address getAddr() {
		return addr;
	}
	public void setAddr(Address addr) {
		this.addr = addr;
	}
	public int getStuId() {
		return stuId;
	}
	public void setStuId(int stuId) {
		this.stuId = stuId;
	}
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Student s1=null;
		try{
			s1=(Student)super.clone();//調用super.clone獲得需要複製的對象
		}catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return s1;
	}
	@Override
	public String toString() {
		return "Student [stuId=" + stuId + ", stuName=" + stuName + ", addr=" + addr + "]";
	}		
}
class Address{
	private String addr;
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	@Override
	public String toString() {
		return "Address [addr=" + addr + "]";
	}
}
public class Test {
	public static void main(String[] args) throws CloneNotSupportedException {
		Address addr=new Address();
		addr.setAddr("beijjj");
		Student s1=new Student();
		s1.setStuId(1);
		s1.setStuName("caojie");
		s1.setAddr(addr);
		Student s2=(Student)s1.clone();
		System.out.println(s1);
		System.out.println(s2);
		addr.setAddr("haha");
		s2.setAddr(addr);
		System.out.println(s1);
		System.out.println(s2);
	}
}
打印結果如下:

Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=haha]]
Student [stuId=1, stuName=caojie, addr=Address [addr=haha]]
可以發現,當我們改變其中一個對象的地址時,兩個對象的值都會發生改變

這是因爲淺複製只是複製了addr變量的引用,並沒有真正的開闢另一塊空間,將值複製後再將引用返回給新對象。

所以,爲了達到真正的複製對象,而不是純粹引用複製。我們需要將Address類可複製化,並且修改clone方法,完整代碼如下:

class Student implements Cloneable{
	private int stuId;
	private String stuName;
	private Address addr;	
	public Address getAddr() {
		return addr;
	}
	public void setAddr(Address addr) {
		this.addr = addr;
	}
	public int getStuId() {
		return stuId;
	}
	public void setStuId(int stuId) {
		this.stuId = stuId;
	}
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Student s1=null;
		try{
			s1=(Student)super.clone();//調用super.clone獲得需要複製的對象
									  //淺複製
		}catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		s1.addr=(Address)addr.clone();//深複製
		return s1;
	}
	@Override
	public String toString() {
		return "Student [stuId=" + stuId + ", stuName=" + stuName + ", addr=" + addr + "]";
	}		
}
class Address implements Cloneable{
	private String addr;
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	@Override
	public String toString() {
		return "Address [addr=" + addr + "]";
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Address addr=null;
		addr=(Address)super.clone();
		return addr;
	}
}
public class Test {
	public static void main(String[] args) throws CloneNotSupportedException {
		Address addr=new Address();
		addr.setAddr("beijjj");
		Student s1=new Student();
		s1.setStuId(1);
		s1.setStuName("caojie");
		s1.setAddr(addr);
		Student s2=(Student)s1.clone();
		System.out.println(s1);
		System.out.println(s2);
		addr.setAddr("haha");
		Address addr2=new Address();
		addr2.setAddr("hehe");
		s2.setAddr(addr2);
		System.out.println(s1);
		System.out.println(s2);
	}
}
打印結果:

Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=haha]]
Student [stuId=1, stuName=caojie, addr=Address [addr=hehe]]


Java.util.Date: 類也實現了深度複製

public Object clone() {
        Date d = null;
        try {
            d = (Date)super.clone();
            if (cdate != null) {
                d.cdate = (BaseCalendar.Date) cdate.clone();
            }
        } catch (CloneNotSupportedException e) {} // Won't happen
        return d;
    }








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章