5.1 데이터 타입 분류
자바의 데이터 타입은 크게 기본 타입과 참조 타입으로 분류된다. 참조 타입이란 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스 타입이 있다.

기본 타입으로 선언된 변수와 참조 타입으로 선언된 변수의 차이점은 저장되는 값이다. 기본 타입으로 선언된 변수는 값 자체를 저장하고 있지만, 참조 타입으로 선언된 변수는 객체가 생성된 메모리 번지를 저장한다.

// 기본 타입 변수
int age = 25;
double price = 100.5;
// 참조 타입 변수
String name = "신용권";
String hobby = "독서";
메모리 상에서 이 변수들이 갖는 값을 그림으로 표현하면 다음과 같다.

변수들은 모두 스택이라는 메모리 영역에 생성된다. 기본 타입 변수인 age와 price는 직접 값을 저장하고 있지만, 참조 타입 변수인 name과 hobby는 힙 메모리 영역의 String 객체 번지를 저장하고 이 번지를 통해 String 객체를 참조한다.
5.2 메모리 사용 영역
자바에서 사용되는 메모리 영역에 대해 알아보자. java 명령어로 JVM이 구동되면 JVM은 운영체제에서 할당받은 메모리 영역을 다음과 같이 구분해서 사용한다.

메소드 영역
메소드 영역은 바이트코드 파일을 읽은 내용이 저장되는 영역으로 클래스별로 상수, 정적 필드, 메소드 코드, 생성자 코드 등이 저장된다. 단순히 바이트코드 파일의 내용이 저장되는 영역이라고만 알고 있자.
힙 영역
힙 영역은 객체가 생성되는 영역이다. 객체의 번지는 메소드 영역과 스택 영역의 상수와 변수에서 참조할 수 있다.
스택 영역
스택 영역은 메소드를 호출할 때마다 생성되는 프레임이 저장되는 영역이다. 메소드 호출이 끝나면 프레임은 자동 제거된다. 프레임 내부에는 로컬 변수 스택이 있다. 여기에서 기본 타입 변수와 참조 타입 변수가 생성되고 저장된다.
5.3 참조 타입 변수의 ==, != 연산
==, != 연산자는 변수의 값이 같은지, 아닌지를 조사한다. 참조 타입 변수의 값은 객체의 번지이므로 참조 타입 변수의 --, != 연산자는 번지를 비교하는 것이 된다. 번지가 같다면 동일한 객체를 참조하는 것이고, 다르다면 다른 객체를 참조하는 것이다.

위 그림에서 refVar1과 refVar2는 서로 다른 객체를 참조하고 있다. refVar2와 refVar3는 동일한 객체를 참조하고 있음.
refVar1 == refVar2 // 결과 false
refVar1 != refVar2 // 결과 true
refVar2 == refVar3 // 결과 true
refVar2 != refVar3 // 결과 false
package ch05.sec03;
public class ReferenceVariableCompareExample {
public static void main(String[] args) {
int[] arr1; // 배열변수인 arr1 생성.
int[] arr2; // 배열변수인 arr2 생성.
int[] arr3; // 배열변수인 arr3 생성.
arr1 = new int[] {1,2,3};
arr2 = new int[] {1,2,3};
arr3 = arr2;
System.out.println(arr1 == arr2);
System.out.println(arr2 == arr3);
}
}

arr1과 arr2는 저장 항목은 같지만 서로 다른 배열 객체로 생성되므로 arr1과 arr2 변수에 대입되는 번지는 다르다.
arr2와 arr3는 같은 번지가 대입되었기 때문에 같은 배열을 참조한다.
5.4 null과 NullPointerException
참조 타입 변수는 아직 번지를 저장하고 있지 않다는 뜻으로 null(널) 값을 가질 수 있다. null도 초기값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성된다.
String refVar1 = "자바";
String refVar2 = null;

