자바 향상 편 (5) - 직렬 화 실현 대상 의 복사 사용

자바 에 이 인터페이스 Cloneable 이 존재 한 다 는 것 을 알 고 있 습 니 다. 이 인 터 페 이 스 를 실현 하 는 종 류 는 모두 복사 되 는 능력 을 가 집 니 다. 또한 복사 하 는 것 은 메모리 에서 이 루어 집 니 다. 성능 에 있어 서 우리 가 new 를 통 해 대상 을 직접 생 성 하 는 것 보다 빠 릅 니 다. 특히 큰 대상 의 생 성 에 있어 서 성능 의 향상 이 매우 뚜렷 합 니 다.그러나 우 리 는 복사 가 깊 은 복사 와 얕 은 복사 로 나 뉘 는 것 을 알 고 있 지만 얕 은 복사 에는 대상 속성 복사 가 철저 하지 못 한 문제 가 존재 한다.깊 은 복사, 얕 은 복사 에 관 한 것 은 여 기 를 참고 하 십시오: 점 석 자바 의 얕 은 복사 와 깊 은 복사
       질문
      우 리 는 먼저 다음 코드 를 본다.
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         ...

      이 프로그램 에서 먼저 메 일 한 통 을 정의 한 다음 에 이 메 일 을 장 삼, 이사, 왕 오 세 사람 에 게 보 냅 니 다. 그들 은 같은 메 일 을 사용 하고 이름 만 다 르 기 때문에 장 삼 을 사용 하여 이 대상 류 를 복사 한 다음 에 이름 을 바 꾸 면 됩 니 다.프로그램 이 여기까지 온 것 은 틀 리 지 않 았 습 니 다. 그러나 만약 에 우리 가 3 분 전에 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());
    }
}

       여기 서도 장 삼 이라는 대상 을 이용 하여 이사, 왕 오 에 대한 복사 본 을 실현 하고 마지막 으로 장 삼 의 메 일 내용 을 오늘 12 시 부터 2 회의실 까지 회의 에 참석 하 십시오.하지만 결 과 는:
        :    12:00         ...
        :    12:00         ...
        :    12:00         ...

      여기 서 우 리 는 왜 이사 와 왕 오 의 메 일 내용 도 바 뀌 었 는 지 의 심 스 러 웠 다.30 분 일찍 오 라 고 하면 의견 이 있 을 거 야!
      사실 문제 가 발생 하 는 관건 은 clone () 방법 에 있 습 니 다. 우 리 는 이 clone () 방법 이 Object 류 의 clone () 방법 을 사용 하 는 것 이라는 것 을 알 고 있 습 니 다. 그러나 이 방법 에 결함 이 존재 합 니 다. 대상 의 모든 속성 을 복사 하지 않 고 선택 적 인 복사 가 있 습 니 다. 기본 규칙 은 다음 과 같 습 니 다.
      1. 기본 유형
         변수 가 기본 적 인 유형 이 라면 int, float 등 값 을 복사 합 니 다.
      2. 대상
          변수 가 인 스 턴 스 대상 이 라면 주소 인용 을 복사 합 니 다. 즉, 이때 새 대상 과 원래 대상 은 공용 인 스 턴 스 변수 입 니 다.
      3. String 문자열
         변수 가 String 문자열 이면 주소 참조 복사.그러나 수정 할 때 문자열 풀 에서 새로운 문자열 을 다시 생 성 합 니 다. 원래 의 자색 도성 대상 은 변 하지 않 습 니 다.
      위의 규칙 을 바탕 으로 우 리 는 문제 의 소 재 를 쉽게 발견 할 수 있다. 그들 세 사람 은 한 대상 을 공용 하고 장 삼 은 이 메 일의 내용 을 수정 하면 이사 와 왕 오 도 수정 하기 때문에 위의 상황 이 발생 한다.이러한 상황 에 대해 우 리 는 해결 할 수 있 습 니 다. clone () 방법 에서 대상 을 새로 만 든 다음 에 이 대상 을 세 번 인용 하면 됩 니 다.
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;
    }

      따라서 얕 은 복사 본 은 자바 가 제공 하 는 간단 한 복사 메커니즘 일 뿐 직접 사용 하기 불편 하 다.
      위의 해결 방안 에 아직도 문제 가 존재 한다. 만약 에 우리 시스템 에 대량의 대상 이 복사 로 생 성 된다 면 우리 가 모든 유형 에 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;
    }
}

      이 도구 류 를 사용 하 는 대상 은 Serializable 인 터 페 이 스 를 실현 해 야 합 니 다. 그렇지 않 으 면 복 제 를 실현 할 방법 이 없습니다.
public class Person implements Serializable{
    private static final long serialVersionUID = 2631590509760908280L;

    ..................
    //  clone()  

}

public class Email implements Serializable{
    private static final long serialVersionUID = 1267293988171991494L;
    
    ....................
}

      따라서 이 도구 류 를 사용 하 는 대상 은 Serializable 인터페이스 만 실현 하면 대상 의 복 제 를 실현 할 수 있 으 며, Cloneable 인 터 페 이 스 를 계승 하여 clone () 방법 을 실현 할 필요 가 없다.
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         ...

기 초 를 공 고 히 하고 기술 을 향상 시 키 며 어려움 을 두려워 하지 않 고 고봉 등반!!!!!!
       참고 문헌 인 진 소 파

좋은 웹페이지 즐겨찾기