📝 이것이 자바다

Chapter 05. 참조 타입

j_estory 2022. 11. 7. 09:30

5.1 데이터 타입 분류 

  • 자바의 타입
    • 기본 타입 : 정수, 실수, 논리 타입
    • 참조 타입 : 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스. 인터페이스 타입이 있다. 
더보기

객체란 ?

객체는 데이터와 메소드로 구성된 덩어리

객체 = 데이터(필드) + 메소드

  • 기본 타입 vs 참조 타입
    • 기본 타입은 값을 저장할 때, 값 자체를 저장하지만, 참조 타입은 객체가 생성된 메모리 번지를 저장한다. 
  • 메모리 상에서 변수들이 갖는 값
    • 변수들은 모두 stack 이라는 메모리 영역에 생성된다. 
    • 기본 타입인 price와 age는 직접 값을 저장하고 있지만, 참조 타입 변수인 name과 hobby는 번지를 저장하고 해당 번지를 통해 String 객체를 참조한다.

 

5.2 메모리 사용 영역

자바에서 사용하는 메모리 영역에 대해서 간단히 알아보자!

  • 자바 명령어로 jvm이 구동되면 jvm은 운영체제에서 할당받은 메모리 영역을 다음과 같이 구분해서 사용한다.
  • 메소드 영역
    • 바이트코드 파일을 읽은 내용이 저장되는 영역으로 클래스별로 상수, 정적필드, 메소드 코드, 생성자 코드 등이 저장된다. 
  • 힙 영역
    • 객체가 생성되는 영역이다.
    • 객체의 번지는 메소드 영역과 스택 영역의 상수와 변수에서 참조할 수 있다.
  • 스택 영역
    • 메소드를 호출할 때마다 생성되는 프레임이 저장되는 영역이다. 
    • 메소드 호출이 끝나면 프레임은 자동으로 제거된다. 
    • 프레임 내부에는 로컬 변수 스텍이 있다. 여기에는 기본 타입 변수, 참조 타입 변수가 생성되고 제거된다. 

 

5.3 참조 타입 변수의 ==. != 연산

  • 참조 타입 변수의 값은 객체의 번지이므로 참조 타입 변수의 ==. != 연산자는 번지를 비교하는 것이 된다. 
  • 번지가 같다면 동일한 객체를 참조하는 것이고, 다르면 다른 객체를 참조하는 것이다. 

 

5.4  null과 NullPointerException

  • 참조 타입 변수는 아직 번지를 저장하고 있지 않다는 뜻으로 null 값을 가질 수 있다.
  • null도 초기값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성된다. 
  • 참조 타입 변수의 null 값을 가지는지 확인하기 위해서 ==, != 연산자를 사용하여 확인한다.
  • 참조 변수를 사용하면서 가장 많이 발생하는 예외를 NullPointerException 이라고 한다. 
    • null인 변수는 해당 변수를 참조하는 객체가 없기 때문에 발생한다.
  • 프로그램에서 일부러 변수에 null을 대입하기도 하는데, 이는 해당 객체를 참조하는 변수를 이용해야 하는데, 변수에 null을 대입하게 되면 번지를 잃게 되므로 더이상 객체를 사용할 수 없게 된다.
    • 어떤 변수에도 객체를 참조하지 않으면 해당 객체는 프로그램에서 사용할 수 없는 객체가 된다. 
    • 즉, 힙 메모리에는 있지만 위치 정보를 모르기 때문에 사용할 수 없다.
      • 자바는 이러한 객체를 쓰레기로 취급하고 Garbage Collector를 실행시켜 자동으로 제거한다. 
      • 자바는 객체를 직접 제거하는 방법을 제공하지 않는다. 
        • 제거하는 방법 -> 객체의 참조를 없애는 것
// 예제
// hobby 변수에 영화가 대입하면서 다른 String 객체의 번지가 대입되어 이전 번지를 잃어버리게 된다.
String hobby = "여행";
hobby = "영화";
  • 예제
String hobby = "여행" 
hobby = null;

String kind1 = "자동차"
Srring kind2 = kind1; // stack 영역에 Kind1/2는 같은 heap 영역에 객체를 참조하고 있다.
kind1 = null;	// null을 부여함으로써 Kind1은 heap 영역에 참조를 끊었다.
  • kind1에 null을 대입한다고 해서 "자동차"에 해당하는 String 객체가 쓰레기 객체가 되지는 않는다. 
  • 그 이유는 kind2 변수가 여전히 참조되고 있기 때문이다.

 

