Java의 클론 이해

8742 단어 java클론
앞말
Java 클론(Clone)은 Java 언어의 특성 중 하나이지만 실제 응용은 드물다.하지만 때로는 복제를 사용하면 더욱 편리하고 효율적일 수 있다.
클론(Clone)의 경우 Java는 다음과 같은 제한이 있습니다.
1. 복제된 클래스는 반드시 스스로 Cloneable 인터페이스를 실현해야 하며, 지시Object.clone() 방법으로 이 클래스의 실례를 필드별로 합법적으로 복제할 수 있다.Cloneable 인터페이스는 사실상 표지 인터페이스로 어떠한 인터페이스 방법도 없다.
2. Cloneable 인터페이스를 실현하는 클래스는 공공 방법으로 다시 작성해야 한다Object.clone.어떤 대상이 이 인터페이스를 실현했다고 복제하는 것은 불가능하다.설령 클론 방법이 반사적으로 호출된다 하더라도, 그것이 성공할 것이라고 보장할 수 없다.
3. Java.lang.Object 클래스에서 복제 방법은 다음과 같이 정의됩니다.

        protected Object clone()
        throws CloneNotSupportedException
이 객체의 복사본을 만들고 반환합니다.보호되는 방법임을 나타낸다. 같은 가방에서 볼 수 있다.
관례에 따라 되돌아오는 대상은 호출super.clone 을 통해 얻어야 한다.
Java의 할당
Java에서 부치는 매우 자주 사용하는데, 간단한 부치는 다음과 같다.

// 
int a = 1;
int b = a;

// 
String[] weekdays = new String[5];
String[] gongzuori = weekdays;// 
상기 코드에서
1. 원본 데이터 형식의 경우 실제 값으로 전달됩니다.
2. 인용 데이터 형식의 경우, 부여 값은 대상이 아닌 대상의 인용으로 전달됩니다.
데이터 형식과 인용 형식의 이 차이를 이해하면clone을 이해할 수 있습니다.
Clone
Java에서 clone은 기존 대상을 메모리에서 같은 대상으로 복사하는 과정입니다.java의 복제는 영역별로 복제됩니다.
자바에서 clone 방법을 지원하려면 먼저 Cloneable 인터페이스를 실현해야 한다
Cloneable은 사실 좀 이상하다. 우리가 자주 사용하는 인터페이스와는 다르다. 그 내부에는 어떤 방법도 포함되지 않고 단지 하나의 표기 인터페이스일 뿐이다.
그 원본은 다음과 같다.

public interface Cloneable {
}
cloneable에 대해 주의해야 할
1. clone을 지원하려면 Cloneable 인터페이스가 필요합니다.
2. Cloneable 인터페이스를 호출하는 clone 방법이 없으면 CloneNotSupportedException 이상이 발생합니다.
그 다음에clone 방법을 다시 쓰고public 접근 단계로 수정합니다

static class CloneableImp implements Cloneable {
 public int count;
 public Child child;
   
   
 @Override
 public Object clone() throws CloneNotSupportedException {
   return super.clone();
 }
}
clone 방법으로 대상을 복사합니다

CloneableImp imp1 = new CloneableImp();
imp1.child = new Child("Andy");
try {
 Object obj = imp1.clone();
 CloneableImp imp2 = (CloneableImp)obj;
 System.out.println("main imp2.child.name=" + imp2.child.name);
} catch (CloneNotSupportedException e) {
 e.printStackTrace();
}
얕은 카피
위의 코드가 구현한 clone은 사실상 얕은 복사본(Shallow Copy)에 속한다.
얕은 카피에 관해서 너는 알아야 할
1. 기본 clone 사용 방법
2. 원본 데이터 도메인의 값 복사
3. 참조 유형의 경우 참조만 복사
4, 빠른 실행, 높은 효율
5. 데이터의 100% 분리를 할 수 없다.
6. 객체에 원본 데이터 도메인만 포함되거나 변경할 수 없는 객체 도메인이 있는 경우 얕은 복제본을 사용하는 것이 좋습니다.
데이터 분리가 불가능하기 때문에, 우리는 이 코드 검증을 사용할 수 있다

