[Java] 이것이 자바다 Ch5. 참조 타입
데이터 타입에는 기본 타입, 참조 타입이 있습니다.
기본 타입은 정수타입, 실수타입, 논리 타입이 있습니다 참조타입은 배열타입, 열거 타입, 클래스, 인터페이스가 있습니다.

여기서 객체란, 데이터와 메소드로 구성된 덩어리라 생각하면 됩니다.
여기서 기본 타입과 참조 타입의 차이점은 저장되는 값입니다.
기본 타입 변수는 직접적인 값을 갖고 있지만, 참조 타입 변수는 번지를 갖고 있습니다.
String 타입은 직접 값을 갖고 있는 것처럼 보이지만, 실제로는 참조 타입입니다.

메모리 상에서 이 변수들이 갖는 값을 그림으로 표현하면 아래 그림과 같습니다.

변수들은 모두 스택이라는 메모리 영역에 생성됩니다. 기본 타입 변수인 age와 price는 직접 값을 저장하고 있지만, 참조 타입 변수인 name과 hobby는 힙 메모리 영역의 String 객체 번지를 저장하고 이 번지를 통해 String 객체를 참조합니다.
5.2 메모리 사용 영역

메소드 영역 - 자바 바이트 코드
힙 영역 - 객체들이 저장
스택 영역 - 변수들이 저장
5.3 참조 변수의 ==, != 연산

참조 타입의 ==, != 연산은 번지를 비교하게 됩니다. 실제 데이터를 비교하는 것이 아닙니다.
그래서 refVar1은 객체1을 참조하고, refVar2, refVar3은 객체2를 참조하고 있습니다.

그래서 위 그림과 같은 결과가 나오게 됩니다.
5.4 null과 NullPointerException
참조 변수에서 매우 중요한 용어입니다.
참조 변수는 번지를 아직 갖고 있지 않다는 뜻으로 null을 가질 수 있습니다. null도 초기 값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성됩니다.


자바는 프로그램 실행 도중에 발생하는 오류를 예외라고 부릅니다. 참조 변수를 사용하면서 가장 많이 발생하는 예외 중 하나는 NullPointerException 입니다. 이럴 경우엔 참조 변수가 객체를 참조하고 있지 않고 있다고 생각하면 됩니다.


위의 경우에 둘 다 NullPointerException이 발생합니다. 참조 변수가 객체 번지를 갖고 있지 않기 때문입니다.
만약
String str = "abc"
int len = str.length();
위와 같은 코드가 있다고 생각해봅니다.
변수 str은 스택영역에 생성되고, String은 참조 변수이기 때문에 힙영역에 생성되는 객체인 "abc"의 번지수가 str에 담기게 됩니다.
str.length() 에서 "."의 의미는 객체가 갖고 있는 length라는 method를 실행합니다.
앞으로 NullPointerException이 발생한다면 참조할 객체가 없이 사용을 했다는 것을 의미하므로, 이를 숙지하고
이 문제를 해결하기 위해서는 제대로 된 객체를 생성하여 대입해주면 됩니다.
어떤 변수에서도 객체를 참조하지 않는다면 해당 객체는 프로그램에서 사용할 수 없는 쓰레기 객체가 됩니다.
즉 힙 메모리에는 있지만 위치 정보를 모르기 때문에 사용할 수 없게 됩니다. 자바는 이러한 객체를 쓰레기로 취급하고 쓰레기 수집기 즉 Garbage Collector를 실행시켜 자동으로 제거하게 됩니다.
자바는 직접적으로 코드를 이용하여 객체를 제거하는 방법을 제공하진 않는데, 객체를 제거하는 유일한 방법은 객체의 모든 참조를 제거하는 것입니다. 의도적으로 변수에 null을 대입하게 된다면 쓰레기 객체로 만들게 되고, 자바의 쓰레기 수집기가 제거하게 됩니다.
또 다른 방법은
String hobby = "여행";
hobby = "영화";
이와 같은 방법으로 "여행"객체는 heap 영역 에서 쓰레기로 취급되며, Garbage Collector에 의해 삭제됩니다.
5.5 문자열(String) 타입

위와 같은 코드는 같은 객체를 참조하게 됩니다.

보통 자바에서는 위와 같은 형식으로 객체를 생성합니다.

이 경우에는 위와 같이 객체가 따로 생성이 되고, 번지 수도 달라지게 됩니다.
String name1 = "홍길동";
String name2 = "홍길동";
String name3 = new String("홍길동");
name1, name2는 리터럴로 생성된 객체를 참조하기 때문에 name1 == name2의 결과가 true가 됩니다.
name3는 new 연산자로 String 객체를 별도로 생성했기 때문에 name1 == name3의 결과는 false가 나옵니다.
만약 동일한 String 객체든 다른 String 객체든 상관없이 내부 문자열만 비교할 경우에는 String 객체의 equals() 메소드를 사용해야 합니다.

문자 추출
문자열에서 특정 위치의 문자를 얻고 싶다면 charAt() 메서드를 사용할 수 있습니다.
Stering subject = "자바 프로그래밍";
char charValue = "subject.charAt(3)";

위와 같은 상황에서는 "프"를 반환하게 됩니다.
문자열 대체
String oldStr = "자바 프로그래밍";
String newStr = oldStr.replace("자바", "JAVA");

Java에서의 문자열 객체가 만들어지면 바꿀 수 없습니다. 그래서 replace 메서드를 사용해도 원본이 바뀌는 것이 아닌, 새로운 객체를 힙 영역에 생성합니다.
문자열 잘라내기
String ssn = "8880815-1234567";
String firstNum = ssn.substring(0,6);
String secondNum = ssn.substring(7);

