자바의 데이터 타입은 크게 기본 타입(primitive)과 참조 타입(reference type)으로 분류된다.
참조 타입이란, 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스 타입이 있다.
객체란? 데이터와 메소드로 구성된 덩어리라고 생각하면된다.
객체 = 데이터(필드) + 메소드
기본 타입으로 선언된 변수와 참조 타입으로 선언된 변수의 차이점은 저장되는 값이다. 기본 타입으로 선언된 변수는 값 자체를 저장하고 있지만, 참초 타입으로 선언된 변수는 객체가 생성된 메모리 번지를 저장한다.
int age = 25;
String name = "홍길동"; //주소 값을 저장하고 있다.
String hobby = "도적질";
변수들은 모두 스택(Stack)이라는 메모리 영역에 생성된다. 기본 타입 변수인 age와 price는 직접 값을 저장하고 있지만, 참조 타입 변수인 name과 hobby는 힙 메모리 영역의 String 객체 번지를 저장하고 이 번지를 통해 String 객체를 참조한다.
java 명령어로 JVM이 구동되면 JVM은 운영체제에서 할당받은 메모리 영역을 다음과 같이 구분해서 사용한다.
메소드 영역은 바이트코드 파일을 읽은 내용이 저장되는 영역으로 클래스별로 상수, 정적필드, 메소드 코드, 생성자 코드 등이 저장된다. 단순히 바이트코드 파일의 내용이 저장되는 영역이라고만 일단 알고 있으면 된다.
힙 영역은 객체가 생성되는 영역. 객체의 번지는 메소드 영역과 스택 영역의 상수와 변수에서 참조할 수 있다.
스택영역은 메소드를 호출할 때마다 생성되는 프레임이 저장되는 영역이다. 메소드 호출이 끝나면 프레임은 자동 제거된다. 프레임 내부에는 로컬 변수 스택이 있다. 기본 타입 변수와 참조 타입 변수가 생성되고 제거된다.
==, != 연산자는 변수의 값이 같은지, 아닌지를 조사한다. 참조 타입 변수의 값은 객체의 번지이므로 참조 타입 변수의 ==, != 연산자는 번지를 비교하는 것이 된다. 번지가 같다면 동일한 객체를 참조하는 것이고, 다르다면 다른 객체를 참조하는 것이다.
참조 타입 변수는 아직 번지를 저장하고 있지 않다는 뜻으로 null 값을 가질수 있다. null도 초기값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성된다.
String refVar1 = "자바"; // 스택영역에서는 번지의 주소 값을 가지고 힙 영역에서는 50번지에 자바라는 값을 가지고 있다.
String refVar2 = null; // 아직 번지의 주소도 값이 없는 상태 하지만 스택 영역에는 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 객체로 생성된다. 다음은 두 개의 String변수 name과 hobby를 선언하고 문자열 리터럴을 대입한 것이다. name 변수와 hobby변수에 문자열 리터럴이 대입되면 문자열은 String 객체로 생성되고, 객체의 번지가 각각 대입된다.
String name; // String 타입 변수 name 선언
name = "홍길동"; // name변수에 문자열 대입
String hobby = "여행"; // String 타입 변수 hobby를 선언하고 문자열 대입
자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 설계되어 있다. name1과 name2 변수에 "홍길동"을 대입할 경우, name1,name2 변수에는 동일한 String 객체의 번지가 저장된다.
String name1 = "홍길동";
String name2 = "홍길동"; // 스택 영역에서 name1, name2는 같은 번지가 저장되어 있다.
String 변수에 문자열 리터럴을 대입하는 것이 일반적이지만, new 연산자로 직접 String 객체를 생성하고 대입할 수도 있다. new 연산자는 새로운 객체를 만드는 연산자로 객체 생성 연산자라고 한다.
String name1 = new String("홍길동"); //new 연산자의 경우에는 서로 다른 String 객체의 번지를 가지게 된다.
String name2 = new String("홍길동");
그렇기 때문에 문자열 리터럴로 생성하느냐 new 연산자로 생성하느냐에 따라 비교 연산자의 결과가 달라질 수 있다.
문자열에서 특정 위치의 문자를 얻고 싶다면 charAt() 메소드를 이용할 수 있다. charAt() 메소드는 매개값으로 주어진 인덱스의 문자를 리턴한다. 여기서 인덱스란 0에서부터 '문자열의 길이 -1'까지의 번호를 말한다.
String subject = "자바 프로그래밍";
char charValue = subject.charAt(3); // 프
문자열에서 문자의 개수를 얻고 싶다면 length()메소드를 사용하면 된다.
String subject = "자바 프로그래밍";
int length = subject.length(); // 8
변수는 하나의 값만 저장할 수 있다. 따라서 저장해야 할 값의 수가 많아지면 그만큼 많은 변수가 필요하다. 많은 양의 값을 다루는 좀 더 효울적인 방법이 배열이다. 배열은 연속된 공간에 값을 나열시키고, 각 값에 인덱스를 부여해 놓은 자료구조이다.
배열 항목에는 또 다른 배열이 대입될 수 있는데, 이러한 배열을 다차원 배열이라고 부른다.

