우리는 Java에 Cloneable 인터페이스가 존재한다는 것을 알고 있습니다. 이 인터페이스를 구현하는 클래스는 복사가 가능함과 동시에 메모리에서 복사가 수행되므로 직접 객체를 생성하는 것보다 빠릅니다. 새로운 기능, 특히 대형 개체 생성 시 성능 향상이 매우 뚜렷합니다. 그러나 복사는 깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)로 나뉘는 것을 알고 있는데, 얕은 복사는 객체 속성이 불완전하게 복사되는 문제가 있습니다. 딥 카피와 얕은 카피에 대해서는 여기를 참고하세요: Java의 얕은 카피와 딥 카피의 점진적 분석
1. 얕은 카피 문제
먼저 다음 코드를 살펴보겠습니다.
public class Person implements Cloneable{ /** 姓名 **/ private String name; /** 电子邮件 **/ private Email email; public String getName() { return name; } public void setName(String name) { this.name = name; } public Email getEmail() { return email; } public void setEmail(Email email) { this.email = email; } public Person(String name,Email email){ this.name = name; this.email = email; } public Person(String name){ this.name = name; } protected Person clone() { Person person = null; try { person = (Person) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; } } public class Client { public static void main(String[] args) { //写封邮件 Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); Person person1 = new Person("张三",email); Person person2 = person1.clone(); person2.setName("李四"); Person person3 = person1.clone(); person3.setName("王五"); System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent()); System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent()); System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent()); } } -------------------- Output: 张三的邮件内容是:请与今天12:30到二会议室参加会议... 李四的邮件内容是:请与今天12:30到二会议室参加会议... 王五的邮件内容是:请与今天12:30到二会议室参加会议...
이 애플리케이션에서는 먼저 이메일을 정의한 다음 Zhang San, Li Si 및 Wang Wu에게 이메일을 보냅니다. 그들은 동일한 이메일을 사용하고 이름만 다르기 때문에 Zhang San의 객체 클래스를 사용합니다. Four와 King Five가 반대하고 이름을 변경합니다. 현재까지는 프로그램에 아무런 문제가 없지만, Zhang San이 30분 전에 도착해야 한다면 이메일 내용을 수정하겠습니다:
public class Client { public static void main(String[] args) { //写封邮件 Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); Person person1 = new Person("张三",email); Person person2 = person1.clone(); person2.setName("李四"); Person person3 = person1.clone(); person3.setName("王五"); person1.getEmail().setContent("请与今天12:00到二会议室参加会议..."); System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent()); System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent()); System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent()); } }
여기서도 Zhang San의 개체를 사용하여 실현합니다. Li Si와 Wang Wu가 복사하고 마지막으로 Zhang San의 이메일 내용을 다음과 같이 변경합니다. 회의에 참석하려면 오늘 12시에 두 번째 회의실로 오십시오... 그러나 결과는 다음과 같습니다.
张三的邮件内容是:请与今天12:00到二会议室参加会议... 李四的邮件内容是:请与今天12:00到二会议室参加会议... 王五的邮件内容是:请与今天12:00到二会议室参加会议...
여기서 Li Si와 Wang Wu의 이메일 내용도 왜 변경되었는지 궁금합니다. 30분 일찍 오라고 하면 반대가 있을 거예요!
사실 문제의 핵심은 clone() 메소드에 있습니다. 우리는 clone() 메소드가 Object 클래스의 clone() 메소드를 사용한다는 것을 알고 있지만 이 메소드에는 결함이 있습니다. 복사하지 않음 객체의 모든 속성은 복사되지만 선택적으로 복사됩니다. 기본 규칙은 다음과 같습니다.
1. 기본 유형
변수가 기본 유형인 경우 해당 값을 복사합니다. int, float 잠깐만요.
2. 객체
변수가 인스턴스 객체인 경우 해당 주소 참조를 복사합니다. 이는 새 객체와 원본 객체가 인스턴스 변수를 공유한다는 의미입니다.
3. 문자열
변수가 문자열인 경우 해당 주소 참조를 복사합니다. 그러나 수정되면 문자열 풀에서 새 문자열이 다시 생성되며 원래 Ziducheng 개체는 변경되지 않은 상태로 유지됩니다.
위의 규칙을 바탕으로 세 사람이 공통된 객체를 공유하면 쉽게 문제를 찾을 수 있습니다. Zhang San이 이메일 내용을 수정하면 Li Si와 Wang Wu도 수정하게 됩니다. 위와 같은 상황이 나타납니다. 여전히 이 상황을 해결할 수 있습니다. clone() 메소드에서 새 객체를 생성한 다음 Zhang San이 해당 객체를 참조합니다.
protected Person clone() { Person person = null; try { person = (Person) super.clone(); person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent())); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; }
따라서 얕은 복사는 Java에서 제공하는 간단한 메소드일 뿐입니다. 복사 메커니즘은 직접 사용하기 쉽지 않습니다.
위의 해결 방법에도 여전히 문제가 있습니다. 우리 시스템에 복사로 인해 생성된 개체가 많은 경우 각 클래스에 대해 clone() 메서드를 작성하면 전체 복사도 수행됩니다. , 많은 수의 새 객체를 생성합니다. 이 프로젝트는 매우 규모가 크므로 여기서는 직렬화를 사용하여 객체를 복사할 수 있습니다.
2. 직렬화를 사용하여 객체 복사
직렬화를 사용하여 객체를 복사하는 방법은 무엇입니까? 메모리에 바이트 스트림을 복사하면 비교적 쉽게 달성할 수 있습니다. 상위 객체를 바이트 스트림에 쓴 다음 바이트 스트림에서 읽어 새 객체를 생성할 수 있으며 새 객체와 상위 객체 간의 참조 공유 문제가 없습니다. 사물.
public class CloneUtils { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T obj){ T cloneObj = null; try { //写入字节流 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new ObjectOutputStream(out); obs.writeObject(obj); obs.close(); //分配内存,写入原始对象,生成新对象 ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ios); //返回生成的新对象 cloneObj = (T) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } }
이 도구 클래스를 사용하는 개체는 직렬화 가능 인터페이스를 구현해야 합니다. 그렇지 않으면 복제를 수행할 방법이 없습니다.
public class Person implements Serializable{ private static final long serialVersionUID = 2631590509760908280L; .................. //去除clone()方法 } public class Email implements Serializable{ private static final long serialVersionUID = 1267293988171991494L; .................... }
따라서 이 도구 클래스를 사용하는 객체는 객체 복제를 달성하기 위해 Serialized 인터페이스만 구현하면 됩니다. clone() 메서드를 구현하기 위해 Cloneable 인터페이스를 상속할 필요는 없습니다.
public class Client { public static void main(String[] args) { //写封邮件 Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); Person person1 = new Person("张三",email); Person person2 = CloneUtils.clone(person1); person2.setName("李四"); Person person3 = CloneUtils.clone(person1); person3.setName("王五"); person1.getEmail().setContent("请与今天12:00到二会议室参加会议..."); System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent()); System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent()); System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent()); } } ------------------- Output: 张三的邮件内容是:请与今天12:00到二会议室参加会议... 李四的邮件内容是:请与今天12:30到二会议室参加会议... 王五的邮件内容是:请与今天12:30到二会议室参加会议...
위 내용은 Java Improvement Part 5 -----직렬화를 사용하여 객체 복사 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!