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 값에 영향을 주지 않습니다. 즉, 서열화를 사용하면 대상의 깊은 복사를 실현할 수 있습니다.총결산
이상은 자바에서 복제한 모든 내용입니다. 본고는 여러분의 자바 학습에 도움이 되기를 바랍니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
38. Java의 Leetcode 솔루션텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.