자바는 객체지향 언어입니다. 본격적인 객체를 학습하기 전에 먼저 참조 타입의 종류와 참조 변수의 역할이 무엇인지 정확히 이해하는 것이 중요합니다.
자바의 타입은 크게 기본 타입, 참조 타입으로 분류됩니다.
기본 타입은 이전에 배웠던 정수, 실수, 문자, 논리 리터럴을 저장하는 타입을 말합니다.
지금까지 우리는 기본 타입으로 변수를 선언하고 데이터를 저장했습니다.
이 페이지에서 말하는 참조 타입이란 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스를 말합니다.
기본 타입과 참조 타입
기본 타입으로 선언된 변수와 참조 타입으로 선언된 변수의 차이점은 저장되는 값입니다.
기본 타입인 byte, char, short, int, long, float, double, boolean 변수는 실제 값을 변수 안에 저장하지만 참조 타입인 배열, 열거, String, 클래스, 인터페이스 변수는 메모리의 번지를 변수 안에 저장합니다.
두 종류의 자료형을 구분하는 가장 명확한 방법은 실제 컴퓨터에 저장될때 해당 위치에 갔을때 실제 해당 값이 들어 있는지 실제 해당 값이 들어 있는 주소가 들어 있는지에 따라 결정됩니다.
즉 값이 들어 있으면 기본 자료형이고 주소가 들어 있으면 참조 자료형이 된다는 말입니다.
예를 들어 int 타입과 double 타입으로 선언된 변수 age와 height가 있고, String 클래스로 선언된 name과 hobby가 있다고 가정해보겠습니다.
int 타입 변수인 age와 height는 직접 값을 저장하고 있지만 String 클래스 변수인 name과 hobby는 힙 영역의 String 객체 주소 값을 가지고 있습니다.
이처럼 주소를 통해 객체를 참고하기 때문에 String 클래스 변수를 참조 타입 변수라고 합니다.
메모리 사용 영역
본격적으로 참조 타입을 알아보기 전에 우선 JVM이 사용하는 메모리 영역에 대해서 알아보겠습니다.
JVM은 운영체제에서 할당받은 메모리 영역을 아래와 같이 세부 영역으로 구분해서 사용합니다
메서드 영역
메서드 영역은 JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역입니다.
영역에는 코드에서 사용되는 클래스들을 클래스 로더로 읽어 클래스별로 정적 필드와 상수, 메서드 코드, 생성자 코드 등을 분류해서 저장합니다.
힙 영역
힙 영역은 객체와 배열등 자바 프로그램에서 사용되는 모든 인스턴스 변수가 저장되는 영역입니다.
여기에 생성된 객채와 배열은 스택 영역의 변수나 다른 객체의 필드에서 참조합니다.
크기가 명확하지 않거나 큰 데이터들, 즉 참조자료형이 힙에 저장되게 됩니다.
new 키워드를 사용하여 인스턴스가 생성되면, 해당 인스턴스의 정보를 힙 영역에 저장합니다.
만일 참조하는 변수나 필드가 없다면 의미 없는 객체가 되기 때문에 JVM이 이것을 쓰레기로 취급하고 자동으로 제거합니다.
따라서 개발자는 객체를 제거하기 위해 별도의 코드를 작성할 필요가 없습니다.
스택 영역
프로그램이 동작하면서 순서대로 저장되는 메모리 입니다.
Stack 에는 heap 영역에 생성된 데이터들에 대한 참조를 위한 값들이 할당되는 곳입니다.
기본 자료형(primitive types) - byte, short, int, long, double, float, boolean, char 타입의 데이터들이 할당됩니다.
이때 기본자료형의 데이터들에 대해서는 참조값을 저장하는 것이 아니라 실제 값을 stack에 직접 저장하게 됩니다.
메서드를 호출할 때마다 프레임을 추가하고 메서드가 종료되면 해당 프레임을 제거하는 동작을 수행합니다.
프레임 내부에는 로컬 변수 스택이 있는데 기본 타입 변수와 참조 타입 변수가 추가되거나 제거됩니다.
스택 영역에 변수가 생성되는 시점은 초기화될 때, 즉 최초로 변수에 값이 저장될 때입니다.
변수는 선언된 블록 안에서만 스택에 존재하고 블록을 벗어나면 스택에서 제거됩니다.
아래의 코드를 예로 설명하겠습니다.
String v1 = "안녕"; // 1번 실행
if (v1 == "안녕") { // 2번 실행
int v2 = 10;
double v3 = 3.14;
}
boolean v4 = true; // 3번 실행
|
cs |
선언된 변수는 실행 순서에 따라서 다음과 같이 스택에 생성되고 소멸됩니다.
v2와 v3는 if 블록 내부가 실행되고 있을 때만 스택 영역에 존재하고 if 블록을 빠져나가면 소멸됩니다.
기본 타입 변수와 참조 타입 변수의 저장 영역은 다르다고 위에서 언급한 바 있습니다.
기본 타입 변수는 스택 영역에 직접 값을 가지고 있지만 참조 타입 변수는 스택 영역에 힙 영역의 객체 주소를 가집니다.
자바에서는 배열을 객체로 취급합니다.
아래의 배열을 확인해 보겠습니다.
int[] number = { 1, 3, 5, 7, 9, 10 };
|
cs |
이때 배열 변수인 number는 스택 영역에 생성되지만, 실제 1,3,5,7,9,10을 갖는 배열은 힙 영역에 생성됩니다.
배열 변수인 number에는 배열의 힙 영역의 주소가 저장됩니다.
참조 변수의 ==,!=연산
기본 타입 변수의 ==,!= 연산은 변수의 값이 같은지, 아닌지를 조사하지만 참조 타입 변수들 간의 ==,!= 연산은 동일한 객체를 참조하는지, 다른 객체를 참조하는지 알아볼 때 사용됩니다.
참조 타입 변수의 값은 힙 영역의 객체 주소이므로 ==,!= 연산은 결국 주소 값을 비교하는 것이 됩니다.
동일한 주소 값을 갖고 있다는 것은 동일한 객체를 참조한다는 의미입니다.
따라서 동일한 객체를 참조하고 있는 경우 ==연산의 결과는 true이고!=연산의 결과는 false입니다.
다음 그림을 살펴보겠습니다.
위 그림에서 Object 1과 Object2는 서로 다른 객체를 참조하고 있습니다.
하지만 Object2와 Object3은 같은 객체를 참조하고 있습니다.
따라서 == 및!=의 연산 결과는 다음과 같습니다.
Object1 == Object2 <----- 결과 false
Object1 != Object2 <----- 결과 true
Object2 == Object3 <----- 결과 true
Object2 != Object3 <----- 결과 false
|
cs |
null과 NullpointerException
null(널)은 자바에서 사용되는 특수 값입니다.
참조 타입 변수는 힙 영역의 객체를 참조하지 않는다는 뜻으로 null값을 가질 수 있습니다.
null을 간략하게 설명하겠습니다.
- null이란 아무것도 없음을 의미합니다. (0 또는 공백 : "")
- 모든 참조 유형에 대한 기본 값은 null입니다.
- null은 유효한 객체 인스턴스가 아니므로 할당되는 메모리가 없습니다.
null값도 초기값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성됩니다.
참고로 null은 public, static이나 같은 Java의
컴파일러가 대소문자를 구분하기 때문에 null을 쓰면 에러를 보게 될 것입니다.
다음 그림을 살펴보겠습니다.
참조 타입 변수가 null값을 가지는지 확인하려면 다음과 같이 ==,!=연산을 수행하면 됩니다.
위 그림에서 Object1은 힙 영역의 객체를 참조하고 있으며 Object12는 null값을 가지므로 결과는 다음과 같습니다.
Object1 == null <----- 결과 false
Object1 != null <----- 결과 true
Object2 == null <----- 결과 true
Object2 != null <----- 결과 false
|
cs |
지금까지 null에 대해 알아봤습니다.
그렇다면 NullpointerException는 무엇일까요?
자바는 프로그램 실행 도중 발생하는 오류를 예외라고 부릅니다.
예외는 사용자의 잘못된 입력으로 발생할 수도 있고, 프로그래머가 코드를 잘못 작성해서 발생할 수도 있습니다.
참조 변수를 사용하면서 가장 많이 발생하는 예외 중 하나로 NullpointerException이 있습니다.
이 예외는 참조 타입 변수를 잘못 사용하면 발생합니다.
참조 변수가 null을 가지고 있을 경우에는 참조 객체가 없으므로 변수를 통해 객체를 사용할 수 없습니다.
만약 null 상태에서 있지도 않은 객체의 데이터나 메서드를 사용하는 코드를 실행하면 NullpointerException이 발생합니다.
int[] intArr = null;
intArr[1] = 10; <----- NullpointerException
|
cs |
위 코드에서 intArr은 배열이므로 참조 변수입니다.
그래서 힙 영역의 객체를 참조하지 않는다는 뜻의 null값 초기화가 가능합니다.
이 상태에서 intArr [1]에 10을 저장하려고 하면 intArr변수가 참조하는 배열이 없기 때문에 NullpointerException이 발생합니다.
'개발자였던 것 > JAVA 기초' 카테고리의 다른 글
자바 문자열 함수/ length(), length, equals(), format() (0) | 2020.04.03 |
---|---|
클래스의 구성 (0) | 2020.03.24 |
배열 (0) | 2020.03.08 |
조건문 (0) | 2020.03.07 |
문자의 입력과 출력-Scanner (0) | 2020.03.06 |