참조 타입 변수가 null 값을 가지는지 확인하려면 다음과 같이 ==, != 연산을 수행할 수 있다.
refVar1 == null // 결과: false
refVar1 != null // 결과: true
refVar2 == null // 결과: true
refVar2 != null // 결과: false
자바는 프로그램 실행 도중에 발생하는 오류를 예외(Exception)라고 부른다. 참조 변수를 사용하면서 가장 많이 발생하는 예외 중 하나는 NullPointerException이다. 변수가 null인 상태에서 객체의 데이터나 메소드를 사용하려 할 때 이 예외가 발생한다.
int[] intArray = null;
intArray[0] = 10; //NullPointerException
배열 변수 intArray에 null을 대입한 상태에서 배열 객체의 0 인덱스 항목에 10을 대입하는 코드(intArray[0] = 10)를 실행하면 NullPointerException이 발생한다. 이유는 intArray가 참조하는 배열 객체가 없으므로 10을 저장할 수 없기 때문임.
String str = null;
System.out.println("총 문자 수: " + str.length()); //NullPointerException
str 변수에 null을 대입한 상태에서 문자열의 길이를 얻기 위해 length() 메소드를 호출하면 NullPointerException이 발생한다. str 변수가 참조하는 String 객체가 없으므로 문자열의 길이를 구할 수 없기 때문이다.
package ch05.sec04;
public class NullPointerExceptionExample {
public static void main(String[] args) {
int [] intArray = null;
//intArray[0] = 10; NullPointerException 발생
String str = null;
//System.out.println(str.length()); NullPointerException 발생
}
}
앞으로 NullPointerException이 발생하면 예외가 발생된 곳에서 null인 상태의 참조 변수가 사용되고 있음을 알아야 한다.
이것을 해결하려면 참조 변수가 객체를 정확히 참조하도록 번지를 대입해야 한다.
경우에 따라서는 참조 타입 변수에 일부러 null을 대입하기도 한다.
-> 해당 참조 변수가 가지는 번지를 없애고 싶을 때
-> 안쓰는 객체는 메모리에서 지우고 효율적으로 사용하기 위해서 이렇게 썼음.
String hobby = "여행";
hobby = null;

어떤 변수에서도 객체를 참조하지 않으면 해당 객체는 프로그램에서 사용할 수 없는 객체가 된다.
즉 힙 메모리에는 있지만, 위치 정보를 모르기 때문에 사용할 수 없게 된다. 자바는 이러한 객체를 쓰레기로 취급하고, 쓰레기 수집기를 실행시켜 자동으로 제거한다.
String hobby = "여행";
hobby = "영화";

package ch05.sec04;
public class GarbageObjectExample {
public static void main(String[] args) {
String hobby = "여행";
hobby = null; // "여행"에 해당되는 String 객체를 쓰레기로 만듦
String kind1 = "자동차";
String kind2 = kind1;
kind1 = null; // "자동차"에 해당하는 String 객체는 쓰레기가 아님
System.out.println("kind2 : " + kind2);
}
}
5.5 문자열(String) 타입
자바의 문자열은 String 객체로 생성된다. name 변수와 hobby 변수에 문자열 리터럴이 대입되면 문자열은 String 객체로 생성되고, 객체의 번지가 각각 대입된다.
String name; //String 타입 변수 name 선언
name = "홍길동"; // name 변수에 문자열 대입
String hobby = "여행"; // String 타입 변수 hobby를 선언하고 문자열 대입

문자열 비교
자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 설계되어 있다. 다음과 같이 name1과 name2 변수에 "홍길동"을 대입할 경우, name1과 name2 변수에는 동일한 String 객체의 번지가 저장된다.
String name1 = "홍길동";
String name2 = "홍길동";

String 변수에 문자열 리터럴을 대입하는 것이 일반적이지만 new 연산자로 직접 String 객체를 생성하고 대입할 수도 있다.
new 연산자는 새로운 객체를 만드는 연산자로 객체 생성 연산자라고 한다.
String name1 = new String("홍길동");
String name2 = new String("홍길동");
이 경우 naem1과 name2 변수는 서로 다른 String 객체의 번지를 가지게 된다.