배열 변수 5번지의 1차원 배열을 참조하고, 변수[0]은 다시 10번지 배열을 참조하고, 변수[1]은 30번지 배열을 참조한다. 위 그림처럼 다차원 배열은 1차원 배열을 서로 연결한 것이라고 볼 수 있다. 다차원 배열에서 각 차원의 항목에 접근하는 방법은 다음과 같다.
변수[1차원인덱스][2차원인덱스]..[N차원인덱스]
그림 값1, 값3 , 값 6을 읽는 방법은 다음과 같다
변수[0][0][0] //값1
변수[0][1][0] //값3
변수[1][0][1] //값6
자바 프로그램을 실행하기 위해 지금까지 main() 메소드를 작성했는데, 여기에서 문자열 배열 형태인 String[] args 매개변수가 왜 필요한지 알아보자
윈도우 명령 프롬프트나 맥OS의 터미널에서 프로그램을 실행할 때는 요구하는 값이 있을 수 있다. 예를 들어 두 수를 입력받고 덕셈을 수행하는 Sum 프로그램은 실행할 때 다음과 같이 두 수를 요구할 수 있다.
java Sum 10 20
공백으로 구분된 10과 20은 문자열로 취급되며 String[] 배열의 항목 값으로 구성된다. 그리고 main() 메소드 호출 시 매개값으로 전달된다.