CloneableImp imp1 = new CloneableImp();
imp1.child = new Child("Andy");
try {
 Object obj = imp1.clone();
 CloneableImp imp2 = (CloneableImp)obj;
 imp2.child.name = "Bob";
     
 System.out.println("main imp1.child.name=" + imp1.child.name);
} catch (CloneNotSupportedException e) {
 e.printStackTrace();
}
상기 코드는imp1의clone 방법을 사용하여imp2를 복제한 다음imp2.child.name 을Bob로 수정한 다음imp1.child.name 인쇄한 결과입니다.

main imp1.child.name=Bob
얕은 복사본이 데이터의 100% 분리를 하지 못했기 때문에imp1과imp2는 같은 어린이 대상을 공유하기 때문에 한 가지 수정이 다른 사람에게 영향을 미칠 수 있다.
딥 카피
딥 카피는 데이터의 100% 분리 문제를 해결할 수 있다.위의 코드에 대해 약간의 수정만 하면 된다.
1. Cloneable 인터페이스를 구현합니다.

public class Child implements Cloneable{

 public String name;

 public Child(String name) {
   this.name = name;
 }

 @Override
 public String toString() {
   return "Child [name=" + name + "]";
 }

 @Override
 protected Object clone() throws CloneNotSupportedException {
   return super.clone();
 }
}
2. clone 방법을 다시 쓰고 데이터 영역의 clone 방법을 호출합니다.

static class CloneableImp implements Cloneable {
 public int count;
 public Child child;
   
   
 @Override
 public Object clone() throws CloneNotSupportedException {
   CloneableImp obj = (CloneableImp)super.clone();
   obj.child = (Child) child.clone();
   return obj;
 }
}
우리가 다시 수정imp2.child.name하면 imp1.child.name의 값에 영향을 주지 않을 것이다. 왜냐하면imp1과imp2가 각자의 child 대상을 가지고 있기 때문이다. 데이터의 100% 격리를 했기 때문이다.
딥 카피의 특징
1. clone 방법을 다시 써야 한다. 부류의 방법만 호출할 뿐만 아니라 속성의 clone 방법도 호출해야 한다.
2. 원본 개체와 클론 개체 사이의 100% 데이터 분리
3. 객체에 참조 유형의 속성이 있는 경우 딥 카피를 사용하는 것이 좋습니다.
4, 심층 복제본은 얕은 복제본보다 시간이 오래 걸리고 효율성이 낮음
클론을 사용하는 이유
중요하고 흔히 볼 수 있는 것은 어떤 API는 List 집합을 제공해야 하지만 호출자의 수정이 자신의 변화에 영향을 미치는 것을 원하지 않기 때문에 대상을 복제하여 데이터 격리의 목적을 달성해야 한다는 것이다.
가능한 한 클론을 피해야 한다
      1.일반적인 상황에서 인터페이스를 실현하는 것은 클래스가 고객을 위해 무엇을 할 수 있는지를 나타내기 위한 것이다. 그러나 Cloneable은 단지 하나의 표기 인터페이스일 뿐이고 클래스 중의 손 보호 방법을 바꾸는 행위는 인터페이스의 극단적인 비전형적인 용법으로 본받을 가치가 없다.
      2.Clone 방법 약정 및 취약한 clone 방법의 자바doc 설명은 약간 애매모호하다. 자바SE8의 약정은 다음과 같다.