5.5 문자열(String) 타입

String name;
name = "홍길동";
String hobby = "여행";
  • name 변수와 hobby 변수에 문자열 리터럴이 대입되면 문자열은 String 객체로 생성되고 객체의 번지가 각각 대입된다.

 

🏷 문자열 비교

  • 자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 설계되어 있다. 
  • 다음과 같이 name1과 name2 변수에 "홍길동" 을 대입할 경우, name1과 name2 변수에는 동일한 String 객체의 번지가 저장된다. 
name1 10 (번지)
name2 10 (번지)
  • String 변수에 문자열 리터럴을 대입하는 것이 일반적이지만, new 연산자로 직접 String 객체를 생성하고 대입할 수 있다. 
  • new 연산자
    • 새로운 객체를 만드는 연산자로, 객체 생성 연산자라고 한다. 
String name1 = new String("홍길동");
String name2 = new String("홍길동");
  • 이렇게 될 경우 name1과 name2는 각각 다른 객체의 번지를 가지게 된다. 
  • 따라서, 문자열의 비교는 문자열 리터럴로 생성하느냐, new 연산자로 생성하느냐에 따라 비교 연산자의 결과가 달라질 수 있다. 
String name1 = "홍길동";
String name2 = "홍길동"; // name1 과 name2는 동일한 객체 번지를 가지고 있다.
String name3 = new String("홍길동") // 객체 생성 연산자를 사용했으므로 name3은 다른 객체 번지를 가지고 있다.

name1 == name2 // 결과: true
name1 == name3 // 결과: false
  • == 연산자
    • 객체의 번지가 동일한지 판단
  • equals() 메소드
    • 객체의 번지가 동일하든 다르든 내부 문자열 리터럴만을 비교하여 판단

 

🏷 문자 추출

  • chartAt() 메소드
    • 문자열에서 특정 위치의 문자를 얻고 싶을 때 사용

 

🏷 문자열 길이

  • length() 메소드
    • 문자열에서 문자의 개수를 얻고 싶을 때 사용
    • 공백도 포함

 

🏷 문자열 대체

  • replace() 메소드
    • 문자열에서 특정 문자열을 다른 문자열로 대체하고 싶을 때 사용
    • 기존 문자열은 그대로 두고, 대체한 새로운 문자열을 리턴
    • String 객체의 문자열은 한번 생성되면 변경이 불가한 특성을 갖기 때문에 replace() 메소드로 인해 리턴하는 문자열은 원래 문자열의 수정본이 아니라 완전히 새로운 문자열이다. 
String oldStr = "자바 프로그래밍";
String newStr = oldStr.replace("자바", "JAVA");

 

🏷 문자열 잘라내기

  • substring() 메소드
    • 특정 위치의 문자열을 잘라내어 가져오고 싶을 때 사용
substring(int beginindex) beginindex에서 끝까지 잘라내기
substring(int beginindex, int endindex) beginindex에서 endindex 앞까지 잘라내기

 

🏷 문자열 찾기

  • indexOf() 메소드
    • 특정 문자열의 위치를 찾고자 할 때 사용
    • 문자열이 포함되어 있지 않으면 -1을 리턴한다. 
  • contains() 메소드
    • 특정 문자열이 포함되어 있는지 여부 사용

 

🏷 문자열 분리

  • split() 메소드
    • 구분자를 사용하여 여러개의 문자열로 구성되어 있을 경우, 이를 따로 분리하여 얻고 싶을 때 사용
    • split() 메소드를 호출하게 되면 분리된 문자열을 배열(array)을 얻을 수 있다.

 

 

5.6 배열(Array) 타입

 

🏷 배열의 특징

  • 배열은 같은 타입의 값만 관리한다.
    • int 배열은 int 타입의 값만 관리하고, String 배열은 문자열만 관리한다. 
  • 배열의 길이는 늘리거나 줄일 수 없다. 
    • 배열의 생성과 동시에 길이가 결정된다.

 

🏷 배열 변수 선언

// 배열의 선언 방식
// 첫번째 방식 - 보통 해당 방법을 많이 사용
int[] arr;
// 두번째 방식
int arr[];
// 값 목록으로 배열 생성
int[] arr = {0,1,2...}


// 주의할 점
int[] arr;
arr = {0,1,2...} // 컴파일 에러