그렇기 때문에 문자열 리터럴로 생성하느냐 new 연산자로 생성하느냐에 따라 비교 연산자의 결과가 달라질 수 있다.
String name1 = "홍길동";
String name2 = "홍길동";
String name3 = new String("홍길동");
name1과 name2는 동일한 문자열 리터럴로 생성된 객체를 참조하기 때문에 name1 == name2의 결과는 true가 나오지만,
name3은 new 연산자로 String 객체를 별도로 생성했기 때문에 name1 == name3의 결과는 false가 나온다.
name1 == name2 //결과 true
name1 == name3 //결과 false
동일한 String 객체든 다른 String 객체든 상관없이 내부 문자열만을 비교할 경우에는 String 객체의 equals() 메소드를 사용한다.
boolean result = str1.equals(str2); // 문자열이 같은지 검사
boolean result = ! str1.equals(str2); // 문자열이 다른지 검사
package ch05.sec05;
public class EqualsExample {
public static void main(String[] args) {
String strVar1 = "홍길동";
String strVar2 = "홍길동";
if(strVar1 == strVar2) {
System.out.println("strVar1과 strVar2는 참조값이 같다.");
} else {
System.out.println("strVar1과 strVar2는 참조값이 다르다.");
}
if(strVar1.equals(strVar2)) {
System.out.println("strVar1과 strVar2는 문자열이 같다.");
}
String strVar3 = new String("홍길동");
String strVar4 = new String("홍길동");
if(strVar3 == strVar4) {
System.out.println("strVar3과 strVar4는 참조값이 같다.");
} else {
System.out.println("strVar3과 strVar4는 참조값이 다르다.");
}
if(strVar3.equals(strVar4)) {
System.out.println("strVar3과 strVar4는 문자열이 같다.");
}
}
}

문자 추출
문자열에서 특정 위치의 문자를 얻고 싶다면 charAt() 메소드를 이용할 수 있다. charAt() 메소드는 매개값으로 주어진 인덱스의 문자를 리턴한다. 여기서 인덱스란 0에서부터 '문자열의 길이 - 1' 까지의 번호를 말한다.
String subject = "자바 프로그래밍";
char charValue = subject.charAt(3);
"자바 프로그래밍" 이라는 문자열은 다음과 같이 인덱스를 매길 수 있다. 따라서 charAt(3)은 3번 인덱스 위치에 있는 문자.
즉 '프'가 해당된다.

package ch05.sec05;
public class CharAtExample {
public static void main(String[] args) {
String snn = "9506241230123";
char sex = snn.charAt(6);
switch (sex) {
case '1':
case '3':
System.out.println("남자입니다.");
break;
case '2':
case '4':
System.out.println("여자입니다.");
break;
}
}
}

문자열 길이
문자열에서 문자의 개수를 얻고 싶다면 length() 메소드를 사용한다.
String subject = "자바 프로그래밍";
int length = subject.length();
length 변수에는 8이 저장된다. subject 객체의 문자열 길이는 공백을 포함해서 8개이기 때문임.

package ch05.sec05;
public class LengthExample {
public static void main(String[] args) {
String ssn = "9506241230123";
int length = ssn.length();
if(length == 13) {
System.out.println("주민등록번호 자릿수가 맞습니다.");
} else {
System.out.println("주민등록번호 자릿수가 틀립니다.");
}
}
}

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

package ch05.sec05;
public class ReplaceExample {
public static void main(String[] args) {
String oldstr = "자바 문자열은 불변입니다. 자바 문자열은 String입니다.";
String newstr = oldstr.replace("자바", "JAVA");
System.out.println(oldstr);
System.out.println(newstr);
}
}

문자열 잘라내기
문자열에서 특정 위치의 문자열을 잘라내어 가져오고 싶다면 substring() 메소드를 사용한다.
| 메소드 | 설명 |
| substring(int beginindex) | beginindex에서 끝까지 잘라내기 |
| substring(int beginindex, int endindex) | beginindex에서 endindex 앞 까지 잘라내기 |
String ssn = "880815-1234567";
String first = ssn.substring(0,6);
String secondNum = ssn.substring(7);

따라서 firstNum 변수는 "880815" 문자열을 참조하고, secondNum 변수는 "1234567"을 참조한다.
package ch05.sec05;
public class SubStringExample {
public static void main(String[] args) {
String ssn = "880815-1234567";
String firstNum = ssn.substring(0, 6);
System.out.println(firstNum);
String secondNum = ssn.substring(7);
System.out.println(secondNum);
}
}

문자열 찾기
문자열에서 특정 문자열의 위치를 찾고자 할 때에는 indexOf() 메소드를 사용한다. indexOf() 메소드는 주어진 문자열이 시작되는 인덱스를 리턴한다.
String subject = "자바 프로그래밍";
int index = subject.indexOf("프로그래밍");
index 변수에는 3이 저장되는데, 다음과 같이 "자바 프로그래밍"에서 "프로그래밍" 문자열의 인덱스 위치가 3번이기 때문.

