자바의 데이터 타입에는 크게 두가지가 존재한다.
기본 타입
(primitive)
기본 타입
은 여태까지 보아왔던 (정수, 실수, 문자, 논리 리터럴)을 저장한다.
참조 타입
(referecne)
참조 타입
은 객체의 번지를 참조하는 타입으로 배열
, 열거
, 클래스
, 인터페이스
타입을 말한다.
기본 타입
과 참조 타입
은 변수가 어떤 값을 저장하느냐? 에서 차이가 있다.
기본 타입
은 실제 값을 변수에 저장하지만,
참조 타입
은 메모리의 번지를 값으로 갖는다.
age와 price는 기본 타입
이므로 직접 값을 저장하지만, String 클래스 변수인 name, hobby는 힙
영역의 객체 주소값을 가르키고 있다.
여기서 String 클래스 변수를 참조 타입 변수
라고 한다.
시작하기전, JVM은 OS에서 할당 받은 메모리를 세 영역으로 구분한다.
메소드 영역(Method Area), 힙 영역(Heap Area), JVM 스택 영역 (Stack Area)
Q. 메소드 영역을 모든 스레드가 공유한다는 것이 어떤 의미를 가질까?
(다시한번) 기본타입 변수는 값을 저장하는 스택 영역에 있지만, 참조 변수는 값이 아니라 힙/메소드 영역의 객체 주소를 가진다.
3장 에서 언급하였듯이, 참조 타입 변수끼리 비교를 한다는 것은 주소값
을 비교하는 것이다.
따라서 동일 객체를 참조하고 있을 경우에는 true
, 아니라면 false
를 반환한다.
==
,!=
연산을 사용하면 된다.str
변수가 참조하는 String 객체가 없기에 NPE가 발생한다. (== null.length())객체
로 생성되고, 변수는 String 객체를 참조
한다.==
연산을 한다면 주소값을 비교하기에 s1==s2
는 true
이고, s1==s3
는 false
이다. String s1 = "우이천";
String s2 = "우이천";
String s3 = new String("우이천");
변수를 1억개 저장해야 한다면 1억개의 변수를 생성해야 할까? 아니다.
같은 타입의 많은 양의 데이터를 다루는 프로그램에서는 좀 더 효율적인 방법을 고안했는데 이것이 배열
이다.
배열은 같은 타입의 데이터를 연속된 공간에 나열시키고, 각 데이터에 인덱스(index)를 부여해 놓은 자료구조
이다.
즉, "N번째에 X값을 저장해놓겠어요"하는 것이다.
변수를 1억개 생성하는 것 뿐만 아니라 1억번 다룬다고 할때에도 유용하다.(반복문)
단, 배열은 같은 타입의 데이터만 저장할 수 있다.
만약 다른 타입 값을 저장하려 하면 타입 불일치 컴파일 오류가 발생한다.
또한, 한번 생성된 배열을 늘리거나, 줄일 수 없다.
배열 선언하는 방법은 아래와 같이 두가지 방법이 있다.
1. 타입[] 변수;
2. 타입 변수[];
강사분께서 따로 설명이 없으신데 JAVA에선 둘다 사용이 되는 갑다.
(굳이 왜)그렇지만 대부분 1을 선호하시는 것 같고, 2는 C/C++에서 자주 봤다.
배열 변수는 참조 변수에 속한다. 배열도 객체이므로 힙에 생성된다.
만약 배열 변수가 null 값을 가진 상태에서 변수[인덱스]로 값을 접근하거나 저장하게 되면 NPE가 발생한다.
이렇게 생성된 배열에서 1번째 값을 바꾸고 싶다면 아래와 같이 바꿀 수 있다.
String[] var={"우", "이", "천"};
var[1] = "우이천";
위의처럼 값의 목록으로 배열 객체를 생성할 때 주의할 점이 있는데, 이미 선언한 후에 다른 실행문에서 중괄호를 사용한 배열 생성은 허용되지 않는다.
메소드의 매개값이 배열일 경우에도 마찬가지다.
아래와 같이 값 목록을 생성함과 동시에 new 연산자를 사용해야 한다.
/* method */
int add(int[] scores) {...}
int result1 = add({1,2,3}); //error
int result2 = add(new int[]{1,2,3});
값의 목록이 없지만, 향후 값들을 저장할 배열을 미리 만들고 싶다면 new 연산자로 배열 객체를 생성할 수 있다.
new 연산자로 배열을 처음 생성할 경우, 배열은 자동적으로 기본값으로 초기화 된다.
(세줄요약)
필드변수
length가 있어 .
으로 접근하면 된다. 반복문
(for)돌릴 때 유용하다. 배열변수.length;
배열변수.length = 10 //error
psvm
main 메소드의 매개값 String[] args가 왜 필요한지 이제 이해 할 수 있다.
즉, java *.class 문자열0 문자열1 ... 을 한다면 문자열 n개를 main 메소드의 매개변수로 전달한다는 것이다.
1차원 배열은 1행 N열로 이루어져 있다.
2차원 배열은 N'행 M열로 이루어져 있다.
ex) 2행 3열 배열
int[][] scores = new int[2][3]
C/C++를 사용하다보면, 먼저 2차원 배열을 동적으로 만드는 순서는 아래와 같다.
1. 1차원 배열 포인터를 잡는 2차원 포인터 (void** ptr)
를 할당한다.
2. 그 이후 반복문을 통하여 1차원 배열들을 할당한다.
3. 2차원 포인터가 1차원 포인터들의 첫번째 주소값을 할당한다.
(매우 귀찮다. 코드 라인도 늘고 stl 쓴다.)
위 과정과 매우 비슷하다.
그러므로 score.lengh
는 2(2행이므로), score[idx].length
는 3(3열이므로) 가 될 것이다.
물론 각 행마다 열의 수를 다르게 일일이 설정해 줄 수도 있다.
- 주의점 : 배열의 길이를 정확히 알고 인덱스를 사용하지 않는다면,
ArrayIndexOutOfBoundsException
을 내뿜는다.
또한 아래와 같이 배열을 선언할 수도 있다.
int[][] scores = {{95,80},{95, 96}};
이렇게 선언하면, scores
는 2행 2열을 갖는 2차원 배열이고, scores[idx]
는 2열을 갖는 1차원 배열이 될 것이다.
기본 타입(byte
, char
, ...) 배열은 각 항목에 직접 값을 갖지만, 참조 타입(클래스
, 인터페이스
) 배열은 각 항목에 객체의 번지를 가지고 있다. 즉, 객체를 참조하게 된다.
String[] strArray = new String[3];
strArray[0] = "Java";
strArray[1] = "C++";
strArray[2] = "C#";
배열은 한 번 생성하면 크기를 변경할 수 없기 때문에 더 많은 저장공간이 필요하다면, 보다 큰 배열을 새로 만들고 이전 배열로부터 항목 값들을 복사해야 한다.
for
문이나, System.arraycopy()
를 사용하면 된다. (다른 좋은 API 많을 것 같아서 생략)
기존 사용하던 for
문
for(int i=0; i<scores.size(); i++){
sum+=scores[i]
}
향상된 for
문
for(int score : scores){
sum+=score;
가독성도 좋아지는 것 같다. enhanced for문 짱이다 짱!
다만 인덱스 접근이 불가능 하므로, 순회에 사용하기 적절하다.
데이터 중 몇 가지로 한정된 값만을 갖는 경우가 흔히 있다. (private static final int)
ex) 요일(월, 화 수, ...), 계절(봄, 여름, .... )
이와 같이 한정된 값만을 갖는 데이터 타입이 열거 타입(enumeration type)이다.
열거 타입은 몇개의 열거 상수(enumeration constant) 중에서 하나의 상수를 저장하는 데이터 타입이다.
관례적으로 열거 타입의 이름은 파스칼 케이스를 사용한다. (모든 자바 클래스들이 그럴껄요..?)
열거 상수들은 모두 대문자를 사용하고, 여러 단어가 사용된다면 _
(밑줄)을 사용한다.
Week
형식의 변수를 아래와 같이 선언하고, 또 할당할 수 있다.
Week today;
Week tmrw = Week.sunday;
Week ttmrw = null;
열거 타입 변수는 null 값을 저장할 수 있는데, 열거 타입도 참조 타입이기 때문이다.
그렇다면 다음 실행 결과는 어떻게 될까?
Week today = Week.SUNDAY;
today == Week.SUNDAY //true
today
변수는 stack
에 생성되고, 그리고 heap
영역에 Week
객체들이 저장되기에 결과는 true
가 된다.
열거 타입은 Enum
클래스를 상속받기에, 다음과 같은 메소드를 갖는다.