본문 바로가기

2022 여름방학 자바 스터디

[이것이 자바다] Ch05. 참조 타입

5.1. 데이터 타입 분류

자바의 데이터 타입은 크게 기본 타입과 참조 타입으로 분류됨
기본 타입이란, 정수, 실수, 문자, 논리 리터럴을 저장하는 타입을 말함
참조 타입이란 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스 타입을 말함

- 기본 타입을 이용해서 선언된 변수는 실제 값을 변수 안에 저장함

- 참조 타입인 배열, 열거, 클래스, 인터페이스를 이용해서 선언된 변수는 메모리의 번지를 값으로 가짐 (번지를 통해 객체를 참조)

 

- 변수는 스택 영역에 생성되고, 객체는 힙 영역에 생성됨

- 아래의 예시에서 int, double은 직접 값을 저장하고 있지만 String 클래스 변수는 객체 주소 값을 가지고 있음

// 기본 타입 변수
int age = 25;
double price = 100.5;

// 참조 타입 변수
String name = "김우리";
String hobby = "독서";

5.2. 메모리 사용 영역

5.2.1. 메소드 영역

- JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역

- 코드에서 사용되는 클래스들을 클래스 로더로 읽어

  클래스 별로 런타임 상수풀, 필드 데이터, 메소드 데이터, 메소드 코드, 생성자 코드 등을 분류해서 저장함

힙(Heap) 영역

- 객체와 배열이 생성되는 영역

- 힙 영역에서 생성된 객체와 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조함

- 참조하는 변수나 필드가 없다면 의미없는 객체가 되기 때문에 이것을 쓰레기로 취급함

- JVM은 쓰레기 수집기를 실행시켜 쓰레기 객체를 힙 영역에서 자동으로 제거함

- 따라서 개발자는 객체를 제거하기 위해 별도의 코드를 작성할 필요가 없음

- 자바는 코드로 객체를 직접 제거시키는 방법을 제공하지 않음

JVM 스택(Stack) 영역

- 각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 할당됨

- 자바 프로그램에서 추가적으로 스레드를 생성하지 않았다면 메인 스레드만 존재하므로, JVM 스택도 하나임

- JVM 스택은 메소드를 호출할 때마다 프레임을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop)하는 동작을 수행함

 

- 기본 타입 변수는 스택 영역에 직접 값을 가지고 있지만, 참조 타입 변수는 값이 아니라 힙 영역이나 메소드 영역의 객체 주소를 가짐

- 다음과 같은 배열 변수는 스택 영역에 생성되지만, 실제 값은 힙 영역에 생성됨

- 배열 변수에는 배열의 힙 영역의 주소가 저장되어 있음

int[] scores = {10, 20, 30};

5.3. 참조 변수의 ==, != 연산

- 기본 타입 변수의 ==, != 연산은 변수의 값이 같은지 아닌지 단순히 조사

- 참조 타입 변수의 ==, != 연산은 동일한 객체를 참조하는지, 다른 객체를 참조하는지 조사

- 즉, 힙 영역의 객체 주소 값을 비교하는 것

5.4. null과 NullPointerException

- 참조 타입 변수는 힙 영역의 객체를 참조하지 않는다는 뜻으로 null 값을 가질 수 있음

- null 값도 초기값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성됨

- 참조 타입 변수가 null 값을 가지는지 확인하려면 ==, !=연산을 수행해보면 됨

 

NullPointerException

- 참조 타입 변수가 null을 가지고 있을 경우, 참조 타입 변수는 사용할 수 없음

- 참조 타입 변수를 사용하는 것은 곧 객체를 사용하는 것을 의미하는데, 참조할 객체가 없으므로 사용할 수 없는 것임

- 이 경우 NullPointerException이 발생하게 됨

String str = null;
System.out.println("총 문자수: " + str.length()); // NullPointerException

5.5. String 타입

- 문자열을 String 변수에 저장한다는 표현은 틀린 표현임

- 문자열은 String 객체로 생성되고, 변수는 String 객체를 참조함

- 자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 되어 있음

// 동일한 객체를 참조하는 경우
String name1 = "김우리";
String name2 = "김우리";

 

- 일반적으로 변수에 문자열을 저장할 경우 문자열 리터럴을 사용하지만, new연산자를 사용해 직접 String 객체 생성 가능

- new연산자는 힙 영역에 새로운 객체를 만들 때 사용하는 연산자로, 객체 생성 연산자라고 함

// 동일하지 않은 객체를 참조하는 경우
String name1 = new String("김우리");
String name2 = new String("김우리");

 

- 동일한 문자열 리터럴로 String 객체를 생성했을 경우 == 연산의 결과는 true

- new 연산자로 String 객체를 생성했을 경우 == 연산의 결과는 false

- 동일한 객체이건 아니건 상관없이 문자열만을 비교할 때는 equals() 메소드를 사용해야 함

- equals() 메소드는 원본 문자열과 매개값으로 주어진 비교 문자열이 동일한지 비교한 뒤 true, false 리턴

 

- 다음 코드처럼 hobby가 String 객체를 참조했으나, null을 대입함으로써 더 이상 String 객체를 참조하지 않도록 할 수 있음

- 그렇다면 참조를 잃은 String 객체는?

  JVM은 참조되지 않은 객체를 쓰레기 객체로 취급하고 쓰레기 수집기를 구동시켜 메모리에서 자동 제거함

String hobby = "여행";
hobby = null;

5.6. 배열 타입

5.6.1. 배열이란?

- 같은 타입의 많은 양의 데이터를 다루는 경우 사용함 (배열은 같은 타입의 데이터만 저장 가능)

- 배열은, 같은 타입의 데이터를 연속된 공간에 나열시키고 각 데이터에 인덱스를 부여해 놓은 자료구조

- 한 번 생성된 배열은 길이 변경이 불가능함

5.6.2. 배열 선언

- 대괄호는 배열 변수를 선언하는 기호로 사용

- 타입 뒤에 붙을 수도 있고 변수 뒤에 붙을 수도 있음

타입[] 변수; 타입 변수[];
int[] arr; int arr[];

 

- 배열 변수는 참조 변수에 속함

- 배열도 객체이므로 힙 영역에 생성되고, 배열 변수는 힙 영역의 배열 객체를 참조하게 됨

- 참조할 배열 객체가 없다면 배열 변수는 null 값으로 초기화될 수 있음

- 만약 배열 변수가 null 값을 가진 상태에서 변수[인덱스]로 값을 읽거나 저장하게 되면 NullPointerException이 발생하게 됨

- 배열 변수는 배열을 생성하고 참조하는 상태에서 값을 저장하거나 읽어야 함

5.6.3. 값 목록으로 배열 생성