만약 주어진 문자열이 포함되어 있지 않으면 indexOf() 메소드는 -1을 리턴한다.
int index = subject.indexOf("프로그래밍");
if(index == -1) {
//포함되어 있지 않은 경우
} else {
//포함되어 있는 경우
}
주어진 문자열이 단순히 포함되어 있는지만 조사하고 싶다면 contains() 메소드를 사용하면 편리하다. 원하는 문자열이 포함되어 있으면 contains() 메소드는 true를 리턴하고, 그렇지 않으면 false를 리턴한다.
boolean result = subject.contains("프로그래밍");
package ch05.sec05;
public class IndexOfContainsExample {
public static void main(String[] args) {
String subject = "자바 프로그래밍";
int location = subject.indexOf("프로그래밍");
System.out.println(location);
String subString = subject.substring(location);
System.out.println(subString);
location = subject.indexOf("자바"); // indexof는 없으면 -1 출력함.
if(location >= 0) {
System.out.println("자바와 관련된 책이군요.");
} else {
System.out.println("자바와 관련 없는 책이군요.");
}
boolean result = subject.contains("자바"); // contains는 true or false를 출력.
if(result) {
System.out.println("자바와 관련된 책이군요.");
} else {
System.out.println("자바와 관련 없는 책이군요.");
}
}
}

문자열 분리
문자열이 구분자를 사용하여 여러 개의 문자열로 구성되어 있을 경우, 이를 따로 분리해서 얻고 싶다면 split() 메소드를 사용한다.
String board = "번호,제목,내용,성명";
String []arr = board.split(",");
board는 쉼표로 구분된 문자열을 가지고 있다. split() 메소드를 호출할 때 쉼표를 제공하면 분리된 문자열로 구성된 배열(array)를 얻을 수 있다.

package ch05.sec05;
public class SplitExample {
public static void main(String[] args) {
String board = "1,자바 학습,참조 타입 String을 학습합니다.,홍길동";
String[] token = board.split(",");
System.out.println("번호: " + token[0]);
System.out.println("제목: " + token[1]);
System.out.println("내용: " + token[2]);
System.out.println("성명: " + token[3]);
for(int i = 0; i<token.length; i++) {
System.out.println(token[i]);
}
}
}

5.6 배열(Array) 타입
변수는 하나의 값만 저장할 수 있다. 따라서 저장해야 할 값의 수가 많아지면 그만큼 많은 변수가 필요하다.
예를 들어 학생 30명의 성적을 저장하고 평균값을 구한다고 가정해보자. 먼저 학생 30명의 성적을 저장하기 위해서 변수 30개를 선언해야 한다. 만약 전교생들에 대한 성적을 처리한다면 수백 개의 변수 선언으로 인해 코드는 끔찍해진다.
따라서 많은 양의 값을 다루는 좀 더 효율적인 방법이 필요한데 이것이 바로 배열이다. 배열은 연속된 공간에 값을 나열시키고, 각 값에 인덱스를 부여해 놓은 자료구조이다. 앞의 예에서 학생들의 성적은 다음과 같이 score 배열로 생성할 수 있다.

인덱스는 대괄호 []와 함께 사용하여 각 항목의 값을 읽거나 저장하는데 사용된다. 예를 들어 score[0]은 83, score[1]은 90, score[2]는 87 값을 가진다. 이렇게 성적을 배열에 저장하면 평균 값은 배열의 인덱스를 이용해서 for문으로 쉽게 구할 수 있다.
int sum = 0;
for(int i=0; i<30; i++) {
sum += score[i];
}
int avg = sum / 30;
for 문이 30번 반복 실행하면서 i는 0~29까지 변한다. 따라서 sum 변수에는 score[0] ~ score[29] 까지의 값이 더해지고, 마지막으로 얻은 sum을 배열의 길이 30으로 나누면 평균 avg를 얻을 수 있다. 이렇게 하면 학생 수가 30명이 아니라 수백 명이 되어도 for문의 i<30만 변경하면 되므로 많은 양의 데이터를 적은 코드로 손쉽게 처리할 수 있다.
배열은 다음과 같은 특징을 가지고 있다.
| 배열은 같은 타입의 값만 관리한다. 배열의 길이는 늘리거나 줄일 수 없다. |
배열 변수 선언
배열을 사용하기 위해서는 우선 배열 변수를 선언해야 한다. 배열 변수 선언은 다음과 같이 두 가지 형태로 작성할 수 있지만, 관례적으로 첫 번째 방법을 주로 사용한다.

