참조 타입
데이터 타입 분류
- 기본 타입
- 정수 타입
- 실수 타입
- 논리 타입
- 참조 타입
- 배열 타입
- 열거 타입
- 클래스
- 인터페이스
- 기본 타입과 참조 타입의 차이점: 저장되는 값이 무엇인지
- 기본 타입: 실제 값을 변수 안에 저장
- 참조 타입: 배열, 열거, 클래스, 인터페이스를 이용해서 선언된 변수는 메모리의 번지를 값으로 갖는다.
- 변수는 스택 영역에 생성되고 객체는 힙 영역에 생성된다. 스택 영역에 있는 참조 타입의 변수는 힙 영역의 실제 객체를 가리킨다.
메모리 사용 영역
- JVM은 운영체제엇 할당 받은 메모리 영역(Runtime Data Area)을 메소드 영역, 힙 영역, 스택 영역으로 구분해서 사용한다.
Method Area
- 코드에서 사용되는 클래스(~.class)들을 클래스 로더로 읽어 클래스 별로 런타임 상수풀(runtime constant pool), 필드(field) 데이터, 메소드(method) 데이터, 메소드 코드, 생성자(constructor)코드 등을 분류해서 저장한다.
- 메소드 영역은 JVM이 시작될 때 생성되고 모든 스레드가 공유하는 영역이다.
- 메소드 코드, 상수, 열거 상수는 정적(메소드) 영역에 생성된다.
Heap
- 힙(Heap) 영역: 객체와 배열이 생성되는 영역이다. 힙 영역에 생성된 객체와 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조한다.
- Garbage: 참조하는 변수가 필드가 없다면 의미 없는 객체가 된다.
- Garbage Collector: JVM은 GC를 동작시켜 가비지를 힙 영역에서 자동으로 제거한다. 자바에서는 가비지를 제거하기 위해 별도 코드를 작성할 필요도 없고, 할 수도 없다.
JVM Stack
- 각 스레드마다 하나씩 존재하며 스레드가 시작될 때 할당된다.
- 추가적으로 스레드를 생성하지 않았다면 main 스레드만 존재하므로 JVM 스텍도 하나이다.
- 메소드를 호출할 때마다 프레임을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop)한다.
- 예외 발생 시
printStackTrace()
메소드로 보여주는 Stack Trace의 각 라인은 하나의 프레임을 표현한다. 프레임 내부에는 로컬 변수 스택이 있다. 변수가 추가(push)되거나 제거(pop)된다. 변수는 초기화가 될 때 이 영역에 생성된다.
- 변수는 선언된 블록 안에서만 스택에 존재하고 블록을 벗어나면 스택에서 제거된다.
1
2
3
4
5
6
7
8
9
char v1 = 'A'; //stack: v1
if (v1 == 'A'){
int v2 = 100;
double v3 = 3.14; //stack: v3 v2 v1
}
//stack: v1
boolean v4 = true; //stack: v4 v1
- 참조 타입 변수는 값이 아니라 힙 영역이나 메소드 영역의 객체 주소를 가진다.
참조 변수의 ==, != 연산
- 참조타입 변수 간의 ==, != 연사는 동일한 객체를 참조하는지, 다른 객체를 참조하는 지 알아볼 때 사용된다. 즉 동일한 주소값을 갖고 있는지, 다른 주소값을 갖고 있는지를 알아본다.
null과 NullPointerException
- 참조 타입 변수는 힙 영역의 객체를 참조하지 않는다는 뜻으로 null 값을 가질 수 있다.
- null로 초기화된 참조 변수는 스택 영역에 생성된다.
refVar == null
,refVar != null
로 확인 가능- NullPointer Exception
- 참조변수를 사용하면서 가장 많이 발생하는 예외 중 하나
- 참조 타입 변수가 null가지고 있을 경우, 사용할 수 없다.
- 참조 타입 변수를 사용한다는 것은 곧 객체를 사용하는 것을 의미하는데, 참조할 객체가 없으므로 사용할 수가 없는 것이다.
1
2
3
4
5
int[] intArray = null;
intArray[0] = 10; //NullPointerException
String str = null;
System.out.println("총 문자 수: " + str.length()); // NullPointerException
- 프로그램 실행 도중 NullPointerException이 발생하면, 예외가 발생된 곳에서 객체를 참조하지 않은 상태로 참조 타입 변수를 사용하고 있다는 뜻이다.
배열 타입
배열이란?
- 같은 타입의 데이터를 연속된 공간에 나열시키고, 각 데이터에 인덱스(index)를 부여해 놓은 자료구조
한 번 생성된 배열은 길이를 늘리거나 줄일 수 없다.
- 선언 방법:
타입[] 변수
.타입 변수[]
값 목록으로 배열 생성
- 배열 변수를 미리 선언한 후에 다른 실행문에서 중괄호를 사용해 배열을 생성할 수 없다.
- 따라서 배열 변수를 미리 선언한 후 값 목록들이 나중에 결정되는 상황이라면 new 연산자를 사용해서 값 목록을 지정해준다.
1
2
3
4
타입[] 변수;
변수 = { 값0, 값1, 값2 }; //컴파일 에러
변수 = new 타입[] { 값0, 값1, 값2 }; // OK!
- 메소드의 매개값이 배열일 경우에도 마찬가지로 new 연산자를 사용하여 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ArrayCreateByValueListExample2 {
public static void main(String[] args) {
int[] scores;
int sum = add(new int[] {83, 90, 87});
System.out.println("총합: " + sum);
}
public static int add(int[] scores) {
int sum = 0;
for (int score : scores)
sum += score;
return sum;
}
}
new 연산자로 배열 생성
new 연산자로 배열을 처음 생성할 경우, 배열은 자동적으로 기본값으로 초기화된다.
배열의 초기값
분류 | 데이터 타입 | 초기값 |
---|---|---|
기본 타입 (정수) | byte[] char[] short[] int[] long[] | 0 ‘\u0000’ 0 0 0L |
기본 타입 (실수) | float[] double[] | 0.0F 0.0 |
기본 타입 (논리) | boolean[] | false |
참조 타입 | 클래스[] 인터페이스[] | null null |
배열 길이
- 코드에서 배열의 길이를 얻으려면 배열 객체의 length 필드를 읽으면 된다.
length 필드는 읽기 전용 필드이므로 값을 바꿀 수 없다.
- length필드는 for 문을 사용해서 배열 전체를 루핑할 때 유용하다.
커맨드 라인 입력
- “java 클래스”로 프로그램을 실행하면 JVM은 길이가 0인 String 배열을 먼저 생성하고
main()
메서드를 호출할 때 매개값으로 전달한다.
1
java 클래스 문자열0 문자열1 문자열2 문자열3
1
2
3
String[] args = { 문자열0, 문자열1, 문자열2, 문자열3 };
public static void main(String[] args) {} //main() 메소드 호출 시 매개값으로 전달
- main() 메소드는 String[] args 매개 변수를 통해서 커맨드 라인에서 입력된 데이터의 수(배열의 길이)와 입력된 데이터(배열의 항목 값)을 알게 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainStringArrayArgument {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("프로그램의 사용법");
System.out.println("java MainStringArrayArgument num1 num2");
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);
}
}
1
2
int 변수 = Integer.parseInt("정수로 변환 가능한 문자열");
// 정수로 변환할 수 없는 문자열이 주어졌을 경우 NumberFormatException
다차원 배열
- 값들이 행과 열로 구성된 배열을 2차원 배열이라고 한다.
1
2
3
4
5
int[][] scores = new int[2][3];
scores.length; //2
scores[0].length; //3
scores[1].length; //3
- 이 코드는 세 개의 배열 객체를 생성한다
- 길이 2인 배열객체: 다른 2개의 객체의 주소 저장
- 길이 3인 배열객체 2개
- 자바는 일차원 배열이 서로 연결된 구조로 다차원 배열을 구현한다.
- 행렬 구조가 아닌 계단식 구조를 가질 수 있다.
1
2
3
4
5
6
7
int[][] scores = new int[2][];
scores[0] = new int[2];
scroes[1] = new int[3];
scores.length; //2
scores[0].length; //2
scores[1].length; //3
- 그룹화된 값 목록을 가지고 있다면 중괄호 안에 중괄호를 사용해서 값 목록을 나열한다.
1
타입[][] 변수 = { {값1, 값2}, {값1, 값2} };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ArraysInArrayExample {
public static void main(String[] args) {
int[][] mathScores = new int[2][3];
for (int i = 0; i < mathScores.length; i++) {
for (int k = 0; k < mathScores[i].length; k++) {
System.out.println("mathScores[" + i + "]" + "[" + k + "]" + " = " + mathScores[i][k]);
}
}
System.out.println();
int[][] englishScores = new int[2][];
englishScores[0] = new int[2];
englishScores[1] = new int[3];
for (int i = 0; i < englishScores.length; i++) {
for (int k = 0; k < englishScores[i].length; k++) {
System.out.println("englishScores[" + i + "]" + "[" + k + "] = " + englishScores[i][k]);
}
}
System.out.println();
int[][] javaScores = { {95, 80}, {92, 96, 80} };
for (int i = 0; i < javaScores.length; i++) {
for (int k = 0; k < javaScores[i].length; k++) {
System.out.println("javaScores[" + i + "]" + "[" + k + "] = " + javaScores[i][k]);
}
}
System.out.println();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//결과
mathScores[0][0] = 0
mathScores[0][1] = 0
mathScores[0][2] = 0
mathScores[1][0] = 0
mathScores[1][1] = 0
mathScores[1][2] = 0
englishScores[0][0] = 0
englishScores[0][1] = 0
englishScores[1][0] = 0
englishScores[1][1] = 0
englishScores[1][2] = 0
javaScores[0][0] = 95
javaScores[0][1] = 80
javaScores[1][0] = 92
javaScores[1][1] = 96
객체를 참조하는 배열
배열 복사
for문 사용
1
2
3
4
5
6
7
8
9
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];
}
}
System.arraycopy() 사용
1
System.arraycopy(Object src, int srcPros, Object dest, int destPos, int length);
src
:원본배열,srcPos
: 원본배열에서 복사할 항목의 시작 인덱스,dest
: 새 배열,destPos
: 새 배열에서 붙여넣을 시작 인덱스,length
: 복사할 개수- arr1의 모든 항목을 arr2에 복사할 때
1
System.arraycopy(arr1, 0, arr2, 0, arr1.length)
- 참조 타입 배열의 경우, 배열 복사가 되면 복사되는 값이 객체의 번지이므로 새 배열의 항목은 이전 배열의 항목이 참조하는 객체와 동일하다. 이것을 shallow copy라고 한다.
- 반대로 deep copy는 참조하는 객체도 별도로 생성하는 것을 말한다.
향상된 for문
1
for ( (2)타입변수 : (1)배열 ) { (3)실행문 }
- (1)배열에서 가져올 첫 번째 값이 존재하는 지 평가한다.
- 가져올 값이 존재하면 해당 값을 (2)변수에 저장한다.
- (3)실행문을 실행한다.
- 블록 내부의 실행문이 모두 실행되면 다시 루프를 돌아 배열에서 가져올 다음 값이 존재하는지 평가한다.
- 값이 존재하면 2 -> 3 -> 1로 다시 진행하고, 가져올 다음 항목이 없으면 for문은 종료된다.
해당 값을 변수에 저장하는 것이다. 따라서 기본 타입의 배열이라면 변수에 값을 할당해서 배열의 값을 바꿀 수 없다.
열거 타입
- 데이터 중에는 몇 가지로 한정된 값만 갖는 경우가 있다. (요일/계절 데이터)
- 한정된 값만을 갖는 데이터 타입이 열거 타입(enumeration type)이다.
- 열거 타입은 몇 개의 열거 상수(enumeration constant) 중에서 하나의 상수를 저장하는 데이터타입이다.
열거 타입 선언
- 열거 타입 이름은 파스칼 케이스(Week, MemberGrade..)로 작성한다.
1
public enum 열거타입이름 { ... }
- 열거 상수는 열거 타입의 값으로 사용되는데, 모두 대문자로 작성한다.
- 열거 상수가 여러 단어로 구성될 경우 단어 사이를 밑줄(_)로 연결한다.
1
2
public enum Week { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY ... }
public enum LoginResult { LOGIN_SUCESS, LOGIN_FAILED }
열거 타입 변수
- 열거 상수는 단독으로 사용할 수 없고 반드시
열거타입.열거상수
로 사용된다. - 참조 타입이기 때문에 열거 타입 변수는 null을 저장할 수 있다.
1
2
열거타입 변수; //선언
열거타입 변수 = 열거타입.열거상수; //저장
1
2
3
Week reservationDay;
Week today = Week.SUNDAY;
Week birthday = null;
- 참조 타입 변수는 객체를 참조하는 변수라고 알고 있는데, 그렇다면 열거 상수도 객체일까? 그렇다. 열거 상수는 열거 객체로 생성된다. 열거 타입 Week의 경우 MONDAY부터 SUNDAY까지의 열거 상수는 총 7개의 Week 객체로 생성된다. 그리고 메소드 영역에 생성된 열거 상수가 해당 Week 객체를 각각 참조하게 된다.
1
2
Week today = Week.SUNDAY;
today == Week.SUNDAY; // true. 같은 객체를 참조한다.
- 위의 코드의 경우 열거 타입 변수 today는 스택 영역에 생성된다. today에 저장되는 값은 Week.SUNDAY 열거 상수가 참조하는 객체의 번지이다. 따라서 열거 상수 Week.SUNDAY와 today 변수는 서로 값은 Week 객체를 참조하게 된다.
1
2
3
Week week1 = Week.SATURDAY;
Week week2 = Week.SATURDAY;
week1 == week2; // true. 같은 객체를 참조한다.
- 자바는 컴퓨터의 날짜, 요일, 시간을 프로그램에서 사용할 수 있도록 하기 위해 Date, Calendar, LocalDateTime 등의 클래스를 제공한다.
1
2
3
4
5
6
7
8
9
Calendar now = Calendar.getInstance(); //
int year = now.get(Calendar.YEAR); //년
int month = now.get(Calendar.MONTH); //월(1~12)
int week = now.get(Calendar.DAY_OF_MONTH); //일
int hour = now.get(Calendar.DAY_OF_WEEK); //요일(1~7)
int minute = now.get(Calendar.DAY_OF_HOUR); //시간
int minute = now.get(Calendar.DAY_OF_MINUTE); //분
int minute = now.get(Calendar.DAY_OF_SECOND); //초
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class enumWeekExample {
public static void main(String[] args) {
Week today = null;
Calendar cal = Calendar.getInstance();
int week = cal.get(Calendar.DAY_OF_WEEK);
switch (week) {
case 1:
today = Week.SUNDAY;
break;
case 2:
today = Week.MONDAY;
break;
case 3:
today = Week.TUESDAY;
break;
case 4:
today = Week.WEDNESDAY;
break;
case 5:
today = Week.THURSDAY;
break;
case 6:
today = Week.FRIDAY;
break;
case 7:
today = Week.SATURDAY;
break;
}
System.out.println("오늘 요일: " + today);
if (today == Week.SUNDAY) {
System.out.println("일요일에는 축구를 합니다.");
} else {
System.out.println("열심히 자바 공부를 합니다.");
}
}
}
열거 객체의 메소드
- 열거 객체는 열거 상수의 문자열을 내부 데이터로 가지고 있다.
- 모든 열거 타입은 컴파일 시 java.lang.Enum 클래스를 상속하게 되어 있기 때문에 열거 객체에서 java.lang.Enum 클래스의 메소드를 사용할 수 있다.
name()
- 열거 객체가 가지고 있는 문자열을 리턴한다. 리턴되는 문자열은 열거 타입을 정의할 때 사용한 상수 이름과 동일하다.
1
2
Week today = Week.SUNDAY;
String name = today.name();
ordinal()
전체 열거 객체 중 몇 번째 열거 객체인지 알려준다.
열거 객체의 순번은 열거 타입을 정의할 때 주어진 순번을 말하는데, 0부터 시작한다.
1
2
Week today = Week.SUNDAY;
int ordinal = today.ordinal(); //6
compareTo()
- 매개값으로 주어진 열거 객체를 기준으로 전후로 몇 번째 위치하는 지 비교
- 열거 객체가 매개값의 열거 객체보다 순번이 빠르다면 음수, 늦다면 양수 리턴
- 즉 상대적 위치를 리턴한다.
1
2
3
4
Week day1 = Week.MONDAY;
Week day2 = Week.WEDNESDAY;
int result1 = day1.comparedTo(day2); //-2
int result2 = day2.comparedTo(day1); //2
valueOf()
- 매개값으로 주어지는 문자열과 동일한 문자열을 가지는 열거 객체를 리턴한다.
- 외부로부터 문자열을 입력받아 열거 객체로 변환할 때 유용하게 사용할 수 있다.
1
2
Week weekDay = Week.valueOf("SATURDAY");
//weekDay 변수는 Week.SATURDAY 열거 객체를 참조하게 된다.
values()
- 열거 타입의 모든 열거 객체들을 배열로 만들어 리턴한다.
1
2
3
4
Week[] days = Week.values();
for(Week day : days) {
System.out.println(day);
}
확인 문제
- 7번: 배열의 항목에서 최대값 구하기
1
2
3
4
5
6
7
8
9
10
11
public class Exercise07 {
public static void main(String[] args) {
int max = 0;
int[] array = {1, 5, 3, 8, 2};
for (int i = 0; i < array.length; i++) {
if (max < array[i])
max = array[i];
}
System.out.println("max: " + max);
}
}
- 8번: 다차원 배열 전체 항목의 합과 평균값 구하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Exercise08 {
public static void main(String[] args) {
int[][] array = { {95, 86}, {83, 92, 96}, {78, 83, 93, 87, 88} };
int sum = 0;
double avg = 0.0;
int count = 0;
for (int i = 0; i < array.length; i++) {
for (int k = 0; k < array[i].length; k++) {
sum += array[i][k];
count++;
}
}
avg = sum / (double) count;
System.out.println("sum: " + sum);
System.out.println("avg: " + avg);
}
}
- 9번 키보드로 학생 수와 각 학생드르이 점수를 입력받아서, 최고 점수 및 평균 점수를 구하는 프로그램
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Exercise09 {
public static void main(String[] args) {
boolean run = true;
int studentNum = 0;
int[] scores = null;
Scanner scanner = new Scanner(System.in);
while (run) {
System.out.println("-------------------");
System.out.println("1.학생수 | 2.점수입력 | 3.점수리스트 | 4.분석 | 5.종료");
System.out.println("-------------------");
System.out.print("선택> ");
int selectNo = scanner.nextInt();
if (selectNo == 1) {
System.out.print("학생수> ");
studentNum = scanner.nextInt();
scores = new int[studentNum];
} else if (selectNo == 2) {
for (int i = 0; i < scores.length; i++) {
System.out.print("scores[" + i + "]> ");
scores[i] = scanner.nextInt();
}
} else if (selectNo == 3) {
for (int i = 0; i < scores.length; i++)
System.out.println("scores[" + i + "]> " + scores[i]);
} else if (selectNo == 4) {
int max = 0;
int sum = 0;
for (int i = 0; i < scores.length; i++) {
if (max < scores[i])
max = scores[i];
sum += scores[i];
}
System.out.println("최고 점수: " + max);
System.out.println("평균 점수: " + sum / (double) scores.length);
} else if (selectNo == 5) {
run = false;
}
}
System.out.println("프로그램 종료");
scanner.close();
}
}