Java 공부 14일차(기본형과 참조형이란?)1편

임선구·2025년 1월 15일

몸 비틀며 Java

목록 보기
15/58

오늘의 잔디 점점 심어지고 있다 1년동안 열심히 심어보자자자잦자!!!!!


오늘의 공부 클라이밍 가는 날이라 조금이지만 했다!!


기본형 vs 참조형1 - 시작

자바에서 참조형을 제대로 이해하는 것은 정말 중요하다.

지금까지 기본형과 참조형에 대해서 조금씩 보았다. 이번에는 기본형과 참조형에 대해서 더 깊이있게 알아보고 확실하
게 정리해보자.

변수의 데이터 타입을 가장 크게 보면 기본형과 참조형으로 분류할 수 있다. 사용하는 값을 변수에 직접 넣을 수 있는 기본형, 그리고 이전에 본 Student student1 과 같이 객체가 저장된 메모리의 위치를 가리키는 참조값을 넣을 수 있는 참조형으로 분류할 수 있다.

  • 기본형(Primitive Type): int , long , double , boolean 처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입을 기본형이라 한다.
  • 참조형(Reference Type): Student student1 , int[] students 와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입을 참조형이라 한다. 참조형은 객체 또는 배열에 사용된다.

쉽게 이야기해서 기본형 변수에는 직접 사용할 수 있는 값이 들어있지만 참조형 변수에는 위치(참조값)가 들어가 있다.
참조형 변수를 통해서 뭔가 하려면 결국 참조값을 통해 해당 위치로 이동해야 한다.

기본형 vs 참조형 - 기본

  • 기본형은 숫자 10 , 20 과 같이 실제 사용하는 값을 변수에 담을 수 있다. 그래서 해당 값을 바로 사용할 수 있다.
  • 참조형은 실제 사용하는 값을 변수에 담는 것이 아니다. 이름 그대로 실제 객체의 위치(참조, 주소)를 저장한다. 참
    조형에는 객체와 배열이 있다.
    • 객체는 . (dot)을 통해서 메모리 상에 생성된 객체를 찾아가야 사용할 수 있다.
    • 배열은 [] 를 통해서 메모리 상에 생성된 배열을 찾아가야 사용할 수 있다.

기본형 vs 참조형 - 계산

  • 기본형은 들어있는 값을 그대로 계산에 사용할 수 있다.
    • 예) 더하고 빼고, 사용하고 등등, (숫자 같은 것들은 바로 계산할 수 있음)
  • 참조형은 들어있는 참조값을 그대로 사용할 수 없다. 주소지만 가지고는 할 수 있는게 없다. 주소지에 가야 실체가
    있다!
    • 예) 더하고 빼고 사용하고 못함! 참조값만 가지고는 계산 할 수 있는 것이 없음!

기본형은 연산이 가능하지만 참조형은 연산이 불가능하다.

int a = 10, b = 20;
int sum = a + b;

기본형은 변수에 실제 사용하는 값이 담겨있다. 따라서 + , - 와 같은 연산이 가능하다.

Student s1 = new Student();
Student s2 = new Student();
s1 + s2 //오류 발생

참조형은 변수에 객체의 위치인 참조값이 들어있다. 참조값은 계산에 사용할 수 없다. 따라서 오류가 발생한다.

물론 다음과 같이 . 을 통해 객체의 기본형 멤버 변수에 접근한 경우에는 연산을 할 수 있다.

Student s1 = new Student();
s1.grade = 100;
Student s2 = new Student();
s2.grade = 90;
int sum = s1.grade + s2.grade; //연산 가능

쉽게 이해하는 팁

기본형을 제외한 나머지는 모두 참조형이다.

  • 기본형은 소문자로 시작한다. int , long , double , boolean 모두 소문자로 시작한다.
    • 기본형은 자바가 기본으로 제공하는 데이터 타입이다. 이러한 기본형은 개발자가 새로 정의할 수 없다. 개발
      자는 참조형인 클래스만 직접 정의할 수 있다.
  • 클래스는 대문자로 시작한다. Student
    • 클래스는 모두 참조형이다.

참고 - String
자바에서 String 은 특별하다. String 은 사실은 클래스다. 따라서 참조형이다. 그런데 기본형처럼 문자 값을 바로 대입할 수 있다. 문자는 매우 자주 다루기 때문에 자바에서 특별하게 편의 기능을 제공한다. String 에 대한 자세한 내용은 뒤에서 설명한다.

기본형 vs 참조형2 - 변수 대입

대원칙: 자바는 항상 변수의 값을 복사해서 대입한다.

자바에서 변수에 값을 대입하는 것은 변수에 들어 있는 값을 복사해서 대입하는 것이다.
기본형, 참조형 모두 항상 변수에 있는 값을 복사해서 대입한다. 기본형이면 변수에 들어 있는 실제 사용하는 값을 복사해서 대입하고, 참조형이면 변수에 들어 있는 참조값을 복사해서 대입한다.

이 대원칙을 이해하면 복잡한 상황에도 코드를 단순하게 이해할 수 있다.

기본형 대입

int a = 10;
int b = a;

참조형 대입

Student s1 = new Student();
Student s2 = s1;

기본형은 변수에 값을 대입하더라도 실제 사용하는 값이 변수에 바로 들어있기 때문에 해당 값만 복사해서 대입한다고
생각하면 쉽게 이해할 수 있다. 그런데 참조형의 경우 실제 사용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사된다. 쉽게 이야기해서 실제 건물이 복사가 되는 것이 아니라 건물의 위치인 주소만 복사되는 것이다. 따라서 같은건물을 찾아갈 수 있는 방법이 하나 늘어날 뿐이다.

구체적인 예시를 통해서 변수 대입시 기본형과 참조형의 차이를 알아보자.

기본형과 변수 대입

다음 코드를 보고 어떤 결과가 나올지 먼저 생각해보자. 너무 쉽고 당연한 내용이지만, 이후에 나올 참조형과 비교를 위해서 다시 한번 정리해보자.

VarChange1

package ref;
public class VarChange1 {
 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

그림을 통해 자세히 알아보자.

실행 결과

a = 10
b = 10

변수의 대입은 변수에 들어있는 값을 복사해서 대입한다. 여기서는 변수 a 에 들어있는 값 10 을 복사해서 변수 b 에 대입한다. 변수 a 자체를 b 에 대입하는 것이 아니다!

a = 20

실행 결과

a = 20
b = 10

변수 a 에 값 20 을 대입했다. 따라서 변수 a 의 값이 10 에서 20 으로 변경되었다. 당연한 이야기지만 변수 b 에는 아무런 영향을 주지 않는다.

b = 30

실행 결과

a = 20
b = 30

변수 b 에 값 30 을 대입했다. 변수 b 의 값이 10 에서 30 으로 변경되었다. 당연한 이야기지만 변수 a 에는 아무런 영향을 주지 않는다.

최종 결과

여기서 핵심은 int b = a 라고 했을 때 변수에 들어있는 값을 복사해서 전달한다는 점이다. 따라서 a=20 , b=30 이라고 했을 때 각각 본인의 값만 변경되는 것을 확인할 수 있다.

너무 당연한 이야기를 왜 이렇게 장황하게 풀어서 하지? 라고 생각한다면 이제 진짜 문제를 만나보자.

참조형과 변수 대입

참조형 예시를 위해 Data 클래스를 하나 만들자. 이 클래스는 단순히 int value 라는 멤버 변수를 하나 가진다.

Data

package ref;
public class Data {
 int value;
}

다음 코드를 보고 어떤 결과가 나올지 먼저 생각해보자. 꼭 먼저 생각해보고 이후에 실행해서 정답을 맞춰보자.

VarChange2

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 참조값=ref.Data@x001
dataB 참조값=ref.Data@x001
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

dataA 변수는 Data 클래스를 통해서 만들었기 때문에 참조형이다. 이 변수는 Data 형 객체의 참조값을 저장한다.
Data 객체를 생성하고, 참조값을 dataA 에 저장한다. 그리고 객체의 value 변수에 값 10 을 저장했다.

Data dataB = dataA

실행 코드

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 = ref.Data@x001
dataB = ref.Data@x001dataA.value = 10
dataB.value = 10

변수의 대입은 변수에 들어있는 값을 복사해서 대입한다. 변수 dataA 에는 참조값 x001 이 들어있다. 여기서는 변수 dataA 에 들어있는 참조값 x001 을 복사해서 변수 dataB 에 대입한다. 참고로 변수 dataA 가 가리키는 인스턴스를 복사하는 것이 아니다! 변수에 들어있는 참조값만 복사해서 전달한다.
이제 dataAdataB 에 들어있는 참조값은 같다. 따라서 둘다 같은 x001 Data 인스턴스를 가리킨다.

dataA.value = 20

실행 코드

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 으로 변경한다. 그런데 dataAdataB 는 같은 x001 인스턴스를 참조하기 때문에 dataA.valuedataB.value 는 둘다 같은 값인 20 을 출력한다.

dataB.value = 30

실행 코드

dataB.value = 30;
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);

출력 결과

dataA.value = 30
dataB.value = 30

dataB.value = 30 코드를 실행하면 dataB 가 가리키는 x001 인스턴스의 value 값을 20 에서 30으로 변경한다. 그런데 dataAdataB 는 같은 x001 인스턴스를 참조하기 때문에 dataA.valuedataB.value 는 같은 값인 30 을 출력한다.
여기서 핵심은 Data dataB = dataA라고 했을 때 변수에 들어있는 값을 복사해서 사용한다는 점이다. 그런데 그
값이 참조값이다. 따라서 dataAdataB 는 같은 참조값을 가지게 되고, 두 변수는 같은 객체 인스턴스를 참조하게 된다.