타입은 배열에 저장될 값의 타입을 말하는데, 다음은 타입별로 배열을 선언하는 방법이다.

배열 변수는 참조 변수이다. 배열도 객체이므로 힙 영역에 생성되고 배열 변수는 힙 영역의 배열 주소를 저장한다.
참조할 배열이 없다면 배열 변수도 null로 초기화할 수 있다.

만약 배열 변수가 null 값을 가진 상태에서 변수[인덱스]로 값을 읽거나 저장하게 되면 NullPointerException이 발생한다.
값 목록으로 배열 생성
배열에 저장될 값의 목록이 있다면, 다음과 같이 간단하게 배열을 생성할 수 있다.


중괄호 {}는 나열된 값들을 항목으로 가지는 배열을 힙에 생성하고, 번지를 리턴한다. 배열 변수는 리턴된 번지를 저장함으로써 참조가 이루어진다. 문자열을 갖는 배열은 다음과 같이 생성할 수 있다.

이렇게 생성된 배열에서 "Spring"은 season[0], "Fall"은 season[2]로 읽을 수 있다. season[1]의 "Summer"을 "여름"으로 바꾸고 싶다면 다음과 같이 대입 연산자를 사용하면 된다.

package ch05.sec06;
public class ArrayCreateByValueListExample {
public static void main(String[] args) {
String[] season = {"Spring", "Summer", "Fall", "Winter"};
System.out.println("season[0] : " + season[0]);
System.out.println("season[1] : " + season[1]);
System.out.println("season[2] : " + season[2]);
System.out.println("season[3] : " + season[3]);
season[1] = "여름";
System.out.println("season[1] : " + season[1]);
System.out.println();
int[] scores = {83, 90, 87};
int sum = 0;
for(int i = 0; i < scores.length; i++) {
sum += scores[i];
}
System.out.println(sum);
double avg = (double) sum/scores.length;
System.out.println(avg);
}
}

중괄호 {}로 감싼 값의 목록을 배열 변수에 대입할 때 주의할 점이 있다. 배열 변수를 미리 선언한 후에는 값 목록을 변수에 대입할 수 없다.

배열 변수를 선언한 시점과 값 목록이 대입되는 시점이 다르다면 다음과 같이 new 타입 []을 중괄호 앞에 붙여주면 된다. 타입은 배열 변수를 선언할 때 사용한 타입과 동일하게 주면 된다.

예를 들어 String 배열 변수 names를 선언한 후에 값 목록을 대입할 경우에는 new String[]을 중괄호 앞에 붙여줘야 한다.

메소드의 매개변수가 배열 타입일 경우에도 마찬가지다. 아래와 같이 매개변수로 int[] 배열 타입을 갖는 printItem() 메소드가 있다고 가정해보자. printItem() 메소드를 호출할 때 매개값으로 중괄호로 감싼 값 목록을 주면 컴파일 에러가 발생한다.

매개변수가 이미 선언되어 있고, 호출 시 값 목록을 제공하므로 다음과 같이 호출해야 한다.

package ch05.sec06;
public class ArrayCreateByValueListExample2 {
public static void main(String[] args) {
int [] scores;
scores = new int[] {83, 90, 87};
int sum1 = 0;
for(int i = 0; i < scores.length; i++) {
sum1 += scores[i];
}
System.out.println("총합 : " + sum1);
printItem(new int[] {83, 90, 87});
}
public static void printItem(int [] scores) {
for(int i = 0; i < scores.length; i++) {
System.out.println("score["+i+"]" + scores[i]);
}
}
}

new 연산자로 배열 생성
값의 목록은 없지만 향후 값들을 저장할 목적으로 배열을 미리 생성할 수도 있다. new 연산자를 다음과 같이 사용하면 배열 객체를 생성시킨다. 길이는 배열이 저장할 수 있는 항목수를 말한다.

new 연산자는 해당 길이의 배열을 생성하고 배열의 번지를 리턴하기 때문에 배열 변수에 대입할 수 있다. 이미 배열 변수가 선언된 후에도 다음과 같이 대입이 가능하다.

