Java對象和集合的拷貝/克隆/復制


昨天同事遇到了一個奇怪的問題,他需要將一個JavaBean拷貝一份,然后對新創建的Bean進行操作。但是他對新的Bean操作后,會影響舊的Bean的值。當聽到這個問題的時候,我第一反應就是他的拷貝方法有問題,只是將aBean的內容復制給了bBean,但是在內存中指向的是同一個地址。這里就引出了兩個關鍵詞,淺拷貝深拷貝

淺拷貝(淺克隆)

被復制對象的所有變量值都和原來的對象的值相同,但是復制后的對象的引用仍然指向原來的對象。簡單來說,就是對A進行拷貝生成B,只是將A的值復制給了B,內存中指向的是同一地址,影響就是改變B的同時,A的值也會改變。

深拷貝(深克隆)

被復制對象的所有變量值都和原來的對象的值相同,除了變量的值改變,也會創建新的指向變量的地址。源對象A和復制后的B,雖然內容是一樣的,但是各自指向的地址不同,所以改變相互不受影響。

那問題已經知道,Java的Bean對象要怎么進行深拷貝呢。

一、Java對象克隆

Java中對象的深克隆

①利用Object類的clone()方法。
②在派生類中重寫基類的clone方法,並聲明為public。
③在派生類的clone()方法中,調用super.clone()。
④在派生類中實現Cloneable接口。

這是一個Student的類,重寫了clone()方法。

public class Student implements Cloneable {
	private String name;
	private int age;

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	public Object clone() {
		Object o = null;
		try {
			o = (Student)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		
		return o;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
}

測試clone

public static void main(String[] args) { 
	Student s1=new Student("張三",18);
	System.out.println("修改前s1值:" + s1.toString());
	
	Student s2=(Student)s1.clone(); 
	s2.setName("李四");
	s2.setAge(20);
	
	//修改學生2后,不影響學生1的值。
	System.out.println("修改后s1值:" + s1.toString());
	System.out.println("s2的值:" + s2.toString());
}

控制台輸出的結果如下:

修改前s1值:Student [name=張三, age=18]
修改后s1值:Student [name=張三, age=18]
s2的值:Student [name=李四, age=20]

上面的例子對象中的屬性是基本類型,但是如果包含非基本類型,結果怎樣呢,上代碼,對Student類進行修改,加入Course類。
Student類

public class Student implements Cloneable {
	
	private String name;
	private int age;
	private Course course;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Course getCourse() {
		return course;
	}
	public void setCourse(Course course) {
		this.course = course;
	}
	
	public Student(String name, int age, Course course) {
		super();
		this.name = name;
		this.age = age;
		this.course = course;
	}
	
	public Student() {
	}
	
	public Object clone() {
		Object o = null;
		try {
			o = (Student)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		
		return o;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", course=" + course + "]";
	}
	
}

Course類


public class Course {
	
	private String name;
	private int value;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	
	public Course(String name, int value) {
		super();
		this.name = name;
		this.value = value;
	}
	public Course() {
		
	}
	@Override
	public String toString() {
		return "Course [name=" + name + ", value=" + value + "]";
	}
	
}

測試克隆

public static void main(String[] args) { 
	
	Student s1 = new Student();
	s1.setName("張三");
	s1.setAge(24);
	
	Course c = new Course();
	c.setName("語文");
	c.setValue(80);
	
	s1.setCourse(c);
	System.out.println("修改前s1值:" + s1.toString());
	
	Student s2 = (Student)s1.clone();
	s2.setName("李四");
	s2.setAge(20);
	
	Course c2 = s2.getCourse();
	c2.setName("數學");
	c2.setValue(90);
	
	//修改學生2的Course后,影響學生1的Course。
	System.out.println("修改后s1值:" + s1.toString());
	System.out.println("s2的值:" + s2.toString());
}

控制台輸出結果

修改前s1值:Student [name=張三, age=24, course=Course [name=語文, value=80]]
修改后s1值:Student [name=張三, age=24, course=Course [name=數學, value=90]]
s2的值:Student [name=李四, age=20, course=Course [name=數學, value=90]]
上面的例子說明調用clone方法,拷貝原始對象中的內容,對於基本數據類型,這樣的操作是沒有問題的,但是對於非基本類型,它們保存的僅僅是對象的引用。

為了解決上面的問題,我們需要使用深度克隆方案。修改之后的Student類和Course類如下
Student類

public class Student implements Cloneable {
	
	private String name;
	private int age;
	private Course course;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Course getCourse() {
		return course;
	}
	public void setCourse(Course course) {
		this.course = course;
	}
	
	public Student(String name, int age, Course course) {
		super();
		this.name = name;
		this.age = age;
		this.course = course;
	}
	
	public Student() {
	}
	
	public Student clone() {
		Student s = null;
		try {
			s = (Student)super.clone();
			s.course = course.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		
		return s;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", course=" + course + "]";
	}
	
}

Course類


public class Course implements Cloneable{
	
	private String name;
	private int value;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	
	public Course(String name, int value) {
		super();
		this.name = name;
		this.value = value;
	}
	public Course() {
		
	}
	
	public Course clone() {
		Course c = null;
		try {
			c = (Course)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		
		return c;
	}
	@Override
	public String toString() {
		return "Course [name=" + name + ", value=" + value + "]";
	}
	
}

測試方法使用之前的方法,下面的運行后的結果。

修改前s1值:Student [name=張三, age=24, course=Course [name=語文, value=80]]
修改后s1值:Student [name=張三, age=24, course=Course [name=語文, value=80]]
s2的值:Student [name=李四, age=20, course=Course [name=數學, value=90]]

根據結果可知,通過深度克隆后,clone后的對象非基本類的變量修改,不會對原對象造成影響。

對於集合的Clone操作也一樣,集中的屬性如果是基本數據類型的話,循環賦值,或者使用addAll()方法都不會對原集合造成影響。若集合中存放了非基本數據類型的話,如上述中的Student對象,就必須對Student對象添加重寫clone()方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM