참조형 변수 짚고 가기

최동민·2022년 6월 8일
1

Spring 수업정리

목록 보기
17/47

자바라는 언어를 배우며, 스프링에서 이를 활용할 때에 기본적인 개념에 부딪히는 경우가 잦은 요즘이다. 역시 기본기가 중요하다는 생각을 하게 되면서 오늘 되짚고 가보려 하는 부분은 바로 참조 변수이다.

참조형 변수(Reference Variable)


  • 참조형 변수 저장값

우리가 흔히 알고 있는 기본형 변수(Primitive Variable)는 값을 저장하는 저장공간이다. 참조형 변수와는 뭐가 다를까? 참조형 변수도 변수이기에 값을 저장하는 공간이다. 하지만 그 값이 기본형 변수와는 다르다.
기본형 변수에 저장되는 값은 리터럴. 즉, 실제 값이 저장된다.
있는 그대로의 값. 1, A, true 등을 말한다.
하지만 참조 변수는 실제 값이 아닌 주소 값을 저장한다.

우리가 사용하는 컴퓨터의 메모리는 메모리 주소라는 개념을 담고 있다.
즉 메모리 안에서도 주소별로 저장 공간이 있다는 것.
그렇다면 주소값이 어떻게 저장되는 걸까?

메모리 안을 확인해보면
기본형 변수는 실제 값이 저장이 되는데 만약 우리가 변수를 선언하면 Stack이라는 영역에 선언한 그 변수만큼의 공간이 생성이 되고 그 공간 안에 값이 들어가게 된다.

int i = 10;

사진과 같이 Stack영역에 4byte크기의 이름이 i라는 공간에 생성되고 그 안에 10이 들어가게 되는 것이다.

그런데 참조형 변수는

String str = new String("java"); 

선언 시 기본형 변수처럼 Stack영역에 str이라는 이름을 가진 공간이 생성이 된다. 생성되는 변수의 크기는 4byte의 고정된 크기만큼 생성이 되고, 다음으로 new라는 명령어로 Heap영역에 새로운 저장공간을 생성하게 한다.
그 공간의 크기는 우리가 넣으려는 값(java)의 크기만큼 생성된다.
그리고 이 공간은 메모리 주소 값을 할당받게 된다.

즉 Heap영역에 공간이 생성되고 주소 값을 할당받으면 그 주소 값이
Stack영역에 생성된 Str이라는 장소에 저장되는 것이다.

정리하자면, 기본형 변수는 Stack영역에서 실제 값을 그대로 저장하지만 참조형 변수는 먼저 Stack영역에 공간을 생성하고 Heap영역에 새로운 공간을 생성해 실제 값을 저장한 다음에 그 공간의 주소 값을 불러와서 Stack영역의 공간에 저장한다는 것이다.

기본형 변수는 실제 값을 저장하고 참조형 변수는 주소 값을 저장한다.


  • 참조형 변수 종류

기본형 변수와 참조형 변수의 차이점에 대해 알아보았다면 참조형 변수 종류에는 어떤게 있을까?
우선 기본형 변수는 총 8개의 자료형을 가지고 있고 실제 값, 리터럴에 맞는 형태로 선언해줘야 한다고 했다.
하지만 참조형 변수의 형태는 기본형 변수처럼 정해져 있지 않다.
사용자가 원한다면 자기가 원하는, 필요한 변수형을 만들어 사용할 수 있기 때문이다. 정확히 말하자면 기본형 8가지를 제외한 모든 것이라 할 수 있다.
이러한 변수형을 만든다는 것은 개발자 입장에서 기본형 변수에만 국한되는 값만 사용하는 것이 아니라 기능과 특성까지 만들고 사용할 수 있다는 획기적인 말이 되는 것이기도 하다.
기본적으로 우리가 아는 참조형 변수 2가지를 알아볼 것인데, 참고로 기본형 변수에서는 자료형(Data Type)이라 했지만, 참조형 변수에서는 자료형이 아닌 형태, 즉 타입(Type)이라 한다.

String str = new String("자바");
String이라는 참조 변수가 있다. 선언된 변수(str)에 저장되는 값의 타입, 그 변수의 크기를 지정하는 역할을 할 것이다.
여기서 쓰이는 new는 생성자(Constructor)라는 이름을 가지고 있다. 사전적 의미 그대로 새로운. 새로운 무언가를 만든다는 의미이다.
String 이라는 참조 변수를 new라는 생성자가 새로 생성한다.
문자열을 처리하는 String이라는 참조 변수를 생성해 "자바"라는 문자열을 변수에 저장한다는 뜻이 되겠다.
그리고 이렇게 new로 생성할 때마다 새 집을 준다. 생성된 참조 변수는 모두 다른 주소를 갖게 된다는 것.