다음은 길이 5인 int[] 배열을 생성하고, 배열 번지를 intArray 변수에 대입한다.

new 연산자로 배열을 처음 생성하면 배열 항목은 기본값으로 초기화 된다.
int[] 배열을 다음과 같이 생성했다면 항목은 모두0으로 초기화 된다.

String 배열을 다음과 같이 생성했다면 항목은 모두 null로 초기화 된다.

배열을 생성하고 난 후 특정 인덱스 항목을 새로운 값으로 변경하는 방법은 동일하다.
package ch05.sec06;
public class ArrayCreateByNewExample {
public static void main(String[] args) {
int [] arr1 = new int[3];
for(int i =0; i < arr1.length; i++) {
System.out.println("arr1["+ i +"] : "+arr1[i]);
}
System.out.println();
arr1[0] = 10;
arr1[1] = 20;
arr1[2] = 30;
for(int i =0; i < arr1.length; i++) {
System.out.println("arr1["+ i +"] : "+arr1[i]);
}
System.out.println();
double [] arr2 = new double[3];
for(int i =0; i < arr1.length; i++) {
System.out.println("arr2["+ i +"] : "+arr2[i]);
}
System.out.println();
arr2[0] = 0.1;
arr2[1] = 0.2;
arr2[2] = 0.3;
for(int i =0; i < arr1.length; i++) {
System.out.println("arr2["+ i +"] : "+arr2[i]);
}
System.out.println();
String [] arr3 = new String[3];
for(int i =0; i < arr3.length; i++) {
System.out.println("arr3["+ i +"] : "+arr3[i]);
}
System.out.println();
arr3[0] = "1월";
arr3[1] = "2월";
arr3[2] = "3월";
for(int i =0; i < arr1.length; i++) {
System.out.println("arr3["+ i +"] : "+arr3[i]);
}
}
}


배열 길이
배열의 길이란 배열에 저장할 수 있는 항목 수를 말한다. 코드에서 배열의 길이를 얻으려면 도트(.) 연산자를 사용해서 참조하는 배열의 length 필드를 읽으면 된다.

배열의 length 필드는 읽기만 가능하므로 다음과 같이 값을 변경할 수는 없다.

배열 길이는 for문을 사용해서 전체 배열 항목을 반복할 때 많이 사용된다.
package ch05.sec06;
public class ArrayLengthExample {
public static void main(String[] args) {
int [] scores = {84, 90, 96};
int sum = 0;
for(int i = 0; i < scores.length; i++) {
sum += scores[i];
}
System.out.println("총합 : " + sum);
double avg = (double) sum / scores.length;
System.out.println("평균 : " + avg);
}
}

for 문의 조건식에서 < 연산자를 사용한 이유는 배열의 마지막 인덱스는 배열 길이보다 1이 적기 때문이다. 인덱스를 초과해서 사용하면 ArrayIndexOutOfBoundsException이 발생한다.
5.7 다차원 배열
5.8 객체를 참조하는 배열
기본 타입(byte, char, short, int, long, float, double, boolean) 배열은 각 항목에 값을 직접 저장하지만, 참조 타입(클래스, 인터페이스) 배열은 각 항목에 객체의 번지를 저장한다. 다음과 같이 String 타입의 배열을 생성하고, 각 항목에 문자열을 대입했다고 가정해보자.

strArray 변수와 String[] 배열을 그림으로 표현하면 다음과 같다. String[] 배열의 항목은 String 변수와 동일하게 참조 타입 변수로 취급된다.

==, !=연산자를 사용하면 배열 항목이 참조하는 객체가 같은 객체인지 다른 객체인지를 확인할 수 있고, 문자열만 비교할 때는 equals() 메소드를 사용한다.


리터럴 문자열이 같기 때문에 language[0]과 language[1] 항목은 동일한 번지에 저장된다. 하지만 language[2] 항목은 new 연산자로 생성된 String 객체가 대입되므로 다른 번지가 저장된다.
5.9 배열 복사
배열은 한 번 생성하면 길이를 변경할 수 없다. 더 많은 저장 공간이 필요하다면 더 큰 길이의 배열을 새로 만들고 이전 배열로부터 항목들을 복사해야 한다. 예를 들어 길이 3인 배열의 항목을 길이 5인 배열로 다음과 같이 복사할 수 있다.

