1. 데이터 타입 분류
- 기본 타입 (원시타입 : primitive type) : 정수, 실수, 문자, 논리 리터럴
- 참조 타입 (reference type) : 객체의 번지를 참조하는 타입 (배열, 열거, 클래스, 인터페이스)
- 변수는 스택 영역에 생성되고 객체는 힙 영역에 생성됨
2. 메모리 사용 영역
JVM으로 바이트 코드 파일을 실행하면 JVM은 운영체제에서 할당받은 메모리 영역 (Runtime Data Area)을 메소드 영역, 힙 영역, JVM 스택 영역으로 구분해서 사용함
메소드 영역
- 클래스들을 클래스 로더로 읽어 클래스별로 런타임 상수풀 (runtime constant pool), 필드 데이터, 메소드 데이터, 메소드 코드, 생성자 코드 등을 분류해서 저장
- JVM 실행 시 생성되고 모든 스레드가 공유 가능
힙 영역
- 객체와 배열이 생성되는 영역
- 객체와 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조함
- 참조하는 변수나 필드가 없다면 JVM은 쓰레기 수집기 (Garbage Collector)를 실행시켜 자동으로 객체를 제거
JVM 스택 영역
- 각 스레드마다 하나씩 존재 / 스레드가 시작될 때 할당 / 기본적으로 main 스레드 하나는 존재
- 메소드를 호출할 때마다 프레임을 추가 (push)하고 메소드가 종료되면 해당 프레임을 제거 (pop)
- 프레임 내부의 로컬 변수 스택에 기본 타입 변수나 참조 타입 변수가 추가 (push) 되거나 제거 (pop)됨
- 추가되는 시점은 변수가 초기화 될 때
- 변수는 선언된 블록 안에서만 스택에 존재하고 블록을 벗어나면 스택에서 제거됨
3. 참조 변수의 동등 비교 (==, !=)
- 참조 타입 변수들 간의 ==, != 연산은 동일한 객체를 참조하는지 판단 = 주소 값 비교 = 동일 객체 참조
4. null, NullPointerException
null
- 참조 타입 변수는 힙 영역의 객체를 참조하지 않는다는 뜻으로 null 값을 가질 수 있음
- 초기화로도 사용 가능 = 스택 영역에 변수 생성
NullPointerException
- 참조 타입 변수를 잘못 사용하면 발생되는 예외
- null 값을 가진 참조 타입 변수를 사용하면 발생
5. String 타입
- String 변수에 문자열을 저장하려면 큰 따옴표로 감싼 문자열 리터럴을 대입
- 문자열 리터럴이 동일하다면 String 객체를 공유함
- 참조 타입이므로 초기값으로 null 대입 가능
- new 연산자 (객체 생성 연산자) : 힙 영역에 새로운 객체를 만들 때 사용
- equals() 메소드 : 동일 객체와는 상관 없이 문자열만을 비교할 때 사용 / 원본 문자열과 매개값으로 주어진 비교 문자열이 동일한지 비교 한 후 true 또는 false 리턴
String str1 = "오즈";
String str2 = "오즈";
String str3 = new String("오즈");
str1 == str2 // true
str2 == str3 // false
boolean result = str1.equals(str3); // true
6. 배열 타입
배열
- 같은 타입의 많은 양의 데이터를 저장하고 각 데이터에 인덱스를 부여해 놓은 자료구조
- 선언과 동시에 저장할 수 있는 데이터 타입이 결정됨
- 한 번 생성된 배열은 길이를 늘리거나 줄일 수 없음
선언
타입[] 변수;
타입 변수[];
- 위 처럼 두 방식으로 선언 가능
- 대괄호 []는 배열 변수를 선언하는 기호로 사용
- 참조 변수 / null 값으로 초기화 가능
- 배열 변수는 배열 객체를 참조하는 상태에서 값을 저장하거나 읽어야 함 / 아닐시 NullPointerException 예외 발생
값 목록으로 배열 생성
String[] names = {"김오즈", "이오즈", "정오즈"};
String[] names = null;
names = {"김오즈", "이오즈", "정오즈"}; // 컴파일 에러
names = new String[] {"김오즈", "이오즈", "정오즈"};
int add(int[] scores) { ... }
int result = add({95, 85, 90}); // 컴파일 에러
int result = add(new int[] {95, 85, 90});
- 중괄호 {}는 주어진 값들을 항목으로 가지는 배열 객체를 힙에 생성하고, 배열 객체의 번지를 리턴
- 배열 변수를 이미 선언한 후에 다른 실행문에서 중괄호를 사용한 배열 생성 불가능
- 배열 변수를 미리 선언한 후, 나중에 초기화해야 하는 상황에는 new 연산자를 사용해서 값 목록을 지정
- 메소드의 매개값이 배열일 경우, 배열을 생성함과 동시에 매개값으로 사용하고자 할 때는 new 연산자 사용
new 연산자로 배열 생성
- new 연산자로 향후 값들을 저장할 배열을 미리 생성 가능
타입[] 변수 = new 타입[길이];
타입[] 변수 = null;
변수 = new 타입[길이];
- 길이는 배열이 저장할 수 있는 값의 수
- new 연산자로 배열 생성하는 경우에는 배열 변수 선언 후에도 가능
| 분류 | 데이터 타입 | 초기값 |
| 기본 타입 (정수) | byte[] char[] short[] int[] long[] |
0 '\u0000' 0 0 0L |
| 기본 타입 (실수) | float[] double[] |
0.0F 0.0 |
| 기본 타입 (논리) | boolean[] | false |
| 참조 타입 | 클래스[] | null |
| 인터페이스[] | null |
- new 연산자로 배열 처음 생성할 경우, 배열은 자동적으로 기본값으로 초기화 됨
변수[인덱스] = 값;
- 배열이 생성되고 나서 새로운 값을 저장하려면 대입 연산자 사용
배열 길이
- 배열의 길이 : 배열에 저장할 수 있는 전체 항목 수
public class Example {
public static void main (String[] args) {
int[] scores = {83, 90, 87};
int sum = 0;
for (int i = 0; i < scores.length; i++) {
sum += scores[i];
}
System.out.println("총합 : " + sum);
}
}
- length 필드는 읽기 전용 필드이기 때문에 값 변경 불가능
- 배열의 인덱스를 초과하게 되면 ArrayIndexOutOfBoundsException 예외가 발생
커맨드 라인 입력
public static void main(String[] args) { ... }
- 프로그램을 실행하면 String 배열 args를 먼저 생성하고 main() 메소드 호출시 매개값으로 전달
다차원 배열
int[][] arr1 = new int[2][3];
arr1.length // 2
arr1[0].length // 3
arr1[1].length // 3
int[][] arr2 = new int[2][];
arr2[0] = new int[2];
arr2[1] = new int[3];
arr2.length // 2
arr2[0].length // 2
arr2[1].length // 3
int[][] arr3 = {{10, 30}, {70, 90}};
<배열 생성 결과>
| arr1 | ||
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| arr2 | ||
| 0 | 0 | - |
| 0 | 0 | 0 |
| arr3 | |
| 10 | 30 |
| 70 | 90 |
- int[][] arr1 = new int[2][3]; 코드는 세 개의 배열 객체를 생성하는데, 배열 변수인 arr1은 길이가 2인 배열 객체를 참조하고 해당 배열 객체의 arr1[0]과 arr1[1]은 다시 길이가 3인 배열 객체를 각각 참조함
- 자바는 일차원 배열이 서로 연결된 구조로 다차원 배열을 구현하기 때문에 수학 행렬 구조가 아닌 계단식 구조를 가질 수 있음
- 배열 사용시 배열의 길이를 정확히 알고 사용해야 함
객체를 참조하는 배열
String[] strArray = new String[3];
strArray[0] = "Java";
strArray[1] = "C++";
strArray[2] = "Python";
- 참조 타입 배열은 각 항목에 객체의 번지를 가지고 있음
- 상기한 코드는 1개의 배열 객체와 3개의 String 객체를 생성함
배열 복사
배열은 한 번 생성하면 크기를 변경할 수 없기 때문에 더 많은 저장 공간이 필요하다면 큰 배열을 새로 만들고 이전 배열의 항목 값들을 복사해야 함
얕은 복사 : 참조 타입 배열의 복사시, 새 배열의 항목이 이전 배열의 항목이 참조하는 객체와 동일
깊은 복사 : 참조하는 객체도 별도로 생성하는 것
for문을 사용한 복사
public class Example {
public static void main(String[] args) {
int[] oldArr = {1, 2, 3};
int[] newArr = new int[5];
for (int i = 0; i < oldArr.length; i++) {
newArr[i] = oldArr[i];
}
for (int i = 0; i < newArr.length; i++) {
System.out.print(newArr[i] + ", "); // 1, 2, 3, 0, 0,
}
}
}
- 복사되지 않은 항목은 기본 초기값으로 채워짐
System.arraycopy() 메소드를 사용한 복사
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
- src : 원본 배열 / srcPos : 원본 배열에서 복사할 항목의 시작 인덱스 / dest : 새 배열 / destPos : 새 배열에서 붙여넣을 시작 인덱스 / length : 복사 할 개수
향상된 for문
for (타입 변수 : 배열) {
실행문;
}
- 반복 실행을 위한 카운터 변수와 증감식을 사용하지 않음
- 배열 및 컬렉션 항목의 개수만큼 반복하고 자동적으로 for문을 빠져나옴
- 실행 순서 : 배열에서 가져올 값이 존재하는 지 평가 > 값이 존재한다면 해당 값을 변수에 저장 > 실행문 실행 > 배열에서 가져올 다음 값이 존재하는 지 평가
7. 열거 타입 (enumeration type)
한정된 값만을 갖는 데이터 타입
몇 개의 열거 상수 (enumeration constant) 중에서 하나의 상수를 저장하는 데이터 타입
선언
- 순서 : 열거 타입 이름 정하기 > 열거 타입 이름으로 소스 파일 생성 (관례 : 첫 문자를 대문자, 나머지 소문자 / 여러 단어로 구성된다면 각 단어의 첫 문자는 대문자)
public enum Week {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}
- public enum : 열거 타입을 선언하기 위한 키워드
- 열거 타입 이름은 소스 파일명과 대소문자가 모두 일치 해야됨
- 관례 : 열거 상수는 모두 대문자 / 여러 단어로 구성된다면 언더바 (_)로 연결
열거 타입 변수
Week today = Week.SUNDAY;
- 열거 타입 변수에 열거 상수 저장 가능
- 열거 타입도 참조 타입이기 때문에 null 값 저장 가능 / 열거 상수는 열거 객체로 생성됨 (상기 코드는 7개의 Week 객체로 생성됨)
열거 객체의 메소드
- 모든 열거 타입은 컴파일 시 Enum 클래스를 상속하기 때문에 java.lang.Enum 클래스에 선언된 메소드를 사용 가능
- name() 메소드 : 열거 객체가 가지고 있는 문자열을 리턴
- ordinal() 메소드 : 0부터 시작해서 열거 객체들이 몇 번째 열거 객체인지 리턴
- compareTO() 메소드 : 매개값으로 주어진 열거 객체를 기준으로 해당 열거 객체가 몇 번째에 위치하는지를 비교
- valueOf() 메소드 : 매개값으로 주어지는 문자열과 동일한 문자열을 가지는 열거 객체를 리턴 / 외부로부터 문자열을 입력받아 열거 객체로 변환할 때 유용하게 사용
- values() 메소드 : 열거 타입의 모든 열거 객체들을 배열로 만들어 리턴
public class Example {
public static void main(String[] args) {
// name() 메소드
Week today = Week.SUNDAY;
String name = today.name();
System.out.println(name); // SUNDAY
// ordinal() 메소드
int ordinal = today.ordinal();
System.out.println(ordinal); // 6
// compareTo() 메소드
Week day1 = Week.MONDAY;
Week day2 = Week.WEDNESDAY;
int result1 = day1.compareTo(day2);
int result2 = day2.compareTo(day1);
System.out.println(result1); // -2
System.out.println(result2); // 2
// valueOf() 메소드
if (args.length == 1) {
Week weekDay = Week.valueOf(name);
if (weekDay == Week.SATURDAY || weekDay == Week.SUNDAY) {
System.out.println("주말");
} else {
System.out.println("평일");
}
}
// values() 메소드
Week[] days = Week.values();
for (Week day : days) {
System.out.println(day);
}
}
}