firstNum 변수는 "880815" 문자열을 참조하고, secondNum 변수는 "1234567" 를 참조합니다.
문자열찾기
문자열에서 특정 문자열의 위치를 찾고자 할 때에는 indexOf() 메소드를 사용합니다.
indexOf() 메소드는 주어진 문자열이 시작되는 인덱스를 리턴합니다.
String subject = "자바 프로그래밍";
int index = subject.indexOf("프로그래밍");

3번이 index변수에 저장되게 됩니다.
만약 찾고자 하는 문자열이 없다면 -1을 반환하게 됩니다.
주어진 문자열이 단순히 포함되어 있는지만 조사하고 싶다면 contains() 메소드를 사용하면 편리합니다. 원하는 문자열이 포함되어 있다면 contains() 메소드는 true를 반환하고, 그렇지 않으면 false를 반환합니다.
boolean result = subject.contains("프로그래밍");
문자열분리
문자열이 구분자를 사용하여 여러 개의 문자열로 구성되어 있을 경우, 이를 따로 분리해서 얻고 싶다면 split()메소드를 사용합니다.
String board = "번호,제목,내용,글쓴이";
String[] arr = board.split(",");
arr[0] = "번호";
arr[1] = "제목";
arr[2] = "내용";
arr[3] = "글쓴이";
5.6 배열 타입
변수는 한 개의 데이터만 저장할 수 있습니다. 따라서 저장해야 할 데이터의 수가 많아지면 그 만큼 많은 변수가 필요한데, 이는 비효율적인 방법입니다. 배열로 쉽게 진행할 수 있습니다.
int[] intArray;
double[] doubleArray;
String[] strArray;
int intArray[];
double doubleArray[];
String strArray[];
위 두 가지 방법으로 배열을 선언할 수 있습니다.
배열은 참조 변수에 속합니다. 배열도 객체이므로 힙 영역에 생성되고 배열 변수는 힙 영역의 배열 객체를 참조하게 됩니다. 참조할 배열 객체가 없다면 배열 변수는 null 값으로 초기화될 수 있습니다.
타입[] 변수 = null;
만약 배열 변수가 null 값을 가진 상태에서 변수[인덱스]로 값을 읽거나 저장하게 된다면 NullPointerException이 발생합니다. 반드시 참조하는 상태에서 값을 저장하거나 읽어야 합니다.
값 목록으로 배열 생성
데이터타입[] 변수 = { 값0, 값1, 값2, ... 값n };

String[] mames = {"신용권", "홍길동", "김자바"};
이렇게 생성된 배열에서 "신용권"은 names[0]에 저장되고 names[1]의 값을 "홍삼원"으로 변경하고 싶다면 다음과 같이 대입 연산자를 사용하면 됩니다.
names[1] = "홍삼원";
new 연산자로 배열 생성
값의 목록을 갖고 있지 않지만, 향후 값들을 저장할 배열을 미리 만들고 싶다면 new 연산자로 다음과 같이 배열 객체를 생성할 수 있습니다.
타입[] 변수 = new 타입[길이];
길이는 배열이 저장할 수 있는 값의 수를 말합니다. new 연산자로 배열을 생성할 경우에는 이미 배열 변수가 선언된 후에도 가능합니다.
열거 타입
데이터 중에는 몇 가지 한정된 값만을 갖는 경우가 있습니다. 흔히 예를 들어 요일에 대한 데이터, 계절에 대한 데이터가 그 예시입니다. 열거타입은 관례적으로 이름의 첫 문자를 대문자로 하고 나머지는 소문자로 구성합니다.
열거타입선언
열거타입 선언을 위해서는 먼저 열거 타입의 이름을 정하고 열거 타입 이름으로 소스 파일을 생성해야 합니다. 이름의 첫 문자는 대문자로, 나머지는 소문자로 구성합니다. 만약 여러 단어로 구성된 이름이라면 단어 첫 문자는 대문자로 하는 것이 관례입니다.
Week.java
MemberGrade.java
ProductKind.java
소스 파일의 내용으로는 다음과 같이 열거 타입 선언이 옵니다.
puiblic enum 키보드는 열거 타입을 선언하기 위한 키워드입니다. 반드시 소문자로 작성해야 합니다. 열거 타입 이름은 소스 파일명과 대소문자가 모두 일치해야 합니다.
public enum 열거타입이름 { ... }
열거타입을 선언했다면 이제는 열거 상수를 선언하면 됩니다. 열거 상수는 열거 타입의 값으로 사용되는데, 관례적으로 열거 상수는 모두 대문자로 작성합니다.
public enum Week { MONDY, TUESDAY, WEDNESDAY, .... }
만약 열거 상수가 여러 단어로 구성될 경우에는 단어 사이를 밑줄로 연결하는 것이 관례입니다.
public enum LoinResult { LOGIN_SUCCESS, LOGIN_FAILED }
열거 타입을 선언했다면 이제 열거 타입을 사용할 수 있습니다. 열거 타입도 하나의 데이터 타입이므로 변수를 선언하고 사용해야 합니다. 다음은 열거 타입 변수를 선언하는 방법입니다.
Week today;
Week reservationDay;
열거 타입 변수를 선언했다면 다음과 같이 열거 상수를 저장할 수 있습니다. 열거 상수는 단독으로 사용할 수는 없고 반드시 열거타입.열거상수로 사용됩니다.
Week today = Week.SUNDAY;
열거 타입 변수는 null 값을 저장할 수 있습니다. 열거 타입도 참조 타입이기 때문입니다.
참조 타입 변수는 객체를 참조하는 변수로 알고 있습니다. 그렇다면 열거 상수는 객체일까요?
그렇습니다. 열거 상수는 열거 객체로 생성됩니다.