어떤 언어를 배우든 간에 처음 배우면 Hello World!
출력하는 것부터 하고 다음 단계가 변수이다. 자바의 변수에 대해서, 또한 대분류에 대해서 살펴본다.
변수는 Java에서 아래와 같이 선언한다.
int num;
마치 C언어와 같은 모습을 띄고 있다. 여기에 값을 넣는것 까지 진행하면 어떨까? 이 역시도 C와 동일하다. 이롸 같이 변수에 값을 넣어 메모리를 할당 하는 것을 초기화라고 한다.
num = 5;
기본적으로 선언을 했으면 반드시 초기화를 해줘야 한다. 처음에 선언하면 메모리에 어떤 값이 들어갔는지 알 수 없기 때문이다. 그리고 선언과 초기화를 동시에도 진행 가능하다.
int num = 5;
일반적인 형태는 아래와 같다. 변수명 앞의 타입을 자료형(data type)이라 하고, 정해진 값을 리터럴(literal)이라 한다.
자료형 변수명 = 리터럴;
변수는 크게 일반 변수, 레퍼런스 변수로 분류가 가능하고, 지역 변수, 특별히 자바에서는 클래스 변수라는 개념도 존재한다.
일반 변수는 앞에서 언급한 리터럴, 즉 값이 존재한다. 값을 가지고 있기 때문에, 컴파일 시점에 크기가 정해져 있다. 아래는 각종 일반 변수를 만드는 자료형 및 할당 받는 크기를 나타낸 표이다.
따라서 변수를 출력하면 아래와 같이 바로 값이 나온다.
int num = 5;
System.out.println(num);
5
레퍼런스 변수의 생성과 초기화는 아래와 같다.
Test test;
test = new Test();
일반 변수와 비슷한 면이 있는 것을 알 수 있는데, 가장 큰 차이점은 바로 new
연산자이다. 바로 변수를 초기화할 때 사용한다.
레퍼런스 변수(또는 참조 변수)는 따로 값을 가지고 있는 것이 아니라 주소를 가지고 있다. 즉 컴파일 시점에는 크기를 알 수 없고 실행할 때 new
명령어를 만나면 그제서야 메모리에 할당받게 된다. 할당 받는 과정을 살펴보자.
선언만 할 경우에는 아직 아무것도 없는 상태이다. 이 때 레퍼런스 변수는 초기값으로 null
을 가지게 된다. 아무것도 지정이 안되었다는 뜻이다. 그리고 이 할당이 안되었을 때, 개발자들을 괴롭히는 NullPointerException
이 발생하게 된다. 이제 초기화를 하면 어떻게 될까?
이 그림에서와 같이 Stack에서는 주소를 가지고 있고, 실제 데이터의 경우에는 Heap 메모리 상에 있는 것을 확인할 수 있다. Test의 인스턴스가 생성이 되고 Heap 메모리에 실제 데이터가, Stack에는 주소가 적혀있고, 실제 'test' 변수의 경우 값이 저장되는 것이 아닌 Heap 메모리에 있는 값을 참조할 뿐이다.
stack 과 heap에 대해서는 여기를 참고하면 된다.
만약, 여기서 test = new Test();
가 또 있으면 어떻게 될까?
그림에서처럼 새로운 주소를 할당받게 된다. 그리고 기존에 있던 0x11
주소에 있는 객체는 더이상 연결할 수 없게 된다. 그리고 GC(Garbage Collector)가 이를 시간이 지나면 정리하게 된다.
그러면 왜 값을 직접 가지는게 아닌 값을 참조할 뿐일까? 바로 일반 변수와는 다르게 실행 시점까지 얼마나 메모리를 할당 받아야 하는지 알 수 없기 때문이다. 기존에 이미 크기가 정해진 일반 변수와는 달리 레퍼런스 변수는 크기가 정해져있지 않다. JVM은 관심법을 쓰는게 아니다. 실행이 되고 new
를 만나야지만 그 메모리를 할당 받을 수 있다.
자바에는 클래스 변수라고 하는 것이 있다. 아래와 같이 선언하면 된다.
static int num;
바로 static
이 붙으면 된다. 그러면 stack과 heap이 있는 쓰레드를 벗어나 JVM의 메소드 영역에 저장이 된다. 해당 영역에서는 컴파일을 하면서 필요한 클래스 정보들과 더불어 static
이 붙은 모든 것들(클래스 변수, 클래스 메소드, ...)을 저장하게 된다.
즉, 이 변수는 객체와는 별개이다.
클래스 변수와 반대 개념으로 각 객체마다 있는 변수가 인스턴스 변수이다.