clone 방법은 이 대상의 복사본을 만들고 되돌려줍니다.복제본의 정확한 의미는 이 대상의 클래스에 달려 있다.일반적인 의미는 모든 대상에 대한 x, 표현식x.clone() != x true x.clone().getClass() == x.getClass() 도true로 돌아오지만 반드시 x.clone().equals(x)도true로 돌아오지만 반드시 필요한 것은 아니다
위의 두 번째와 세 번째 표현식은false로 되돌아오기 쉽다.따라서 유일하게 영원한true를 보장할 수 있는 것은 표현식 1, 즉 두 대상이 독립된 대상이다.
       3.가변 대상final역은 복제 방법에 있습니다. 만약에 우리가 가변 대상의final역도 복사해야 한다면final역의 제한으로 인해 실제로는 컴파일할 수 없습니다.따라서 복제를 실현하기 위해서는 이 가변 대상 영역의final 키워드를 버리는 것을 고려해야 한다.
       4.스레드 안전 만약 당신이 스레드 안전 클래스로 Cloneable 인터페이스를 실현하기로 결정한다면, 그것의 Clone 방법이 동기화 작업을 잘 할 수 있도록 보장해야 합니다.기본 Object.clone 방법은 동기화되지 않았습니다.
전반적으로 말하자면 자바의 clone 방법은 사실상 완벽하지 않기 때문에 가능한 한 사용을 피하는 것을 권장한다.다음은 몇 가지 대체 방안이다.
Copy constructors
복제 구조기를 사용해도 대상의 복사를 실현할 수 있다.
1. 복제 구조기도 구조기의 일종이다
2. 하나의 매개 변수만 받아들여지고 매개 변수 유형은 현재의 클래스이다
3. 매개 변수와 같은 새로운 대상을 생성하는 데 목적이 있다
4. 복제 구조기는 clone 방법에 비해 간단하고 실현하기 쉽다는 장점이 있다.
복제 구조기를 사용한 코드 예시

public class Car {
 Wheel wheel;
 String manufacturer;
 
 public Car(Wheel wheel, String manufacturer) {
   this.wheel = wheel;
   this.manufacturer = manufacturer;
 }
 
 //copy constructor
 public Car(Car car) {
   this(car.wheel, car.manufacturer);
 }
 
 public static class Wheel {
   String brand;
 }
}
주의, 위의 코드는 얕은 복사로 이루어지고, 깊은 복사를 실현하려면 아래의 코드를 참고하십시오

//copy constructor
public Car(Car car) {
 Wheel wheel = new Wheel();
 wheel.brand = car.wheel.brand;
   
 this.wheel = wheel;
 this.manufacturer = car.manufacturer;
}
더욱 편리하게 하기 위해서, 우리는 상술한 종류를 위해 정태적인 방법을 추가할 수 있다

public static Car newInstance(Car car) {
 return new Car(car);
}
Serializable을 사용하여 딥 카피 수행
사실 서열화를 사용해도 대상의 깊은 복사를 실현할 수 있다.약식 코드는 다음과 같다.

public class DeepCopyExample implements Serializable{
 private static final long serialVersionUID = 6098694917984051357L;
 public Child child;
 
 public DeepCopyExample copy() {
   DeepCopyExample copy = null;
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(this);
 
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     copy = (DeepCopyExample) ois.readObject();
   } catch (IOException e) {
     e.printStackTrace();
   } catch (ClassNotFoundException e) {
     e.printStackTrace();
   }
   return copy;
 }
}
그 중에서 Child는 Serializable 인터페이스를 구현해야 합니다

public class Child implements Serializable{
 private static final long serialVersionUID = 6832122780722711261L;
 public String name = "";

 public Child(String name) {
   this.name = name;
 }

 @Override
 public String toString() {
   return "Child [name=" + name + "]";
 }
}
예제 겸 테스트 코드 사용하기

DeepCopyExample example = new DeepCopyExample();
example.child = new Child("Example");
   
DeepCopyExample copy = example.copy();
if (copy != null) {
 copy.child.name = "Copied";
 System.out.println("example.child=" + example.child + ";copy.child=" + copy.child);
}
// :example.child=Child [name=Example];copy.child=Child [name=Copied]
출력 결과를 보면copy 대상의child 값 수정은example 대상의child 값에 영향을 주지 않습니다. 즉, 서열화를 사용하면 대상의 깊은 복사를 실현할 수 있습니다.
총결산
이상은 자바에서 복제한 모든 내용입니다. 본고는 여러분의 자바 학습에 도움이 되기를 바랍니다.

좋은 웹페이지 즐겨찾기