// -> 올바른 방법
int[] arr;
arr = new int[] {0,1,2...}

// 메소드에 매개변수 사용 시
void printItem(int[] scores) {...}

printItem({0,1,2 ...}) // 컴파일 에러

// -> 올바른 방법
printItem(new int[] {0,1,2 ...});
  • 배열 변수는 참조 변수로, 배열도 객체이므로 힙 영역에 생성되고, 스택 영역에 있는 배열 변수는 힙 영역의 배열 주소를 참조한다.
  • 참조하는 배열이 없다면 배열 변수도 null로 초기화 할 수 있다. 

 

❌ 주의할 점

  • 배열 변수를 미리 선언한 후에는 값 목록을 변수에 대입할 수 없다.
  • 배열 변수를 선언한 시점과 값 목록이 대입되는 시점이 다르다면 new 타입[]을 중괄호 앞에 붙여주면 된다. 
  • 메소드의 매개변수가 배열 타입일 때에도 동일하게 적용

 

🏷 new 연산자로 배열 생성

  • 값의 목록은 없지만 향후 값들을 저장할 목적으로 배열을 미리 생성할 수 있다. 
    • 타입[] 변수 = new 타입[길이];
  • new 연산자는 해당 길이의 배열을 생성하고 배열의 번지를 리턴한다. 
  • 배열의 초기 값
    • 정수 배열은 0, 실수 배열을 0.0, 논리 배열은 false, 참조 배열은 null로 초기화 된다. 
      • ex) String 배열을 사용할 경우, null로 초기화 된다. 

 

🏷 배열 길이

  • 배열에 저장할 수 있는 항목 
  • 배열의 length 필드는 읽기만 가능하므로 배열의 길이는 변경할 수 없다.

 

5.7 다차원 배열

  • 배열 항목에는 또 다른 배열이 대입될 수 있다. 
  • 2차원 배열
int[][] scores = {
    {80, 90, 96},
    {76, 88}
}

// 2차원 배열의 길이를 다르게 주는 방법
// 1. 1차원 배열의 길이를 설정
int[][] scores = new int[2][];
// 2. 각각의 항목 값으로 길이가 다른 2차원 배열 생성하여 대입
scores[0] = new int[3];
scores[1] = new int[2];
  • 2차원 배열일 경우, 2차원 배열의 길이를 다르게 줄 수 있다. 

 

5.8 객체를 참조하는 배열

  • 기본 타입 배열은 각 항목에 값을 직접 지정하지만 참조 타입 배열은 각 항목에 객체의 번지를 저장한다. 
  • 스택 영역에 배열 변수가 저장되고 힙 영역에는 객체를 참조하는 배열이라, 값에 그 객체의 번지수가 들어가 있고, 
    번지수를 참조하는 String 객체가 있다. 
  • 앞에서 배웠듯이 ==, != 연산자를 활용하면 객체의 번지를 비교하여 그 객체가 같은 곳을 참조하고 있는지 확인 할 수 있고, 
    .equals() 를 사용하면 단순히 문자열만 비교하게 된다. 

 

5.9 배열 복사

  • 배열은 길이를 변경할 수 없기 때문에 더 많은 저장 공간이 필요하다면 더 큰 길이의 배열을 새로 만들고 이전 배열로 부터 값을 복사해야 한다. 

1️⃣ for 문을 이용한 배열 복사

  • for 문을 이용해서 항목을 하나씩 읽고 새로운 배열에 저장하는 것

2️⃣ System의 arraycopy() 메소드 이용

  • System.arraycopy(원본 배열, 원본 배열 복사 시작 인덱스, 새 배열, 새 배열 붙여넣기 시작 인덱스, 복사 항목 수)
  • arr1을 arr2로 모두 복사하려면
System.arraycopy(arr1,0,arr2,0,arr1.length)

 

5.11 main() 메소드의 String[] 매개변수 용도

  • 자바 프로그램을 실행하기 위해 지금까지 main() 메소드를 작성하였는데,
    여기에서 문자열 배열 형태인 String[] args 매개변수가 왜 필요한지 알아보자!
    • 요거는 다른 카테고리에서 자세히 정리해볼께요!

 

5.12 열거(Enum) 타입

  • 열거 타입은 관례적으로 알파벳으로 정의하고 모두 대문자로 작성한다. 
  • 여러 단어로 구성되어 있을 경우, 단어와 단어 사이에 언더바(_)로 연결하는 것이 관례이다.