main() 메소드 중괄호 {} 내에서 문자열 "10"과 "20"은 다음과 같이 얻을 수 있다.
String x = args[0];
String y = args[1];
문자열 "10"과 "20"을 int 타입으로 변환하려면 다음과 같이 강제 타입 변환을 한다.
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
Sum으로 실행할 때 몇 개의 값이 입력되었는지 확인하려면 main() 메소드에서 배열의 length필드를 읽으면 된다. 두 개의 값이 입력되지 않았다면 다음과 같이 출력 메세지를 보여줄 수도 있다.
if(args.length != 2) {
System.out.println("실행 시 두 개의 값이 필요합니다.");
}
만약 다음과 같이 값을 주지 않고 실행하면 args.length는 0이 된다.
java Sum
다음 예제는 Sum 실행 입력값이 2개가 아닐 경우 입력값이 부족함을 알리고 강제 종료한다. 그리고 2개의 값이 입력되었을 때만 덧셈의 결과를 출력한다.
package ch05.sec11;
public class mainStringArrayArgument{
public static void main(String[] args){
if(args.length != 2){
System.out.println("프로그램 입력값이 부족"); // 입력된 데이터 개수가 두 개가 아닐 경우
System.exit(0); //프로그램 강제 종료
}
String strNum1 = args[0]; // 첫 번째 데이터 얻기
String strNum2 = args[1]; // 두 번째 데이터 얻기
int num1 = Integer.parseInt(strNum1); //문자열을 정수로 변환
int num2 = Integer.parseInt(strNum2);
int result = num1 + num2;
System.out.println(num1 + " + " + num2 + " = " + result);
}
}
이클립스에서 바로 실행하면 프로그램 입력값이 부족이라고 출력된다. 실행 시 입력값을 주지 않았기 때문에 args는 길이 0인 String 배열을 참조한다. 따라서 5라인의 if 조건식이 true가 되어 if 블록이 실행된다. 이클립스에서 입력값을 주고 실행하려면 다음 순서대로 진행하면 된다.
이클립스 상단 메뉴에서 [Run] - [Run Configurations]을 선택하면 나오는 대화상자에서 Project 입력란에 'thisisjava', Main class 입력란에 'ch05.sec11.MainStringArrayArgument`로 되어 있는지 확인한다.
[Argument] 탭을 클릭하고 Program arguments 입력란에 10과 20을 입력한다. 그리고 [Run] 버튼을 클릭한다.
명령 프롬프트나 터미널에서 입력값 주기
윈도우의 명령 프롬프트나 맥OS의 터미널에서 입력값을 주고 실행하고 싶다면 cd 명령을 사용해서thisisjava_second_edtion디렉토리 밑에 있는 bin 폴더까지 이동 후 다음과 같이 java 명령어를 실행하면 된다.
java ch05.sec11.MainStringArrayArgument 10 20
데이터 중에는 몇 가지로 한정된 값을 갖는 경우가 있다. 예를 들어 요일은 월, 화, 수, 목, 금, 토, 일 이라는 7개의 값을, 계절은 봄, 여름, 가을, 겨울이라는 4개의 값을 갖는다. 이와 같이 한정된 값을 갖는 타입을 열거 타입이라고한다.
열거 타입을 사용하기 위해서는 먼저 열거 타입 이름으로 소스(.java)을 생성하고 한정된 값을 코드로 정의해야 한다. 열거 타입 이름은 첫 문자를 대문자로 하고 다음과 같이 캐멀 스타일로 지어주는 것이 관례이다.
WEEK.java
MemberGrade.java
ProductKind.java
요일을 저장할 수 있는 열거 타입인 Week를 이클립스에서 생성해보자
package ch05.sec12;
public enum Week{ //열거 타입 이름
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY // 열거 상수 목록( 한정된 값 목록)
}
열거 상수는 열거 타입으로 사용할 수 있는 한정된 값을 말한다. 관례적으로 알파벳으로 정의하며, 모두 대문자로 작성한다. 만약 열거 상수가 여러 단어로 구성될 경우에는 다음과 같이 단어와 단어 사이에 언더바_로 연결하는것이 관례이다.
public enum LoginResult{
LOGIN_SUCCESS,
LOGIN_FAILED
}
열거 타입도 하나의 데이터 타입이므로 변수를 선언하고 사용해야 한다. 열거 타입 Week로 변수를 선언하면 다음과 같다.
Week today;
Week reservationDay;
열거 타입 변수에는 열거 상수를 대입할 수 있는데, '열거타입, 열거상수' 형태로 작성한다. Week 변수에 열거 상수인 SUNDAY를 대입하는 코드는 다음과 같다.
Week today = Week.SUNDAY;
열거 타입은 참조 타입이므로 Week 변수에 다음과 같이 null도 대입할 수 있다.
Week birhday = null;
열거 변수의 값이 특정 열거 상수인지 비교할 때는 ==와 !=연산자를 사용한다. Week 변수값이 SUNDAY인지 비교하는 코드는 다음과 같다.
Week today = Week.SUNDAY;
today == Week.SUNDAY; //결과 : true