복제, 나는 모두가 그것을 들었다고 생각합니다. 세계 최초의 복제 양인 Dolly는 핵 이식 기술을 사용하여 포유 동물 성인 체세포에서 새로운 개인을 배양하는데, 이는 매우 마법입니다. 실제로, Java에서 복제의 개념, 즉 물체의 복사를 실현하는 것도 있습니다.
이 기사는 Java 복제에 대한 심층적 인 질문을 소개하여 복제를 더 잘 이해하는 데 도움이됩니다.
간단한 변수를 복사하려고한다고 가정하십시오. 매우 간단 :
int 사과 = 5; int 진주 = 사과;
int 유형뿐만 아니라 다른 7 가지 기본 데이터 유형 (부울, 숯, 짧은, 플로트, Double.long) 도이 유형의 상황에도 적용됩니다.
그러나 객체를 복사하면 상황이 약간 복잡합니다.
내가 초보자라고 가정하고, 나는 이것을 쓸 것입니다.
클래스 학생 {개인 int 번호; public int getNumber () {반환 번호; } public void setNumber (int number) {this.number = 숫자; }} public class test {public static void main (String args []) {Student stu1 = new Student (); stu1.SetNumber (12345); 학생 stu2 = stu1; System.out.println ( "학생 1 :" + stu1.getNumber ()); System.out.println ( "학생 2 :" + stu2.getNumber ()); }}결과:
학생 1 : 12345
학생 2 : 12345
여기서 우리는 숫자 필드가 하나만있는 학생 수업을 사용자 정의했습니다.
새로운 학생 인스턴스를 생성하고 STU2 인스턴스에 값을 할당합니다. (학생 stu2 = stu1;)
인쇄 결과를 살펴 보겠습니다. 초보자로서 나는 가슴과 복부를 두드렸다. 그러나 물체는 이렇게 복사되었다.
이것이 사실입니까?
STU2 인스턴스의 숫자 필드를 변경 한 다음 결과를 인쇄하고 다음을 참조하십시오.
stu2.setnumber (54321); System.out.println ( "학생 1 :" + stu1.getNumber ()); System.out.println ( "학생 2 :" + stu2.getNumber ());
결과:
학생 1 : 54321
학생 2 : 54321
이것은 이상합니다. 학생 수의 학생 수가 왜 변경되었고 학생 번호 1도 변경 되었습니까?
그 이유는 문장에 있습니다 (stu2 = stu1). 이 진술의 목적은 STU1의 참조를 STU2에 할당하는 것입니다.
이러한 방식으로, STU1과 STU2는 메모리 힙의 동일한 물체를 가리 킵니다. 그림과 같이 :
그렇다면 어떻게 물체 복사를 달성 할 수 있습니까?
모든 연령대의 왕을 기억하십니까? 11 가지 방법이 있으며 두 가지 보호 방법이 있으며 그 중 하나는 클론 방법입니다.
Java에서는 모든 클래스가 기본적으로 Java 언어 패키지의 객체 클래스에서 상속됩니다. 소스 코드를 확인하십시오. JDK 디렉토리의 SRC.ZIP를 다른 장소에 복사하여 압축을 방해 할 수 있으며 모든 소스 코드가 포함되어 있습니다. Access Qualifier Protected Clone ()에 방법이 있음을 알았습니다.
/*이 개체의 사본을 생성하고 반환합니다. "복사"의 정밀한 의미는 객체의 클래스에 의존 할 수 있습니다. 일반적인 의도는 모든 객체 x에 대해 표현식 : 1) x.clone ()! = x가 true2) x.clone (). getClass () == x.getClass ()가 사실이지만 절대 요구 사항이 아니지만, 이는 사실이 아닙니다. ClonenotsupportedException을 던집니다.
자세히 살펴보면 여전히 기본 방법입니다. 기본 방법은 자바 언어가 아닌 언어로 구현 된 코드이며 Java 프로그램에 의해 전화를 받는다는 것을 모두 알고 있습니다. Java 프로그램은 JVM 가상 머신에서 실행되므로 기본 운영 체제 관련 시스템에 액세스 할 수있는 방법이 없으며 운영 체제와 가까운 언어로만 구현할 수 있습니다.
첫 번째 선언은 복제 된 객체에 별도의 메모리 주소 할당을 갖도록합니다.
두 번째 선언은 원본 및 복제 된 객체가 동일한 클래스 유형을 가져야하지만 필수는 아닙니다.
세 번째 문장은 원래 및 복제 된 객체가 equals () 메소드에 의해 동일하게 사용되어야하지만 필수는 아닙니다.
각 클래스의 상위 클래스는 객체이기 때문에 모두 클론 () 메소드를 포함하지만 방법이 보호되므로 클래스 외부에서는 아무도 액세스 할 수 없습니다.
객체를 복사하려면 클론 방법을 무시해야합니다.
왜 복제?
먼저 질문에 대해 생각해 봅시다. 왜 물체를 복제해야합니까? 새로운 물건 만해도 괜찮지 않습니까?
대답은 다음과 같습니다. 복제 된 객체는 일부 수정 된 특성을 포함 할 수 있으며, 새 개체의 속성은 초기화 시점의 값이므로 현재 객체의 "상태"를 저장하기 위해 새 객체가 필요할 때 클론 방법은 클론 방법에 따라 다릅니다. 그렇다면이 개체의 임시 속성을 내 새 개체에 하나씩 할당해도 괜찮지 않습니까? 괜찮지 만 먼저 이야기하지 말자. 둘째, 위의 소스 코드를 통해 모든 사람은 클론이 기본 방법이며,이 방법은 빠르고 바닥에서 구현됩니다.
우리의 공통된 개체 a = new Object (); 객체 b; b = a를 상기시켜 드리겠습니다. 이 형태의 코드는 참조, 즉 메모리의 객체의 주소를 복사하고 A 및 B 객체는 여전히 동일한 객체를 가리 킵니다.
클론 방법을 통해 할당 된 물체는 원래 객체와 독립적으로 존재합니다.
클로닝을 구현하는 방법
먼저 두 가지 복제 방법 인 얕은 클론과 깊은 클로닝을 소개하겠습니다.
Java 언어에서 데이터 유형은 값 유형 (기본 데이터 유형) 및 참조 유형으로 나뉩니다. 값 유형에는 int, double, byte, boolean, char 등과 같은 간단한 데이터 유형이 포함되며, 참조 유형은 클래스, 인터페이스, 어레이 등과 같은 복잡한 유형을 포함합니다. 얕은 클로닝과 딥 클로닝의 주요 차이점은 참조 유형의 멤버 변수 복사를 지원하는지 여부입니다. 두 사람은 아래에 자세히 소개됩니다.
일반적인 단계는 (얕은 복제)입니다.
1. 복사 된 클래스는 클론 가능한 인터페이스를 구현해야합니다 (구현하지 않으면 클론 메소드를 호출 할 때 ClonenotsupportedException이 발생합니다). 이 인터페이스는 태그 인터페이스입니다 (메소드가 없음)
2. Clone () 메소드를 무시하고 액세스 수정자를 공개로 설정하십시오. 메소드에서 Super.Clone () 메소드를 호출하여 필요한 사본 개체를 가져옵니다. (원주민은 로컬 방법입니다)
다음은 위의 방법의 수정입니다.
클래스 학생은 복제 가능한 {private int number; public int getNumber () {return number;} public void setNumber (int number) {this.number = number;}@public object clone () {student stu = null; try {student) super.clone ();} catch (clonenoTstackection e) {e. stu;}} public class test {public static void main (string args []) {학생 stu1 = new student (); stu1.setnumber (12345); 학생 stu2 = (학생) stu1.clone (); system.out.println ( "Student1 :" + stu1.getnumber ()); stu2.getnumber ()); stu2.setnumber (54321); System.out.println ( "winder1 :" + stu1.getNumber ()); System.out.println ( "Student2 :" + stu2.getNumber ());}}결과:
학생 1 : 12345
학생 2 : 12345
학생 1 : 12345
학생 2 : 54321
이 두 객체가 같은 객체가 아니라고 믿지 않는다면이 문장을 볼 수 있습니다.
System.out.println (stu1 == stu2); // 거짓
위의 사본을 얕은 복제라고합니다.
약간 더 복잡한 딥 카피도 있습니다.
학생 수업에 주소 클래스를 추가합시다.
클래스 주소 {private string add; public String getAdd () {return add;} public void setAdd (String add) {this.add = add;}} 클래스 학생은 클로닝 가능 {private int number; private int addr; public address getAddr () {return addr;} public void setAddr (주소 Addr) {this.adr =} {public int getnumber; setNumber (int number) {this.number = number;}@public object clone () {학생 stu = null; try {stu = (student) super.clone ();} catch (clonenotsupportedException e) {e.printstackTrace ();} return stu;}} public void main (string args =) {string args [) address (); addr.setAdd ( "hangzhou city"); 학생 stu1 = new student (); stu1.setnumber (123); stu1.setaddr (addr); 학생 stu2 = (학생) stu1.clone (); system.out.println ( " + stu1." + ", 추가 :" + " stu1.getAddr (). getAdd ()); system.out.println ( "학생 2 :" + stu2.getNumber () + ", 추가 :" + stu2.getAddr (). getAdd ());}}결과:
학생 1 : 123, 주소 : Hangzhou 학생 2 : 123, 주소 : Hangzhou
언뜻보기에 문제가 없습니다. 이것이 사실입니까?
우리는 주요 방법에서 ADDR 인스턴스의 주소를 변경하려고 노력합니다.
addr.setAdd ( "Xihu District"); System.out.println ( "학생 1 :" + stu1.getNumber () + ", 추가 :" + stu1.getAddr (). getAdd ()); System.out.println ( "학생 2 :" + stu2.getNumber () + ", 추가 :" + stu2.getAddr (). getAdd ());
결과:
학생 1 : 123, 주소 : Hangzhou 학생 2 : 123, 주소 : Hangzhou 학생 1 : 123, 주소 : Xihu 지구 학생 2 : 123, 주소 : Xihu District
이것은 이상합니다. 두 학생의 주소가 왜 바뀌 었습니까?
그 이유는 얕은 복사가 ADDR 변수의 참조 만 복사하고 실제로 다른 공간을 열지 않기 때문입니다. 값을 복사 한 후 새 개체에 대한 참조를 반환하십시오.
따라서 진정한 복사 객체를 달성하기 위해 순전히 참조 복사가 아니라. 주소 클래스를 복사하고 복제 방법을 수정해야합니다. 전체 코드는 다음과 같습니다.
패키지 ABC; 클래스 주소는 복제 가능한 {private String add; 공개 문자열 getAdd () {return add; } public void setAdd (문자열 추가) {this.add = add; } @override public object clone () {address addr = null; try {addr = (주소) super.clone (); } catch (clonenotsupportedException e) {e.printstacktrace (); } return addr; }} 클래스 학생은 복제 가능한 {private int 번호; 개인 주소 ADDR; 공개 주소 getAddr () {return addr; } public void setAddr (주소 addr) {this.addr = addr; } public int getNumber () {반환 번호; } public void setNumber (int number) {this.number = 숫자; } @override public Object Clone () {Student stu = null; try {stu = (학생) super.clone (); // 얕은 사본} catch (clonenotsupportedException e) {e.printstacktrace (); } stu.addr = (주소) addr.clone (); // 딥 카피 리턴 스튜; }} public class test {public static void main (String args []) {address addr = new address (); addr.setAdd ( "Hangzhou City"); 학생 stu1 = 신입생 (); stu1.SetNumber (123); stu1.setaddr (addr); 학생 stu2 = (학생) stu1.clone (); System.out.println ( "학생 1 :" + stu1.getNumber () + ", 주소 :" + stu1.getAddr (). getAdd ()); System.out.println ( "학생 2 :" + stu2.getNumber () + ", 주소 :" + stu2.getAddr (). getAdd ()); addr.setAdd ( "Xihu District"); System.out.println ( "학생 1 :" + stu1.getNumber () + ", 주소 :" + stu1.getAddr (). getAdd ()); addr.setAdd ()); System.out.println ( "학생 2 :" + stu2.getNumber () + ", 주소 :" + stu2.getAddr (). getAdd ()); }}결과:
학생 1 : 123, 주소 : Hangzhou 학생 2 : 123, 주소 : Hangzhou 학생 1 : 123, 주소 : Xihu District 학생 2 : 123, 주소 : Hangzhou City
이 결과는 우리의 아이디어와 일치합니다.
마지막으로 API의 클래스 중 하나를 클론 방법을 구현할 수 있습니다.
java.util.date :
/***이 개체의 사본을 반환합니다. */ public Object Clone () {날짜 d = null; try {d = (date) super.clone (); if (cdate! = null) {d.cdate = (basecalendar.date) cdate.clone (); }} catch (clonenotsupportedException e) {} // return d; }이 범주는 실제로 깊은 사본입니다.
얕은 클론과 깊은 클론
1. 얕은 복제
얕은 클로닝에서, 프로토 타입 객체의 멤버 변수가 값 유형 인 경우, 하나의 사본이 복제 된 객체에 복사됩니다. 프로토 타입 객체의 멤버 변수가 참조 유형 인 경우, 참조 객체의 주소는 복제 된 객체, 즉 프로토 타입 객체의 멤버 변수 및 클로닝 된 객체가 동일한 메모리 주소를 가리 킵니다.
간단히 말하면, 얕은 클로닝에 객체가 복사되면 객체의 값 유형의 멤버 변수 만 복사되고 참조 유형의 멤버 객체가 복사되지 않습니다.
Java 언어에서 객체 클래스의 clone () 메소드를 덮어 쓰면 얕은 복제를 구현할 수 있습니다.
2. 깊은 클로닝
딥 클로닝에서 프로토 타입 객체의 멤버 변수가 값 유형이든 참조 유형인지 여부에 관계없이 하나의 사본이 복제 된 객체에 복사됩니다. Deep Cloning은 또한 프로토 타입 객체의 참조 된 모든 객체를 복제 된 객체에 복사합니다.
간단히 말하면, 객체 자체를 복사하는 것을 제외하고는 딥 클로닝에서 객체에 포함 된 모든 멤버 변수도 복사됩니다.
Java 언어로서, 딥 클로닝을 구현 해야하는 경우, 객체 클래스의 clone () 메소드를 덮어 쓰거나 직렬화 등으로 구현할 수 있습니다.
(참조 유형에 많은 참조 유형이 포함되어 있거나 내부 참조 유형 클래스에 참조 유형이 포함 된 경우 클론 방법을 사용하는 것이 매우 번거 롭습니다.이 시점에서는 직렬화를 사용하여 객체의 깊은 클로닝을 구현할 수 있습니다.)
직렬화는 개체를 스트림에 쓰는 과정입니다. 스트림에 기록 된 객체는 원래 객체의 사본이며 원래 객체는 여전히 메모리에 있습니다. 직렬화에 의해 구현 된 사본은 객체 자체를 복사 할 수있을뿐만 아니라 참조하는 멤버 객체를 복사 할 수 있습니다. 따라서 객체를 스트림에 직렬화 한 다음 스트림에서 읽음으로써 깊은 복제를 달성 할 수 있습니다. 직렬화를 구현할 수있는 객체의 클래스는 직렬화 가능한 인터페이스를 구현해야하며, 그렇지 않으면 직렬화 작업을 구현할 수 없습니다.
펼친
Java 언어가 제공하는 클로닝 가능한 인터페이스 및 직렬화 가능한 인터페이스의 코드는 매우 간단합니다. 둘 다 빈 인터페이스입니다. 이 빈 인터페이스를 식별 인터페이스라고도합니다. 식별 인터페이스에는 어떤 메소드에 대한 정의가 없습니다. 그 기능은 이러한 인터페이스의 구현 클래스에 클로닝을 지원하는지, 직렬화 지원 여부 등과 같은 특정 기능을 가지고 있는지 JRE에게 알리는 것입니다.
다층 클로닝 문제를 해결하십시오
참조 유형에 많은 참조 유형이 포함되어 있거나 내부 참조 유형 클래스에 참조 유형이 포함 된 경우 클론 메소드를 사용하는 것이 매우 귀찮습니다. 현재 직렬화를 사용하여 물체의 깊은 복제를 구현할 수 있습니다.
공개 클래스 외부 구현 직렬화 가능 {개인 정적 최종 긴 SerialversionUID = 369285298572941L; // ID 공개 내부를 명시 적으로 선언하는 것이 가장 좋습니다. // 방사 : [딥 카피 방법, 객체와 모든 객체 속성이 직렬화되어야합니다] public OUTER MYCLONE () {OUTER OUTER = NULL; {// 스트림에 쓰여진 것은 객체의 사본이며 원래 객체는 여전히 JVM에 존재하기 때문에 객체를 스트림으로 직렬화하십시오. 따라서이 기능을 사용하면 ByTearRayoutputStream BAOS 객체의 깊은 사본을 달성 할 수 있습니다. ObjectOutputStream OOS = 새로운 ObjectOutputStream (BAOS); oos.writeobject (this); // 스트림을 객체로 직렬화 BYTEARRAYINPUTSTREAM BAIS = NEW BYTEARRAYINPUTSTREAM (BAOS.TOBYTERRAY ()); ObjectInputStream OIS = New ObjectInputStream (BAIS); 외부 = (외부) ois.readobject (); } catch (ioexception e) {e.printstacktrace (); } catch (classNotFoundException e) {e.printstacktrace (); } 외부 반환; }}내부는 또한 직렬화 가능을 구현해야합니다. 그렇지 않으면 직렬화 할 수 없습니다.
공개 클래스 내부는 시리얼이즈 가능한 {private static final long serialversionuid = 872390113109L; // id public String name = ""를 명시 적으로 선언하는 것이 가장 좋습니다. 공개 내부 (문자열 이름) {this.name = 이름; } @override public String toString () {return "내부의 이름 값은 다음과 같습니다." + 이름; }}이를 통해 두 객체가 서로의 값에 영향을 미치지 않고 메모리 공간에 완전히 독립적으로 존재할 수 있습니다.
요약
객체 복제를 구현하는 두 가지 방법이 있습니다.
1). 클로닝 가능한 인터페이스를 구현하고 객체 클래스에서 Clone () 메소드를 재정의합니다.
2). 직렬화 가능한 인터페이스를 구현하고 객체 직렬화 및 사막화를 통해 클로닝을 구현하여 진정한 깊은 클로닝을 실현할 수 있습니다.
참고 : 직렬화 및 사제화를 기반으로하는 복제는 단순한 복제 일뿐 만 아니라 일반적인 제한을 통해 클로닝 될 대상이 직렬화를 지원하는지 확인할 수 있습니다. 이 점검은 컴파일러에 의해 수행되며 런타임에 예외를 제외하지 않습니다. 이 솔루션은 객체 클래스의 클론 방법을 사용하여 복제 객체보다 낫습니다. 컴파일 시점에 노출되어 문제를 런타임으로 남겨 두는 것이 항상 좋습니다.
위의 내용은 Java 프로그래밍 구현 객체 클로닝 (카피) 코드에 대한 자세한 설명입니다. 모든 사람에게 도움이되기를 바랍니다. 관심있는 친구는이 사이트의 다른 관련 주제를 계속 참조 할 수 있습니다. 단점이 있으면 메시지를 남겨 두십시오.