값과 참조타입의 근본적인 차이점은 데이터가 저장되는 메모리상의 위치

값 - 크기가 작고 고정적이기 때문에 스택에 생성되고
참조 - 가변적이기 때문에 동적으로 관리되는 힙에 생성

값 - 선언만 하면 스택에 즉시 생성되므로 선언 직후부터 데이터를 저장하는 용도로 사용할수 있다
참조 - 선언에 의해 참조만 생성될 뿐이지 데이터를 저장할 수 있는 실제 메모리가 할당되는 것은 아니므로
        선언 즉시 사용할 수 없다, 반드시 new 연산자로 메모리를 할당받아 초기화해야 한다


1)

value는 선언문에 의해 데이터를 저장할 수 있는 메모리를 스택에 확보하므로 바로 값을 대입하거나 읽을수 있다

ar은 정수형의 배열인데, 이 선언문만으로는 배열 요소를 저장할 수 있는 메모리를 확보하지 못한다
선언만 한 참조 타입은 배열을 가리킬 수 있는 참조자일뿐이며 null로 초기화되므로 아직 데이터를 저장할수 없다
ar = new int[10] 으로 힙헤 충분한 크기의 메모리를 할당해야 한다

2)


값 - 변수를 선언한 메소드가 종료될때나 소속된 객체가 사라질 때 자동으로 파괴된다
참조 - 더 이상 참조하는 변수가 없을때 가비지 컬렉터에 의해 파괴된다

3)



같은 타입의 변수로 대입할때의 효과가 다르다
값 - 복사에 의해 완전한 별개의 사본이 생성되며 복사후 사본과 원본은 별개의 변수, 완전히 다른 두 변수이다
참조 - 힙에 할당된 데이터를 참조하는 참조자가 하나 더 늘어날뿐 별도의 메모리가 추가로 할당되는 것은 아니다
       - 둘 중 하나를 변경하면 상대쪽도 같이 변경

정수형의 value와 vcopy를 선언하고 value만 3으로 초기화했다. 이 상태에서 value의 값을 vcopy로 대입하면 vcopy가 value와 같은 값을 가지는 사본이 된다. 두 변수는 스택상에 자신의 데이터를 저장하는 자기만의 메모리를 따로 확보하고 있으므로 대입할 때 일시적으로 값이 같을 뿐이지 완전히 다른 변수이다. 그래서 vcopy를 4로 바꾸어도 value는 원래값 3을 그대로 유지한다. 마찬가지로 value를 어떻게 수정하더라도 vcopy가 영향을 받지는 않는다.



똑같은 방법으로 참조형인 배열 ar과 arcopy를 선언하고 ar만 크기 5의 정수 배열로 초기화했다. arcopy는 초기화하지 않았으므로 null이다. 이 상태에서 ar을 arcopy에 대입했는데 이때는 arcopy가 ar의 데이터를 복사받는 것이 아니라 ar에 할당된 힙의 참조를 같이 가리킨다. 즉 힙에는 하나의 배열만 할당되어 있는데 참조자는 두 개인 상황이 되는 것이다. 이 상태에서 arcopy로 배열 요소값을 변경하면 동일한 대상을 가리키는 ar도 변경된다.

ar을 변경해도 마찬가지로 arcopy가 영향을 받는다. ar, arcopy 둘 다 가리키고 있는 대상이 동일하기 때문에 한쪽을 변경하면 다른 쪽도 영향을 받는 것이다. 참조 타입은 하나의 대상을 두 개 이상의 참조 변수가 가리킬 수 있다. 가비지 컬렉터는 누가 어떤 데이터를 가리키고 있는지를 항상 감시하며 더 이상 참조하는 변수가 없을 때만 힙의 데이터를 해제한다. 위 예제에서 ar과 arcopy가 모두 사라져야만 정수 배열 20바이트가 해제된다.


값 - 아무리 사본을 바꿔도 원본에 영향을 주지 못하며 이런 호출을 값 호출이라 한다.
참조 - 원본과 동일한 위치를 가리키기 때문에 사본으로부터 원본의 내용을 수정할 수 있으며 이런 호출을 참조 호출이라 한다.


4)



값 - 기억된 데이터만 같으면 두 변수를 같은 것으로 평가
참조 - 힙 상의 번지를 비교하기 때문에 내용이 완전히 같더라도 위치가 다르면 같지 않은 것으로 평가한다

정수형은 둘 다 3이라는 값을 가지고 있으므로 == 연산자가 값만 비교하여 같다고 평가한다. 하지만 배열은 차원과 배열 요소의 타입, 값이 완전히 같더라도 힙의 다른 위치를 가리키기 때문에 다르다고 평가한다. 만약 위치가 아닌 배열 자체의 상등 여부를 평가하고 싶다면 배열 요소들이 같은지를 직접 비교해야 한다.


5)




s1의 사본 s2를 만들어 놓고 s2를 변경했는데 원본인 s1은 원래의 문자열을 유지한다. 참조 타입은 힙에 하나의 데이터만 있고 대입하면 같은 대상을 가리키므로 이런 결과는 다소 의외이다. 문자열은 분명히 참조형이므로 대입에 의해 같은 대상을 가리키는 것까지는 참조 타입의 성격대로이다. 그러나 힙의 문자열 자체가 읽기 전용이기 때문에 대입하면 내용이 바뀌는 것이 아니라 아예 새로운 문자열이 생성된다



그래서 사본을 바꾸어도 원본이 영향을 받지 않는다. 문자열끼리의 대입에 의해 일단 같은 위치를 가리키지만 다른 문자열 상수를 대입하면 새로운 기억 장소를 할당받기 때문에 값 타입처럼 동작하는 것으로 보일 뿐이다. 또 문자열끼리의 비교는 위치를 비교하는 것이 아니라 실제 문자열 자체를 비교한다. == 연산자가 참조되는 문자열 자체를 비교하도록 오버로딩되어 있다.

요약하자면 문자열은 힙에 할당되는 참조형이지만 대입과 비교시에는 다른 참조형과는 조금 다르게 동작한다. 그렇다면 문자열에 대해서는 왜 이렇게 예외를 적용하는 것일까? 그 이유는 길게 설명할 필요없이 아주 간단하다. 그게 편하고 상식적이기 때문이다.

Posted by 코딩하는 야구쟁이
,