Call By Value(값에 의한 호출)
기본형 타입의 변수를 복사하게 되면 대상이 된 변수와 복사본인 변수는 서로 영향을 받지 않는다.
int a = 10;
int b = a; // a의 값(value)을 복사
System.out.println(a); // 10
System.out.println(b); // 10
b = 20; // 새로운 값 대입
System.out.println(a); // 10
System.out.ptintln(b); // 20
기본형 타입의 변수는 스택 영역에 자리를 잡을 때 변수별로 각자의 데이터 영역을 갖게 된다.
이 영역에 자신의 값을 저장하고 있기 때문에 변수 b는 생성될 때는 변수 a의 값을 복사 받아 10 이라는 데이터 값이 있었으나,
새롭게 20 이라는 값을 할당 받으며 데이터 값이 변경되는 것이다.
이때 변수 b 의 데이터 값이 변경되더라도 a 는 전혀 다른 영역에 존재하기 때문에 영향을 받지 않는다.
얕은 복사(Shallow Copy)
Call By Reference(참조에 의한 호출)
그렇다면 객체의 주소를 저장하고 있는 참조형 타입의 변수는 어떻게 될까?
결론부터 이야기 하자면 본질적으로는 큰 차이가 없다.
기본형 타입의 변수는 저장하고 있는 그 값 그대로를 복사의 대상으로 삼는다면,
참조형 타입의 변수는 저장하고 있는 객체의 주소를 복사의 대상으로 삼는 것이다.
Human a = new Human();
Human b = a;
System.out.println(a); // 같은 주소값이
System.out.println(b); // 출력된다.
a.age = 10;
System.out.println(a.age); // 10
b.age = 20;
System.out.println(a.age); // 20
System.out.println(b.age); // 20
예시를 통해 기본형 타입 변수와는 달리 a.age 의 값이 변경된 것을 확인할 수 있다.
객체의 주소를 복사한다는 것은 new 연산자로 힙 영역에 자리하는 객체를 똑같이 주소값으로 참조할 수 있게 된다는 것이다.
같은 객체의 주소값을 참조하고 있다는 것으로 두 변수 모두 객체의 변화에 영향을 받을 수 있게 된다.
따라서 a 값을 null을 주게 되면 참조하고 있는 객체가 없기 때문에 참조 변수 b 에게 영향을 받지 않을 수도 있게 된다.
이러한 복사 방식을 '얕은 복사', 즉 데이터 자체가 아니라 주소값만 보고 있는 상태이다.
깊은 복사(Deep Copy)
객체의 주소값을 복사하게 되면 그 주소값을 가지고 있는 참조 변수 모두에게 영향이 가는 것을 위의 예시에서 확인했다.
하지만 각 변수의 변화에 영향을 받지 않는 기본형 타입 변수처럼 참조 변수도 사용할 수 있지 않을까.
객체를 복사할 때 새로운 주소를 참조할 수 있도록 해주면 서로의 영향을 받지 않을 수 있게 된다.
Cloneable interface
Object 의 clone() 메서드를 사용하면 기본적으로는 얕은 복사가 된다.
그렇기 때문에 일종의 가공이 필요하다.
1. Cloneable interface를 implements 받기
2. clone() 메서드를 override 하기
3. try - catch 구문을 이용하여 CloneNotSupportedException 하기
public class Wallet implements Cloneable {
int money;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
만약 a 라는 사람과 b 라는 사람이 있을 때, 각 5만원의 용돈을 받아 지갑 관리를 시작했다고 해보자.
a 는 소비하지 않고 저금을 했고, b 는 2만원을 썼다면 이 둘의 남은 금액은 다르다.
하지만 둘 다 같은 지갑을 바라보고 있다면 원하지 않더라도 서로에게 영향을 줄 수 있다.
그렇기 때문에 지갑 정보는 각 객체마다 저장 장소가 따로 있어야 한다.
// 원본 객체
Wallet wallet = new Wallet();
// 5만원이 들어 있는 지갑 객체
wallet.money = 50000;
// 얕은 복사 - 주소 값 복사
Wallet shallow = wallet;
// 깊은 복사 - 실제 값 복사
// 아직 참조하는 값 없음
Wallet deep = null;
// try - catch 문을 통해 새로운 주소 값에 복사
try {
deep = (Wallet) wallet.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
// 원본 객체 값 변경 시도
wallet.money = 30000;
// 원본 출력
System.out.println(wallet.money); // 30000
// 얕은 복사 출력
System.out.println(shallow.money); // 30000
// 깊은 복사 출력
System.out.println(deep.money); // 50000
얕은 복사로 원본 객체의 참조 변수 wallet 과 같은 주소 값을 가지고 있던 shallow 는
원본 객체 값을 변경하자 함께 변경된 것을 예제로 확인 할 수 있다.
하지만 깊은 복사는 개별의 새로운 저장 공간이 생긴 객체의 주소 값을 바라보고 있기 때문에
처음 값을 그대로 가지고 변경되지 않았다.
'JAVA' 카테고리의 다른 글
배열 (1) | 2022.09.10 |
---|---|
클래스 객체 인스턴스 (0) | 2022.09.09 |
객체 지향 4대 특성 : 다형성, 캡슐화 (0) | 2022.08.22 |
객체 지향 4대 특성 : 추상화, 상속 (0) | 2022.08.21 |
클래스(Class) 와 객체(Object) (0) | 2022.08.20 |