[Java] 자바에서 객체 참조는 어떻게 되는가

Jinyongmin·2024년 12월 12일

JAVA

목록 보기
2/2
post-thumbnail

상황 발생

코딩 테스트 문제를 풀다가 다음과 같은 코드를 작성했는데 내가 예상하지 못하는 결과가 나왔다.

코드

...

List<Set<Integer>> pCard = new ArrayList<>();  
for (int i = 0; i < 3; i++) {  
    pCard.add(new HashSet<>());  
}

... // 대충 pCard.get(num).add 하는 코드


while(true){
	...
	
	Set<Integer> temp = pCard.get(i);

	for (int j = 0; i + j * 3 < N; j++) {  
	    if (temp.contains(card[i + j * 3])) {  
	        temp.remove(card[i + j * 3])
	    }  
	}
	...
}

코드를 보면 Set<Integer> 를 List로 여러개 만들고 확인되는 값이 있으면 지워서 중복으로 되는 경우를 제거할 수 있도록 했다.

하지만, 반복문을 돌면서 원래 pCard의 Set이 remove가 되어 제대로 결과를 출력하지 못하게 되는데..

예시 코드

Set<Integer> init = new HashSet<>();  
init.add(1);  
init.add(2);  
init.add(3);  
  
System.out.println("init = " + init);  
  
Set<Integer> test = init;  
test.remove(3);  
  
System.out.println("init = " + init);  


출력 결과
init = [1, 2, 3]
init = [1, 2]

결과를 보면 분명 test의 있는 3을 remove했는데 init에도 결과가 반영된 것을 볼 수 있다. 그렇다면 왜 그런걸까?

객체 참조

문제에 대한 답은 객체 참조에서 알 수 있다.
객체 참조에 대해서 설명하기 전에 용어에 대해서 짚고 넘어가자

객체(Object)

Heap 메모리에 저장되는 실제 데이터를 말한다. 예를 들어, new String("Hello") 와 같은 명령어는 Heap 메모리에 "Hello"라는 문자열 객체를 생성한다.

참조(Reference)

객체가 Heap 메모리에 저장된 위치를 가리키는 변수를 말한다. 참조는 객체의 메모리 주소를 저장하지만, Java에서는 직접 주소를 다룰 수 없고 참조 변수를 통해서만 접근이 가능하다.

String greeting = new String("Hello");

즉, 다음과 같은 코드에서 greeting은 위치(주소), "Hello"는 Heap 메모리에 있는 데이터인 것이다.

이유는?

Set<Integer> init = new HashSet<>();  
init.add(1);  
init.add(2);  
init.add(3);  
  
System.out.println("init: " + init);  
System.out.println("init hash: " + System.identityHashCode(init));  
  
Set<Integer> test = init;  
System.out.println("test hash before modification: " + System.identityHashCode(test));  
  
test.remove(3);  
  
System.out.println("init after modification: " + init);  
System.out.println("test hash after modification: " + System.identityHashCode(test));


출력 결과
init: [1, 2, 3]
init hash: 288665596
test hash before modification: 288665596
init after modification: [1, 2]
test hash after modification: 288665596

객체가 참조하는 주소값을 출력해보면 다음과 같다. 288665596으로 test는 init과 같이 같은 주소를 참조하고 있는 것을 확인할 수 있다.

그래서 test를 수정하더라도 실제로 init이 가리키는 heap 메모리에 영역의 데이터를 수정하기 때문에 결과가 반영되는 것이다.

identityHashCode?
객체 고유한 해시 코드를 반환하는 메서드로, 객체의 메모리 참조 기준으로 생성되는 hashCode이다.

new로 새로운 객체를 생성하고 할당하면?

...

Set<Integer> test = new HashSet<>();  
test = init;

...

새로운 객체를 만들고 기존 데이터를 할당하면 어떻게 될까?

결론부터 말하면 Variable 'test' initializer 'new HashSet<>()' is redundant 다음과 같은 메세지를 출력한다. 대충 변수 test 초기화가 중복된다는 의미인데 자세한 이유는 다음과 같다.

  1. Set<Integer> test = new HashSet<>();
  • test 라는 변수를 new HashSet<>()으로 초기화한다.
  • 이 시점에서, test는 빈 HashSet을 가리킨다.
  1. test = init;
  • 이후 test에 init을 할당한다.
  • 결국, test가 가리키는 객체는 init으로 변경된다.

결론

대학 1학년, 객체 지향 프로그래밍 수업에서 C++ 로 지겹도록 배웠던 개념인데 자바를 계속 사용하면서 기본적인 것을 잊고 있었던 것 같다.😢

0개의 댓글