가장 기본적인 복사 방법은 for 문을 이용해서 항목을 하나씩 읽고 새로운 배열에 저장하는 것이다.
package ch05.sec09;
public class ArrayCopyByForExample {
public static void main(String[] args) {
int [] oldIntArray = {1,2,3};
int [] newIntArray = new int[5];
for(int i = 0; i < oldIntArray.length; i++) {
newIntArray[i] = oldIntArray[i];
}
for(int i = 0; i < newIntArray.length; i++) {
System.out.println(newIntArray[i]);
}
}
}

배열 복사를 위한 좀 더 간단한 방법이 있다. System의 arraycopy() 메소드를 이용하면 한 줄만으로도 배열 복사를 할 수 있다.

원본 배열이 arr1이고 새 배열이 arr2일 경우 arr1의 모든 항목을 arr2에 복사하려면 이 메서드롤 호출하면 된다.

package ch05.sec09;
public class ArrayCopyExample {
public static void main(String[] args) {
String[] oldStrArray = {"java", "array", "copy"};
String[] newStrArray = new String[5];
System.arraycopy(oldStrArray, 0, newStrArray, 0, oldStrArray.length);
for(int i =0; i< newStrArray.length;i++) {
System.out.print(newStrArray[i]+ " ");
}
}
}

위 예제에서 newStrArray 변수가 참조하는 배열의 항목 초기값은 null이므로 복사되지 않은 3번 인덱스와 4번 인덱스 항목은 null을 유지하고 있다. 그리고 항목의 값이 String 객체의 번지이므로 번지 복사가 되어 참조하는 String 객체는 변함이 없다.

5.10 배열 항목 반복을 위한 향상된 for문
자바는 배열 및 컬렉션을 좀 더 쉽게 처리할 목적으로 다음과 같은 for문을 제공한다. 카운터 변수와 증감식을 사용하지 않고, 항목의 개수만큼 반복한 후 자동으로 for문을 빠져나간다.

package ch05.sec10;
public class AdvancedForExample {
public static void main(String[] args) {
int [] scores = {95, 71, 84, 93, 87};
int sum = 0;
for(int score : scores) {
sum += score;
}
System.out.println("점수 총합 : " + sum);
double avg = (double) sum/scores.length;
System.out.println("점수 평균 = " + avg);
}
}

5.11 main() 메소드의 String[] 매개변수 용도
5.12 열거(Enum) 타입
데이터 중에는 몇 가지로 한정된 값을 갖는 경우가 있다. 예를 들어 요일은 월,화,수,목,금,토,일 이라는 7개의 값을, 계절은 봄,여름,가을,겨울 이라는 4개의 값을 갖는다. 이와 같이 한정된 값을 갖는 타입을 열거(enumeration) 타입 이라고 한다.
열거 타입을 사용하기 위해서는 먼저 열거 타입 이름으로 소스 파일(.java)을 생성하고 한정된 값을 코드로 정의해야 한다. 열거 타입 이름은 첫 문자를 대문자로 하고 다음과 같이 캐멀 스타일로 지어주는 것이 관례이다.

요일 값은 7개이므로 다음과 같이 열거 상수 목록을 작성한다.
package ch05.sec12;
public enum Week {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
열거 상수는 열거 타입으로 사용할 수 있는 한정된 값을 말한다. 관례적으로 알파벳으로 정의하며, 모두 대문자로 작성한다. 만약 열거 상수가 여러 단어로 구성될 경우에는 다음과 같이 단어와 단어 사이를 언더바(_)로 연결하는 것이 관례이다.

열거 타입도 하나의 데이터 타입이므로 변수를 선언하고 사용해야 한다. 열거 타입 Week로 변수를 선언하면 다음과 같다.

열거 타입 변수에는 열거 상수를 대입할 수 있는데 '열거타입.열거상수'형태로 작성한다. Week 변수에 열거 상수인 SUNDAY를 대입하는 코드는 다음과 같다.

열거 타입은 참조 타입이므로 Week 변수에 다음과 같이 null도 대입할 수 있다.

열거 변수의 값이 특정 열거 상수인지 비교할 때는 ==와 != 연산자를 사용한다. Week 변수값이 SUNDAY인지 비교하는 코드는 다음과 같다.


