JAVAオブジェクトクローンで発生する可能性のある問題

2800 ワード

まず、コピーとクローンを区別します.
≪コピー|Copy|emdw≫:変数をコピーすると、元の変数とコピー変数は同じオブジェクトを参照します.1つの変数が参照するオブジェクトを変更すると、別の変数に影響します.
≪クローン|Clone|oem_src≫:オブジェクトをクローンすると、オブジェクトと同じ内容のオブジェクトが再作成されます.
cloneメソッドはObjectクラスが保護されているメソッドで、ユーザーが作成したコードは直接呼び出すことができません.同じクラスだけがそれ自体をクローンできます.
質問:
クローンするオブジェクトのすべてのデータフィールドが数値または基本タイプの場合、このようなクローンは問題ありません.
ただし、オブジェクトにサブオブジェクトの参照が含まれている場合、コピーの結果、2つのドメインが同じサブオブジェクトを参照し、元のオブジェクトとクローンオブジェクトがこの情報の一部を共有します.
これにより、クローンオブジェクトがこの部分を変更すると、元のオブジェクトのデータが変更されます.
デフォルトのクローン操作はすべて浅いコピーで、オブジェクトに含まれる内部オブジェクトはクローンされていません.
浅いコピーを行うと何が起こりますか?
元のオブジェクトが浅いクローンオブジェクトと共有されているサブオブジェクトが可変である場合、問題は発生しません.
ただし、サブオブジェクトが可変である場合が多い.これにより、クローンサブオブジェクトの深さコピーを実現するためにcloneメソッドを書き換える必要があります.
クローン対象のオブジェクトについては、次のような判断が必要です.
(1)デフォルトのクローン方法が要求を満たすことができるかどうか.
(2)デフォルトのクローンメソッドは、可変子オブジェクトを呼び出すcloneメソッドによって修正することができる.
(3)cloneを使用すべきではないか.
1または2を選択した場合、クラスは次の必要があります.
Cloneableインタフェースを実装し、publicアクセス修飾子を使用してcloneメソッドを再定義します.
クローンのメカニズムを次に示します.
import java.util.Date;
import java.util.GregorianCalendar;

public class TestClone {
	public static void main(String[] args) {
		try {
			/**    **/
			Employee origin = new Employee("Tom", 10000);
			origin.setHireDay(2014, 10, 10);
			/**  Employee  **/
			Employee copy = origin.clone();
			copy.raiseSalary(10);
			copy.setHireDay(2015, 11, 11);
			System.err.println(origin); /**Tom,10000.0,Mon Nov 10 00:00:00 CST 2014**/
			System.err.println(copy);   /**Tom,11000.0,Fri Dec 11 00:00:00 CST 2015**/
			/**
			 *                  ,           ,
			 *    Date                  ,        ,         
			 */
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
}

class Employee implements Cloneable {
	private String name;
	private double salary;
	private Date hireDay;

	public Employee(String n, double s) {
		this.name = n;
		this.salary = s;
		this.hireDay = new Date();
	}

	public Employee clone() throws CloneNotSupportedException {
		/**   Object clone   **/
		Employee cloned = (Employee) super.clone();

		/**                      **/
		cloned.hireDay = (Date) hireDay.clone();
		
		/**       hireDay   ,          ,                 **/
		/**Tom,10000.0,Fri Dec 11 00:00:00 CST 2015**/
		/**Tom,11000.0,Fri Dec 11 00:00:00 CST 2015**/

		return cloned;
	}

	public void setHireDay(int year, int month, int day) {
		Date newHireday = new GregorianCalendar(year, month, day).getTime();
		hireDay.setTime(newHireday.getTime());
	}
	
	public void raiseSalary(double byPrecent){
		double raise = salary * byPrecent / 100;
		salary += raise;
	}
	
	/**  Object  toString  **/
	public String toString(){
		return new StringBuffer().append(name).append(",").append(salary).append(",")
				.append(hireDay).toString();
	}
}