해석해보면, 문자열을 처리하는 String타입의 참조 변수를 선언하고, 이름이 str이라는 변수를 메모리 Stack영역에 4byte 크기의 저장공간만큼 생성한 다음, new라는 생성자를 이용해 String 참조 변수(객체)를 생성해 ()안에 있는 문자열 "자바"라는 값을 메모리 Heap영역 어딘가에 저장한 후, 그 저장공간의 주소 값을 Stack영역에 생성된 str이라는 이름을 가진 공간에 저장하는 것이다.
Scanner sc = new Scanner(); 도 마찬가지.


  • 참조형 변수 초기화

String str = new String();
String str = null; 과 같은 말이다.

이렇게 참조형 변수를 초기화 한다.
초기화? 복잡하게 생각할 것 없이 선언과 동시에 값을 넣어주는 것.

null은 참조형 변수의 기본 값이다.
null은 사전적 의미대로 아무런 가치가 없는 그냥 값이라 할 수 있다.
사실 의미가 없는 값이라면 0이나 ''(공백)도 해당할 수 있지 않겠냐 생각할 수 있겠지만 컴퓨터 입장에서는 다르다. 이들은 모두 그 자체의 의미를 가지고 있는 리터럴, 즉. 실제 값이라는 것이다.
하지만 null은 어떠한 형태도 형식도 가지지 않은 진짜 말 그대로의 아무런 의미가 없는 값이다.
(이를 통해 우리는 기본형 변수에는 해당 자료형에 맞는 값 밖에 저장하지 못하기 때문에 어떠한 형태도 없는 값인 null은 기본형 변수에 저장할 수 없겠다는 것을 알 수 있겠다.)
클래스 영역에서 String str; 이라고만 선언을 해도 자동으로 str에는 null이 들어간다.
이것이 바로 참조형 변수의 기본 값이다.


참조형 변수에 리터럴 선언한다면

만약 참조형 변수를 선언할 때에 new 생성자를 사용한 선언이 아니라 리터럴, 즉, 실제 값을 넣어 선언한다면 어떻게 될까?
기본형 변수 선언하듯이 말이다.

String str = "자바"; 

아무런 문제가 없다. 기본형 변수처럼 선언이 가능하다는 뜻이다.
하지만 메모리에 저장되는 과정이 달라지게 된다.

String str1 = "자바";
String str2 = "자바";
String str3 = new String("자바");

이 3개의 코드를 통해 알아보자.

위의 str1과 str2는 new 생성자를 사용하지 않고 리터럴을 그대로 넣은 것이고 str3은 new 생성자를 사용해 새로운 참조 변수를 생성해 만든 것이다.

String str1 = "자바";

별 차이가 없어보인다. 하지만 str2를 실행해보자.

String str1 = "자바";
String str2 = "자바";

어찌된 일일까? Heap영역에 똑같은 주소가 저장이 되어 보인다.
new 생성자를 사용하면 참조 변수를 생성할 때마다 새로운 주소를 할당받는다 했었는데 new 생성자를 사용하지 않았기 때문일까?
str3까지 실행해보자

String str1 = "자바";
String str2 = "자바";
String str3 = new String("자바");

당연히 str3는 new생성자를 사용해 참조 변수를 선언했기 때문에 Heap영역에 새로운 곳에 "자바"라는 값을 저장하고 그 주소를 str3에 저장하였다.
하지만 리터럴로 선언한 참조형 변수 str1과2는 ?
만약 참조형 변수에 리터럴이 바로 들어오면 컴퓨터는 일단 Stack영역에 4byte 크기의 str1이라는 참조 변수를 생성한다. 그리고 Heap영역에 바로 "자바"라는 값을 생성해서 저장하는 것이 아니라 일단 Heap영역을 아주 빠르게 전체를 흝어본다. 지금 Heap영역에 "자바"라는 값을 가진 장소가 있는지 없는지를 검색한다는 뜻이다. Heap영역 전체를 흝어보고 만약 "자바"라는 값을 가진 장소가 없다면 그제서야 새롭게 주소를 할당하고 "자바"라는 값을 저장하고 그 주소 값을 str1에 저장하게 될 것이다.
(중간에 컴퓨터가 Heap영역을 검색한다는 과정이 추가되는 것)
그리고 다음 str2를 실행하게 되면 마찬가지로 일단 Stack영역에 str2를 생성하고 Heap영역을 전체적으로 흝어보게 된다. 이번에는 아까 str1을 생성할 당시 할당해 둔 장소가 존재하고 있기 떄문에 이 장소를 찾게 되면 이 장소의 주소 값을 str2에 저장하게 되는 것이다. 이러한 과정으로 인해 str1 str2는 똑같은 주소 값이 저장되게 되는 것이다.

정리하자면, 참조형 변수도 new 생성자가 아니라 기본형 변수처럼 리터럴로 바로 선언이 가능하다.
하지만 그렇게 선언하면 new 생성자로 선언하는 것과 다른 과정을 거치기 때문에
다른 두 참조 변수가 같은 주소 값을 저장할 수도 있는 상황이 발생할 수도 있다.

profile
코드를 두드리면 문이 열린다

0개의 댓글