기본형 vs 참조형3 - 메서드 호출

이번에는 기본형과 참조형이 메서드 호출에 따라서 어떻게 달라지는지 알아보자.

대원칙: 자바는 항상 변수의 값을 복사해서 대입한다.

자바에서 변수에 값을 대입하는 것은 변수에 들어 있는 값을 복사해서 대입하는 것이다.
기본형, 참조형 모두 항상 변수에 있는 값을 복사해서 대입한다. 기본형이면 변수에 들어 있는 실제 사용하는 값을 복사해서 대입하고, 참조형이면 변수에 들어 있는 참조값을 복사해서 대입한다.

메서드 호출도 마찬가지이다. 메서드를 호출할 때 사용하는 매개변수(파라미터)도 결국 변수일 뿐이다. 따라서 메서드
를 호출할 때 매개변수에 값을 전달하는 것도 앞서 설명한 내용과 같이 값을 복사해서 전달한다.

다음 메서드 호출 코드를 보고 어떤 결과가 나올지 먼저 생각해보자. 너무 쉽고 당연한 내용이지만, 이후에 나올 참조형과 비교를 위해서 한번 정리해보자.

기본형과 메서드 호출

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
  1. 메서드 호출

    메서드를 호출할 때 매계변수 x 에 변수 a 의 값을 전달한다. 이 코드는 다음과 같이 해석할 수 있다.
int x = a

자바에서 변수에 값을 대입하는 것은 항상 값을 복사해서 대입한다. 따라서 변수 a , x 각각 숫자 10 을 가지고 있다.

  1. 메서드 안에서 값을 변경

    메서드 안에서 x = 20 으로 새로운 값을 대입한다.
    결과적으로 x 의 값만 20 으로 변경되고, a 의 값은 10 으로 유지된다.

  2. 메서드 종료

    메서드 종료후 값을 확인해보면 a 는 10이 출력되는 것을 확인할 수 있다. 참고로 메서드가 종료되면 매개변수 x 는 제거된다.

참조형과 메서드 호출

이번에는 참조형 변수의 메서드 호출에 대해 알아보자.
다음 메서드 호출 코드를 보고 어떤 결과가 나올지 먼저 생각해보자.

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

Data 인스턴스를 생성하고, 참조값을 dataA 변수에 담고 value 에 숫자 10을 할당한 상태는 다음과 같다.

  1. 메서드 호출

    메서드를 호출할 때 매계변수 dataX 에 변수 dataA 의 값을 전달한다. 이 코드는 다음과 같이 해석할 수 있다.
Data dataX = dataA

자바에서 변수에 값을 대입하는 것은 항상 값을 복사해서 대입한다. 변수 dataA 는 참조값 x001 을 가지고 있으므로 참조값을 복사해서 전달했다. 따라서 변수 dataA , dataX 둘다 같은 참조값인 x001 을 가지게 된다.
이제 dataX 를 통해서도 x001 에 있는 Data 인스턴스에 접근할 수 있다.

  1. 메서드 안에서 값을 변경

    메서드 안에서 dataX.value = 20 으로 새로운 값을 대입한다.참조값을 통해 x001 인스턴스에 접근하고 그 안에 있는 value 의 값을 20 으로 변경했다.
    dataA , dataX 모두 같은 x001 인스턴스를 참조하기 때문에 dataA.valuedataX.value 는 둘다 20 이라는 값을 가진다.

  2. 메서드 종료

    메서드 종료후 dataA.value 의 값을 확인해보면 다음과 같이 20 으로 변경된 것을 확인할 수 있다.

메서드 호출 전: dataA.value = 10
메서드 호출 후: dataA.value = 20

기본형과 참조형의 메서드 호출

자바에서 메서드의 매개변수(파라미터)는 항상 값에 의해 전달된다. 그러나 이 값이 실제 값이냐, 참조(메모리 주소)값이냐에 따라 동작이 달라진다.

  • 기본형: 메서드로 기본형 데이터를 전달하면, 해당 값이 복사되어 전달된다. 이 경우, 메서드 내부에서 매개변수
    (파라미터)의 값을 변경해도, 호출자의 변수 값에는 영향이 없다.
  • 참조형: 메서드로 참조형 데이터를 전달하면, 참조값이 복사되어 전달된다. 이 경우, 메서드 내부에서 매개변수(파라미터)로 전달된 객체의 멤버 변수를 변경하면, 호출자의 객체도 변경된다.
profile
끝까지 가면 내가 다 이겨

0개의 댓글