1. 기본형, 참조형 - 개념
변수의 데이터 타입을 가장 크게 보면 기본형과 참조형으로 분류할 수 있습니다. 사용하는 값을 변수에 직접 넣을 수 있는 기본형, 그리고 이전 글에서 나온 Student student1과 같이 객체가 저장된 메모리의 위치를 가리키는 참조값을 넣을 수 있는 참조형으로 분류할 수 있습니다.
- 기본형 (Primitive Type) : int, long, double, boolean처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입을 기본형이라고 합니다.
- 참조형 (Reference Type) : Student student1, int[] students와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입을 참조형이라고 합니다. 참조형은 객체 또는 배열에 사용됩니다.
쉽게 이야기해서 기본형 변수에는 직접 사용할 수 있는 값이 들어있지만 참조형 변수에는 위치(참조값)가 들어있습니다.
참조형 변수를 통해서 무엇인가 하려면 결국 참조값을 통해 해당 위치로 이동해야 합니다.
1-1. 기본형 vs 참조형 (기본)
- 기본형은 숫자 10, 20과 같이 실제 사용하는 값을 변수에 담을 수 있습니다. 그래서 해당 값을 바로 사용할 수 있습니다.
- 참조형은 실제 사용하는 값을 변수에 담는 것이 아닙니다. 이름 그대로 실제 객체의 위치(참조, 주소)를 저장합니다. 참조형에는 객체와 배열이 있습니다.
- 객체는 .(dot)을 통해서 메모리 상에 생성된 객체를 찾아가야 사용할 수 있습니다.
- 배열은 [ ]를 통해서 메모리 상에 생성된 배열을 찾아가야 사용할 수 있습니다.
1-2. 기본형 vs 참조형 (계산)
- 기본형은 들어있는 값을 그대로 계산에 사용할 수 있습니다.
- 참조형은 들어있는 참조값을 그대로 사용할 수 없습니다. 주소지만 가지고는 할 수 있는게 없습니다. 주소지에 가야 실체가 있습니다.
밑에서 코드를 통해 자세히 설명드리겠습니다.
기본형 예시
int a = 10, b = 20;
int sum = a + b;
기본형은 변수에 실제 사용하는 값이 들어있습니다. 따라서 +, - 와 같은 연산이 가능합니다.
참조형 예시
Student s1 = new Student();
Student s2 = new Student();
s1 + s2 //오류 발생
참조형은 변수에 객체의 위치인 참조값이 들어있습니다. 참조값은 계산에 사용할 수 없습니다. 따라서 오류가 발생합니다.
물론 다음과 같이 .(dot)을 통해 객체의 기본형 멤버 변수에 접근한 경우에는 연산을 할 수 있습니다.
Student s1 = new Student();
s1.grade = 100;
Student s2 = new Student();
s2.grade = 90;
int sum = s1.grade + s2.grade; //연산 가능
쉽게 이해하는 팁
기본형을 제외한 나머지는 모두 참조형입니다.
기본형은 소문자로 시작합니다. int, long, double, boolean 모두 소문자로 시작합니다.
기본형은 자바가 기본으로 제공하는 데이터 타입입니다. 이러한 기본형은 개발자가 새로 정의할 수 없습니다. 개발자는 참조형인 클래스만 직접 정의할 수 있습니다.
클래스는 대문자로 시작합니다. ex) Student
클래스는 모두 참조형입니다.
* String은 사실 클래스입니다. 따라서 참조형입니다. 그런데 기본형처럼 문자 값을 바로 대입할 수 있습니다. 문자는 매우 자주 다루기 때문에 자바에서 특별하게 편의 기능을 제공합니다. String에 대한 자세한 내용은 밑의 글을 참조하시기 바랍니다.
2. 기본형, 참조형 - 변수 대입
대원칙 : 자바는 항상 변수의 값을 복사해서 대입합니다.
기본형, 참조형 모두 항상 변수에 있는 값을 복사해서 대입합니다. 기본형이면 변수에 들어 있는 실제 사용하는 값을 복사해서 대입, 참조형이면 변수에 들어 있는 참조값을 복사해서 대입합니다.
기본형 대입 예시
int a = 10;
int b = a;
참조형 대입 예시
Student s1 = new Student();
Student s2 = s1;
참조형 대입은 실제 사용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사됩니다. 쉽게 얘기해서 실제 건물이 복사가 되는 것이 아니라 건물의 위치인 주소만 복사되는 것입니다. 따라서 같은 건물을 찾아갈 수 있는 방법이 하나 늘어날 뿐입니다.
밑에서 구체적인 예시를 들어보겠습니다.
2-1. 기본형과 변수 대입
전체코드
package class1;
public class ClassStart1 {
public static void main(String[] args) {
int a = 10;
int b = a;
System.out.println("a = " + a);
System.out.println("b = " + b);
//a 변경
a = 20;
System.out.println("변경 a = 20");
System.out.println("a = " + a);
System.out.println("b = " + b);
//b 변경
b = 30;
System.out.println("변경 b = 30");
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
//출력 결과
a = 10
b = 10
변경 a = 20
a = 20
b = 10
변경 b = 30
a = 20
b = 30
실행 코드
//실행 코드
int a = 10;
int b = a;
System.out.println("a = " + a);
System.out.println("b = " + b);
//출력 결과
a = 10
b = 10
변수의 대입은 변수에 들어 있는 값을 복사해서 대입합니다. 여기서 변수 a 에 들어있는 값 10을 복사해서 변수 b 에 대입합니다. 변수 a 자체를 b에 대입하는 것이 아닙니다.
실행 코드
//실행 코드
//a 변경
a = 20;
System.out.println("변경 a = 20");
System.out.println("a = " + a);
System.out.println("b = " + b);
//출력 결과
변경 a = 20
a = 20
b = 10
변수 a에 값20을 대입했습니다. 따라서 변수 a의 값이 10에서 20으로 변경되었습니다. 변수 b는 아무런 영향을 주지 않습니다.
실행 코드
//실행 코드
//b 변경
b = 30;
System.out.println("변경 b = 30");
System.out.println("a = " + a);
System.out.println("b = " + b);
//출력 결과
a = 20
b = 30
변수 b에 값30을 대입했습니다. 따라서 변수 b의 값이 10에서 30으로 변경되었습니다. 변수 a는 아무런 영향을 주지 않습니다.
여기서 핵심은 int b = a 라고 했을 때 변수에 들어있는 값을 복사해서 전달한다는 점입니다. 따라서 a = 20, b = 30이라고 했을 때 각각 본인의 값만 변경되는 것을 확인할 수 있습니다.
2-2. 참조형과 변수 대입
전체 코드
package ref;
public class Data {
int value;
}
package ref;
public class VarChange2 {
public static void main(String[] args) {
Data dataA = new Data();
dataA.value = 10;
Data dataB = dataA;
System.out.println("dataA 참조값=" + dataA);
System.out.println("dataB 참조값=" + dataB);
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
//dataA 변경
dataA.value = 20;
System.out.println("변경 dataA.value = 20");
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
//dataB 변경
dataB.value = 30;
System.out.println("변경 dataB.value = 30");
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
}
}
//출력 결과
dataA 참조값=class1.Data@4e50df2e
dataB 참조값=class1.Data@4e50df2e
dataA.value = 10
dataB.value = 10
변경 dataA.value = 20
dataA.value = 20
dataB.value = 20
변경 dataB.value = 30
dataA.value = 30
dataB.value = 30
실행 코드
//실행 코드
Data dataA = new Data();
dataA.value = 10;
Data dataB = dataA;
System.out.println("dataA 참조값=" + dataA);
System.out.println("dataB 참조값=" + dataB);
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
//출력 결과
dataA 참조값=class1.Data@4e50df2e
dataB 참조값=class1.Data@4e50df2e
dataA.value = 10
dataB.value = 10
변수 dataA가 가리키는 인스턴스를 복사하는 것이 아닌 변수에 들어있는 참조값(x001)만 복사해서 전달합니다.
실행 코드
//실행 코드
dataA.value = 20;
System.out.println("dataA = " + dataA.value);
System.out.println("dataB = " + dataB.value);
//출력 결과
dataA.value = 20
dataB.value = 20
dataA.value = 20 코드를 실행하면 dataA가 가리키는 x001 인스턴스의 value값을 10에서 20으로 변경합니다. 그런데 dataA와 dataB는 같은 x001 인스턴스를 참조하기 때문에 둘 다 같은 값인 20을 출력합니다.
실행 코드
//실행 코드
dataB.value = 30;
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
//출력 결과
dataA.value = 30
dataB.value = 30
이 코드도 위와 마찬가지로 x001 인스턴스의 value 값을 20에서 30으로 변경합니다. 그래서 dataA와 dataB는 같은 인스턴스를 참조하므로 둘 다 30이라는 값이 출력됩니다.
여기서 핵심은 Data dataB = dataA 라고 했을 때 변수에 들어있는 값을 복사해서 사용한다는 점입니다. 그런데 그 값이 참조값입니다. 따라서 dataA와 dataB는 같은 참조값을 가지게 되고, 두 변수는 같은 객체 인스턴스를 참조하게 됩니다.
3. 기본형, 참조형 - 메서드 호출
메서드 호출할 때 사용하는 매개변수(파라미터)도 결국 변수일 뿐입니다. 따라서 메서드를 호출할 때 매개변수에 값을 전달하는 것도 앞서 설명한 내용과 같이 값을 복사해서 전달해줍니다.
*매개변수에 대한 내용을 모르신다면 참조하시길 바랍니다.
3-1. 기본형과 메서드 호출
전체 코드
//실행 코드
package ref;
public class MethodChange1 {
public static void main(String[] args) {
int a = 10;
System.out.println("메서드 호출 전: a = " + a);
changePrimitive(a);
System.out.println("메서드 호출 후: a = " + a);
}
static void changePrimitive(int x) {
x = 20;
}
}
//출력 결과
메서드 호출 전: a = 10
메서드 호출 후: a = 10
- int x 에 변수 a의 값 10을 전달합니다. 따라서 변수 a, x 각각 숫자 10을 갖고 있습니다.
- 메서드 안에서 x = 20으로 새로운 값을 대입합니다. 결과적으로 x의 값만 20으로 변경되고, a의 값은 10으로 유지됩니다.
- 메서드 종료후 값을 확인해보면 a는 10이 출력되는 것을 확인할 수 있습니다. 참고로 메서드가 종료되면 매개변수 x는 제거됩니다.
3-2. 참조형과 메서드 호출
전체 코드
package ref;
public class Data {
int value;
}
//실행 코드
package ref;
public class MethodChange2 {
public static void main(String[] args) {
Data dataA = new Data();
dataA.value = 10;
System.out.println("메서드 호출 전: dataA.value = " + dataA.value);
changeReference(dataA);
System.out.println("메서드 호출 후: dataA.value = " + dataA.value);
}
static void changeReference(Data dataX) {
dataX.value = 20;
}
}
//출력 결과
메서드 호출 전: dataA.value = 10
메서드 호출 후: dataA.value = 20
- 자바에서 변수에 값을 대입하는 것은 항상 값을 복사해서 대입합니다. 변수 dataA는 참조값 x001을 가지고 있으므로 참조값을 복사해서 전달했습니다. 따라서 dataA, dataX 둘다 같은 참조값인 x001을 갖게 됩니다. 이제 dataX를 통해서도 x001에 있는 Data 인스턴스에 접근할 수 있습니다.
- dataX를 통해 x001 인스턴스에 접근하고 그 안에 있는 value 값을 20으로 변경했습니다. 따라서 dataA.value와 dataX.value는 둘 다 20이라는 값을 갖습니다.
3-2. 참조형과 메서드 호출 총정리
- 기본형 : 메서드로 기본형 데이터를 전달하면, 해당 값이 복사되어 전달됩니다. 이 경우, 메서드 내부에서 매개변수(파라미터)의 값을 변경해도, 호출자의 변수 값에는 영향이 없습니다.
- 참조형 : 메서드로 참조형 데이터를 전달하면, 참조값이 복사되어 전달됩니다. 이 경우, 메서드 내부에서 매개변수(파라미터)로 전달된 객체의 멤버 변수를 변경하면, 호출자의 객체도 변경됩니다.
'Language > Java' 카테고리의 다른 글
[Java] 생성자 (0) | 2023.12.18 |
---|---|
[Java] 절차지향, 객체 지향 프로그래밍 (0) | 2023.12.17 |
[Java] 변수와 초기화 (0) | 2023.12.17 |
[Java] 인자, 매개변수 (1) | 2023.12.14 |
[Java] 클래스, 객체, 인스턴스 (2) | 2023.12.11 |