출처: 권오흠 교수님
Theme. 클래스
한 사람의 "이름"과 "전화번호"는 항상 같이 붙어다녀야 하는 데이터이다.
이 두가지 데이터를 서로 별개의 변수에 저장하면 우리가 이름 데이터를 옮길 때마다 전화번호 데이터도 따로 옮겨줘야 한다.
만약 각 사람에 대해서 이름과 전화번호뿐만 아니라 주소, 이메일 등 여러 가지 데이터를 저장한다면 이 불편은 더 심해질 것이다.
이렇듯 서로 관련있는 데이터들을 하나의 단위로 묶어두면 굉장히 편할 것이다. 이것이 클래스라는 개념이 등장하는 가장 기본적인 이유이다.
전화번호부 프로그램을 작성하면서 클래스에 대해 좀 더 알아보도록 한다.
먼저, 어떤 한 사람의 이름과 전화번호를 하나의 단위로 묶기 위해 "Person1"이라는 클래스를 만들었다.
주의할 것은,
클래스의 이름은 항상 대문자로 시작한다는 것과 클래스의 이름과 소스파일명의 이름이 일치해야한다는 점이다.
위 코드에서 name과 number는 각각 필드(field) 혹은 data member라고 한다.
클래스가 서로 관련 있는 데이터들을 하나의 단위로 묶기 위해 등장했다는 것. 이것이 첫번째 기본이다.
또 하나의 클래스에 대한 기본적인 내용은
클래스도 결국 하나의 "타입(type)"이라는 것이다. 마치, int, double 등 처럼.
다만, int, double처럼 Java가 미리 정해놓은 타입(primitive type, 기본형)이 아니라 사용자가 정의한 새로운 타입이라는 의미에서 "사용자 정의 타입"이라고 부르기도 한다.
만약, 어떤 한 사람의 이름, 전화번호 1쌍의 데이터를 "저장"하고 싶다면?
Person1이라는 클래스에 저장하는 것이 아니라 Person1 타입의 어떤 변수를 만들어 저장해야 한다.
이는 마치 int(기본형 타입)에 4를 저장할 수 없는 것과 같은 논의다.
String 혹은 int 형 변수를 선언하고 사용하는 것처럼 Person1형 변수를 선언하고 사용하는 방법은 무엇일까?
Person1 first; // Person1 타입의 first라는 이름의 변수를 선언
first = new Person1(); // Person1의 객체(Person1 타입의 객체)를 만들고 변수 first가 그 객체의 주소(참조)를 저장
이를 다시 적어보면,
Person1 first = new Person1(); 이다. 이때, new 연산자는 객체를 생성하는 역할을 한다.
클래스명 변수명 = new 클래스();
Theme. 프리미티브 타입과 클래스의 차이점
prmitive type(기본형)의 변수는 변수가 만들어지고 그 "변수 안"에 값이 저장된다.
하지만,
클래스는 변수가 만들어지고, 그 변수에 "생성된 클래스의 객체의 주소(참조)"가 저장된다. 그리고 "객체 안"에 값이 저장된다.
new 연산자를 통해 객체가 생성되고, "=" 연산자를 통해 객체의 주소(참조)가 변수에 저장되는 것. 그 결과 변수가 객체를 참조하게 된다.
그래서 클래스 타입의 변수를 선언하고, 클래스의 객체를 생성하여 변수에 그 객체의 주소를 저장하는 과정을 살펴보면,
다음과 같다.
정리하자면,
모든 프리미티브 타입의 변수는 보통 변수이다. 즉, 변수 자체에 값이 저장된다.
프리미티브 타입이 아닌 모든 변수는 참조 변수이다. 즉, 실제 데이터가 저장될 "객체"는 new 명령으로 따로 만들어야 하고, 참조변수에는 그 객체의 주소를 저장한다.
이어서, 아래 코드를 살펴보자.
위 코드에서 Person1 second = first; 의 의미에 대해 생각해보자.
즉,
Person1 second; // Person1 타입의 second라는 이름의 새로운 참조변수를 선언
second = first; // 무슨 의미??
"=" 연산자의 의미를 생각하면 first라는 변수가 가지고 있는 값을 second라는 변수에 저장한다고 이해할 수 있을 것이다.
이때, first는 참조변수이므로 first가 가지고 있는 값은 "Person1의 객체의 주소"이다.
따라서, first가 가지고 있는 객체의 주소가 second라는 변수에 저장되는 것이다.
결론적으로 참조변수인 first와 second 모두 Person1의 객체를 참조하게 된다.
Person1[] members = new Person1[100]; 에서도
members라는 배열의 각 칸은 Person1 타입의 변수이다. 즉, 참조변수이다.
그러므로 members[0] = first; 가 가능하다.(자료형이 같으므로)
이 식의 의미도 members[0]이 first가 가리키는 객체의 주소를 저장하게 된다는 것이다.
따라서, first, second, members[0], members[1] 모두 동일한 객체(Person1의 객체)를 참조하게 된다.
Theme. 참조변수 그리고 배열
여기서 잠시 "배열"의 자료형에 대해 자세히 알아보자.
int[] numbers = new int[8]; 에서
int[] 는 프리미티브 타입이 아니다! 다시 말해 int[] 타입의 변수인 numbers는 프리미티브 타입(기본형)의 변수가 아니다.
프리미티브 타입의 변수가 아닌 것들은 모두 참조 변수다.
따라서, 참조 변수인 numbers는 new를 통해 생성된 8칸을 가진 배열이 실제로 존재하는 주소를 저장할 뿐이다.
하지만, 배열의 각 칸은 int 타입의 프리미티브 변수이다.
또한, 이전 예제에서
Person1[] members = new Person1[8]; 을 보면
members도 참조 변수, 각 칸도 참조 변수이다.
Person1[] 타입의 배열, Person1 타입의 각 칸 모두 프리미티브 타입의 변수가 아니기 때문이다.
현재 Person1[] members = new Person1[8]; 으로 8칸짜리 members라는 배열이 생성되어 있다.
이때,
members[2].name = "John";
members[2].number = "010232432";
라고 저장하는 것은 불가능하다. 그 이유를 생각하기 위해 위 배열의 각 칸이 참조 변수인 것에 집중해보자.
참조 변수는 그 변수 안에 값을 저장할 수 없고, 값이 저장된 객체의 주소만을 저장할 수 있다.
따라서, 우선적으로 members[2]에 객체의 주소가 저장되어야 한다.
즉, 아래와 같이 작성해야 한다.
members[2] = new Person1(); // Person1의 객체를 생성하고 객체의 주소를 members[2]에 저장
members[2].name = "John";
members[2].number = "010232432";
참조변수와 관련하여 "값에 의한 호출"의 원리를 다시 한번 생각할 수 있다.
예를 들어, 함수를 호출하는 부분에서 bubbleSort(data, count); 라고 호출했다고 가정하자.
이때, data는 배열을 가리키는 참조변수(배열의 이름)이고, count는 int형 변수다.
호출된 메서드 부분에서 public static void bubbleSort(int[] data2, int n); 이라고 작성했다면,
data2를 버블 정렬했을 때, data도 버블 정렬이 됨을 확인할 수 있다. 이는 "값에 의한 호출" 때문인데, 자세히 살펴보면
- data와 data2는 모두 자료형이 int[]인, 즉 참조 변수이다.
- data와 data2가 저장하고 있는 배열(객체)의 주소는 동일하다.
- data2 배열에서 버블 정렬을 하게 되면, 동일한 배열의 주소를 저장하고 있는 data 배열 역시 버블정렬이 된다.
반면에 int형 프리미티브 타입의 변수인 count와 n은 각각 변수 안에 값이 저장되므로, 호출된 메서드에서 n의 값이 변한다 한들, count의 값은 변하지 않는다. 함수가 호출되었을 때, count의 값이 딱 한번! n 안의 값에 복사될 뿐이다.
처음 전화번호부를 만드는 것으로 돌아와서 클래스와 객체를 활용하여 작성한 코드를 살펴보자.
먼저, Person1 클래스는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
|
package Section1;
public class Person1 {
// 전화번호부 프로그램을 만들기 위한 클래스 생성
// Person1 클래스는 한 사람의 이름과 전화번호를 하나의 단위로 묶는다.
public String name; // 각각 field 또는 data member라고 한다.
public String number;
}
|
cs |
각 사람의 이름과 전화번호에 대한 정보가 들어있는 input.txt 파일은
John 237-645-2734
James 0283764823
Lee 2387462
Kim 28734628
Kwon 2738644283
Adam 23784628
의 정보가 들어있다.
이를 입력받아 전화번호부를 만든다면,
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
|
package Section1;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Code02 {
static int n = 0;
static Person1[] members = new Person1[100]; // 배열의 길이 100
public static void main(String[] args) {
try {
Scanner sc = new Scanner(new File("input.txt"));
while (sc.hasNext()) {
String nm = sc.next(); // 이름
String nb = sc.next(); // 전화번호
members[n] = new Person1(); // 배열의 각 칸이 Person1의 객체의 주소를 저장
members[n].name = nm;
members[n].number = nb;
n++;
}
sc.close();
} catch (FileNotFoundException e) {
System.out.println("No data file");
}
for (int i = 0; i < n; i++) {
System.out.println((i+1) + " name: " + members[i].name + "\tnumber: " + members[i].number);
}
}
}
|
cs |
추가적으로 이름의 사전식 순서로 정렬하여 결과를 나타내보면, 다음과 같이 작성할 수 있다. (bubble sort 활용)
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
47
48
49
50
51
52
53
54
55
56
57
|
package Section1;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Code03 {
static Person1[] members = new Person1[100]; // 배열의 길이 100
static int n = 0; // 배열에 저장할 사람 수
public static void main(String[] args) {
try {
Scanner sc = new Scanner(new File("input.txt"));
while (sc.hasNext()) {
String nm = sc.next(); // 이름
String nb = sc.next(); // 전화번호
members[n] = new Person1(); // 배열의 각 칸이 생성된 Person1의 객체의 주소를 저장
members[n].name = nm;
members[n].number = nb;
n++; // 1명을 읽을 때마다 사람 수 증가
}
sc.close();
} catch (FileNotFoundException e) {
System.out.println("No data file");
}
bubbleSort();
for (int i = 0; i < n; i++) {
System.out.println((i + 1) + " name: " + members[i].name + "\tnumber: " + members[i].number);
}
}
// 매개변수가 없는 함수를 사용할 수 있는 이유는,
// 사용하려는 배열과 사람 수가 main 함수가 아닌 클래스에 선언된 변수들이기 때문이다.
private static void bubbleSort() {
for (int i = n - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if(members[j].name.compareToIgnoreCase(members[j+1].name) > 0) {
Person1 tmp = members[j + 1];
members[j + 1] = members[j];
members[j] = tmp;
}
}
}
}
}
|
cs |
'Java > Java로 배우는 자료구조' 카테고리의 다른 글
자료구조 2-2. 메서드와 생성자 (0) | 2023.02.13 |
---|---|
자료구조 2-1. 클래스, 객체, 참조변수(2) (0) | 2023.02.13 |
자료구조 1-3. 문자열 (0) | 2023.02.06 |
자료구조 1-2 메서드 호출과 프로그램의 기능적 분할 (0) | 2023.02.02 |
자료구조 1-1 중 변수 (0) | 2023.01.30 |