프로그래밍 언어들

코스 전체목록

닫기

참조

복제

전자화된 시스템의 가장 중요한 특징은 복제다. 현실의 사물과 다르게 전자화된 시스템 위의 데이터를 복제 하는데는 비용이 거의 들지 않는다. 바로 이러한 특징이 소프트웨어를 기존의 산업과 구분하는 가장 큰 특징일 것이다. 프로그래밍에서 복제가 무엇인가를 살펴보자.

package org.opentutorials.javatutorials.reference;

public class ReferenceDemo1 {

    public static void runValue(){
		int a = 1;
		int b = a;
		b = 2;
		System.out.println("runValue, "+a);	
	}

	public static void main(String[] args) {
		runValue();
	}

}

결과

runValue, 1

결과는 당연하다. 값을 변경한 것은 변수 b이기 때문에 변수 a에 담겨있는 값은 그대로이다. 변수 b의 값에 변수 a의 값이 복제된 것이다. 이를 그림으로 표시하면 아래와 같다.

 

참조

그런데 자연의 산물이 아니라 거대한 약속의 집합인 소프트웨어의 세계에서 당연한 것은 없다. 이것이 당연하지 않은 이유는 다음 예제를 통해서 좀 더 분명하게 드러난다.

package org.opentutorials.javatutorials.reference;
class A{
    public int id;
    A(int id){
		this.id = id;
	}
}
public class ReferenceDemo1 {

	public static void runValue(){
		int a = 1;
		int b = a;
		b = 2;
		System.out.println("runValue, "+a);	
	}
	
	public static void runReference(){
		A a = new A(1);
		A b = a;
		b.id = 2;
		System.out.println("runReference, "+a.id);		
	}

	public static void main(String[] args) {
		runValue();
		runReference();
	}

}

차이점

결과

runValue, 1
runReference, 2

이 코드의 주인공은 아래와 같다.

b.id = 2;
System.out.println("runReference, "+a.id);	

놀라운 차이점이 있다. 변수 b에 담긴 인스턴스의 id 값을 2로 변경했을 뿐인데 a.id의 값도 2가 된 것이다. 이것은 변수 b와 변수 a에 담긴 인스턴스가 서로 같다는 것을 의미하다. 참조(reference)의 세계에 온 것을 환영한다.

앞서 필자는 전자화된 세계에서 가장 중요한 특징으로 복제를 들었다. 그런데 복제만으로 전자화된 시스템을 설명하는 것은 조금 부족하다. 비유하자면 복제는 파일을 복사하는 것이고 참조는 심볼릭 링크(symbolic link) 혹은 바로가기(윈도우)를 만드는 것과 비슷하다. 원본 파일에 대해서 심볼릭 링크를 만들면 원본이 수정되면 심볼릭 링크에도 그 내용이 실시간으로 반영되는 것과 같은 효과다. 심볼릭 링크를 통해서 만든 파일은 원본 파일에 대한 주소 값이 담겨 있다. 누군가 심볼릭 링크에 접근하면 컴퓨터는 심볼릭 링크에 저장된 원본의 주소를 참조해서 원본의 위치를 알아내고 원본에 대한 작업을 하게 된다. 다시 말해서 원본을 복제한 것이 아니라 원본 파일을 참조(reference)하고 있는 것이다. 덕분에 저장 장치의 용량을 절약할 수 있고, 원본 파일을 사용하고 있는 모든 복제본이 동일한 내용을 유지할 수 있게 된다. 참조는 전자화된 세계의 극치라고 할 수 있다.

프로그래밍에서 광범위하게 사용하는 라이브러리라는 개념도 일종의 참조라고 할 수 있다. 공용 라이브러리를 사용하게 되면 하나의 라이브러리를 여러 애플리케이션에서 공유해서 사용하게 된다. 라이브러리의 내용이 변경되면 이를 참조하고 있는 애플리케이션에도 내용이 반영되게 된다. 또 우리가 변수를 사용하는 이유도 말하자면 참조를 위해서라고 할 수 있을 것이다. 본질을 파악하면 이해력도 높아지고 암기할 것도 줄어든다.

아래 두 개의 구문의 차이점을 생각해보자.

int a = 1;
A a = new A(1);

무엇일까? 전자는 데이터형이 int이고 후자는 A이다. int는 기본 데이터형(원시 데이터형, Primitive Data Types)이다. 자바에서는 기본 데이터형을 제외한 모든 데이터 타입은 참조 데이터형(참조 자료형)이라고 부른다. 기본 데이터형은 위와 같이 복제 되지만 참조 데이터형은 참조된다. new를 사용해서 객체를 만드는 모든 데이터 타입이 참조 데이터형이라고 생각해도 된다. (단 String은 제외다) 이를 그림으로 나타내면 아래와 같다.

정리하면 변수에 담겨있는 데이터가 기본형이면 그 안에는 실제 데이터가 들어있고, 기본형이 아니면 변수 안에는 데이터에 대한 참조 방법이 들어있다고 할 수 있다.

참조 데이터 형과 매개 변수

그럼 일종의 변수할당이라고 할 수 있는 메소드의 매개변수는 어떻게 동작하는가를 살펴보자. 조금 복잡하므로 꼼꼼하게 살펴봐야 한다. 예제를 보자.

package org.opentutorials.javatutorials.reference;

public class ReferenceParameterDemo {
    
	static void _value(int b){
		b = 2;
	}
	
	public static void runValue(){
		int a = 1;
		_value(a);
		System.out.println("runValue, "+a);
	}
	
	static void _reference1(A b){
		b = new A(2);
	}
	
	public static void runReference1(){
		A a = new A(1);
		_reference1(a);
		System.out.println("runReference1, "+a.id);		
	}
	
	static void _reference2(A b){
		b.id = 2;
	}

	public static void runReference2(){
		A a = new A(1);
		_reference2(a);
		System.out.println("runReference2, "+a.id);		
	}
	
	public static void main(String[] args) {
		runValue(); // runValue, 1
		runReference1(); // runReference1, 1
		runReference2(); // runReference2, 2
	}

}

결과는 아래와 같다.

runValue, 1
runReference1, 1
runReference2, 2

위의 예제는 3가지 경우를 설명하고 있다. 하나씩 살펴보자. 

아래 코드는 _value의 매개변수로 기본 데이터형(int)를 전달했다. 

runValue();

메소드 _value의 인자로 a를 전달했다. 인자 a는 매개변수 b가 되어서 _value 안으로 전달되고 있다. _value 안에서 b의 값을 변경했다. _value가 실행된 후에 runValue에서 a값을 출력해본 결과 값이 변경되지 않았다. 호출된 메소드의 작업이 호출한 메소드에 영향을 미치지 않고 있다. 어찌보면 자연스러운 결과다.

두번째 코드를 보자. 아래 코드는 _reference1의 매개변수로 참조 데이터 타입을 전달하고 있다. 

runReference1(); 

메소드 _reference1 안에서 매개변수 b의 값을 다른 객체로 변경하고 있다. 이것은 지역변수인 b의 데이터를 교체한 것일 뿐이기 때문에 runReference1의 결과에는 영향을 미치지 않는다. 

그런데 다음의 코드는 호출된 메소드의 작업이 호출한 메소드의 변수에 영향을 미친다. 

runReference2();

매개변수 b의 값을 다른 객체로 교체한 것이 아니라 매개변수 b의 인스턴스 변수 id 값을 2로 변경하고 있다. 이러한 맥락에서 _reference2의 변수 b는 runReference2의 변수 a와 참조 관계로 연결되어 있는 것이기 때문에 a와 b는 모두 같은 객체를 참조하고 있는 변수다.

매개변수를 다른 객체로 변경하는 것과 참조 데이터 타입의 매개변수에 담겨 있는 객체에 접근하는 것은 완전히 다른 의미를 가지기 때문에 두가지 경우의 차이점을 확실하게 이해하도록 하자.

참조

댓글

댓글 본문
  1. wwwqiqi
    완료
  2. Alan Turing
    22/1010
  3. PassionOfStudy
    복습 7일차!
  4. PassionOfStudy
    참조!
  5. 코드파괴자
    22.05.09 Attack Ride. R.R.R.Reference!

    (runreference1(), runreference2() 반복 학습 필요)
  6. 모찌말랑카우
    22.03.02 완료
  7. aesop0207
    22.02.22. Tue
    다시봐야할 듯
  8. 드림보이
    2021.12.29. 참조 파트 수강완료
  9. syh712
    2021-12-15
    <참조>
    1.복제(복사)
    int a = 1;
    int b = a;
    b = 2;

    2. 참조(바로가기)
    A a = new A(1);
    A b = a;
    b.id = 2;
    => 변수 b에 담긴 인스턴스의 id 값을 2로 변경했을 뿐인데 a.id의 값도 2가 됐다. 이것은 변수 b와 변수 a에 담긴 인스턴스가 서로 같다는 의미임.
    "참조는 전자화된 세계의 극치"
    "우리가 변수를 사용하는 이유도 말하자면 참조를 위해서라고 할 수 있을 것"

    3. 데이터타입의 차이
    int a = 1; => 기본데이터형(복제)
    A a = new A(1); => 참조데이터형(참조)
    =>new를 사용해서 객체를 만드는 모든 데이터 타입이 참조 데이터형임.(string 제외)
  10. H4PPY
    1121
  11. IaaS
    2021.11.03 수강완료
  12. 베이스박
    학습완료. 감사합니다.
  13. 오션멍키
    210911 감사합니다
  14. 악어수장
    2021-5-11
  15. 김태현
    감사합니다
    중괄호 아래에서 재 지정한 id값이 클래스 매개변수로 올라가 인스턴스가 받아서 println으로 출력
    데이터 타입은 클래 메서드를 가리키고/ 생성자에서 공부한 내용
  16. 김승민
    2020-05-11
    감사합니다.
  17. 한건희
    참조 4/2번째 '참조란?'이란 강의영상에서 클래스 A의 id 인자값을 1로 명시해주었는데, 2로 출력되는 이유는
    a와 b라는 변수가 각각 데이터값을 저장하는것이아니라, a와 b라는 변수가 A클래스에 인스턴트를 참조하고있으니, b.id나 a.id나 둘다 초기값을 지정해주면 둘다 동시에 데이터가 변경된다는 의미가 맞는건가요? 그렇게때문에 처음 id 인자값을 1로 지정해주었지만 b.id = 2라는 초기값때문에 둘다 데이터값이 변경되어서 2가 출력되는것이구요?
  18. silver94
    감사합니다!
  19. 허공
    감사합니다!
  20. 홍주호
    20191003 완료
  21. doevery
    수강완료
  22. 아롱범
    '기본 데이터형은 위와 같이 복제 되지만 참조 데이터형은 참조된다'

    이번 챕터의 핵심문구라고 생각합니다.
  23. proud00
    잘 봤습니다.
  24. 라또마니
    감사합니다.
  25. andrewjkme
    예제로 든 Class 네임이 A라서, 인스턴스 이름이 a, b 로 정의하는것이 약간 헷갈리는거 같습니다.

    new 가 붙으면 새로운 인스턴스를 만든다고 보시면 될거 같아요.

    A b = a; A라는 데이터 타입의 인스턴스 b가 인스턴스 a를 참조한다
    A b = new A(2); A라는 데이터 타입의 인스턴스 b는 2라는 값은 가지는 새로운 A 데이터 타입의 인스턴스를 생성한다
    대화보기
    • 개미
      ...ㅠㅠㅠ문과는 울고갑니다...이해가 안되는 부분이 있어서 댓글 달아봅니다.
      b=new A(2);에서 결국 그럼 어쨌든 b는 참조 데이터형 A를 갖는다고 이해했고, 그래서 a.id 의 값도 2가 나와야 한다고 생각했는데 틀렸더라구요. 괄호안에 들어가는 매개변수가 다르면 다른 인스턴스가 생성된다고 이해하는게 맞는 건가요..
      ? 설명은 찰떡같이 해주셧는데 못 알아들어서 죄송합니다....ㅠ_ㅜ
    • Daydream
      잘 봤습니다.
      오늘도 화이티입니다.
      20181028
    • 휘송
      @CashCash//
      표현하는 부분에서 약간 정정할 부분이 있어 말씀드립니다. 정확히 이해하고 쓰셨겠지만 혹시 읽으시는 다른 분들이 오해하실까봐 답글 답니다. 설명하신 _reference1 부분에서 A b =a; 가 되어 A b = new A(1);이 된다고 설명하셨는데 엄밀하게 말하면 둘은 서로 다릅니다. 다시 말해서
      A a = new A(1);
      A b = new A(1);
      이 둘은 서로 다른 instance 주소를 참조하게 됩니다. 여기서 'new'를 통해 컴퓨터는 새로운 instance를 위한 메모리 공간을 확보하게 됩니다. 즉 a와 b는 다른 instance 주소를 참조하게 되는 것이죠.

      즉,
      A a = new A(1);
      A b = a; -> (b가 instance A의 메모리 주소를 참조한다.)
      b.id = 2;
      System.out.println(a.id); -> 2 출력

      A a= new A(1); -> id=1인 a instance
      A b= new A(1); -> id=1인 b instance (A b = a; 와는 다릅니다.)
      b.id = 2; (a와는 관계가 없다.)
      System.out.println(a.id); -> 1 출력
      대화보기
      • 전민희
        감사합니다 ㅎ.ㅎ
      • 전하연
        감사합니다
        2018.9.7 20:18
      • 무뇌
        처음으로 댓글달아봅니다...만... 역시는역시네요 항상 잘보고있습니다 정말 감사드립니다!
      • 이태호
        7/17
        참조는 마치 바로 가기 만들기
      • seokhee
        메모장 대박설명입니다. 개념은 알고 있었는데 유레카처럼 바로 이해되네 와;
      • 김진홍
        감사합니다.
      • 하면된다하자
        명강의 감사합니다.
      • ckyuseon
        잘 봤습니다. 감사합니다.
      • Richard Yang
        와 설명짱... 바로가기 .... 와...
      • 촌띄기
        복사랑 바로가기에서 무릎탁
      • Younghun Liam Youn
        잘 봤습니다 :) 감사합니다!
      • GoldPenguin
        감사합니다.
      • J_Project
        심심하니까 보겠습니다!
        감사합니다
      • 지나가다
        복합적으로 적용됩니다.
        일단 _reference1(A b)에서 매개변수(객체의 경우)는 자바에서 call by reference로 동작하므로
        b = a 로 연결고리를 가지고 있습니다.
        그런데 바로 다음 소스에서
        b = new A(2); 로 새로운 객체를 생성하여 할당합니다.
        이렇게 되면 _reference1 메서드 호출 전에 있던 a와의 연결이 끊어지게 됩니다.
        A a = new A(1);
        A b = new A(2);
        위와 같은 결과가 됩니다.
        그런데 매개변수가 가지는 lifesycle(scope)은 _reference1 메서드 내에서만 유효합니다.
        그리고 b에 새로 할당되었던 new A(2) 객체는 메서드가 수행됨과 동시에 더이상 참조되지 않습니다.
        그래서, System.out.println("runReference1, "+a.id); 에서는 a에 처음 생성되어 할당된 new A(1) 객체가 여전히 유효한 값을 가지게 되므로
        결과값 “1”을 보여주게 됩니다.
        대화보기
        • 엔터군
          이번강의가 다른 코드를 보면서 약간의 혼동이 오던 개념을 깔끔히 정리해준 기분입니다..
          정말 감사합니다....저에게 큰 도움이 되었어요....
          책보고 공부하다가 헤멜때 생활코딩 와서 이고잉님 강의 한번 듣고 깔끔하게 정리되면서..
          책보다 생활코딩을 위주로 공부하게 되버렸습니다...
          이렇게 좋은 강의를 만들어 주셔서 진심으로 감사드립니다..^^
        • CashCash
          @오잉님//
          잘못 이해하고 계신 것 같아 답글드립니다. 영상에서도 볼 수 있으시겠지만 a가 _reference1메소드의 인자로 들어가서 A b = a, 즉 A b = new A(1); 와 같이 됩니다. 다음과 같은 상황인거죠.
          A a = new A(1);
          A b = new A(1);
          그런데 _reference1 메소드에서 참조변수 b 에 새로운 객체를 생성해서 담고 있으니 (b = new A(2);) 다음과 같은 상황으로 바뀌게 됩니다.
          A a = new A(1);
          A b = new A(2);
          참조변수 a와 b가 참조하는 객체가 서로 다르게 되는 것이죠.
          따라서 runReference1 메소드에서 a.id를 출력하는 경우 1이 출력되게 됩니다.
          비슷한 방법으로 _reference2 메소드와 runReference2 메소드도 관찰하시면 됩니다.

          참고로 처음의 경우 <a가 _reference1메소드의 인자로 들어가서 A b = a, 즉 A b = new A(1); 와 같이 된다>는 것은 참조변수 a에 들어 있는 참조값이 참조변수 b로 복제된다(본 강의의 단어를 빌려 쓰자면)고 이해하시면 됩니다. 이 경우 값이 복사되는 것이고 자바는 항상 call by value 라고 생각하시면 됩니다.
          대화보기
          • 진격의숑숑
            감사합니다
          • 라떼
            헷갈리네요 ㅠ 감사합니다!
          • 오잉
            runReference1() 메소드가 헷갈리네요.

            new A(1) 인스턴스를 참조하고있던 변수 a가 _reference1(a) 메소드를 통해서 인자로 들어가고,
            거기서 new A(2) 인스턴스를 새롭게 참조하고 있는데 그렇게 되면 현재 참조하고있지 않는
            new A(1) 인스턴스는 소멸되어야 하지 않나요?

            오히려 기본형 데이터타입처럼 갑자기 _reference1 메소드 내에 new A(2) 인스턴스를 참조하는
            새로운 변수가 뜬금없이 생성 된 것과 그로인해 출력 된 1 이라는 결과가 좀 이해하기 어렵습니다.

            혹시 new A(1) 인스턴스를 참조 중인 a 변수가_reference1 메소드의 인자로 전달된 후
            값자기 new A(2) 라는 새로운 인스턴스를 만나면서 지역변수화 된 것은 아닐까요?
            _reference1 내에서만 사용되는 지역변수요...

            알쏭달쏭 하네요 ㅠㅠ
          • 짱슈아빠
            이보슈 학창시절에 글케 졸았으면 이젠 그만 집중하는 방법도 좀 길러보슈. 개인적으로 졸리면 세수하고 이빨 닦고 다시 보슈.
            대화보기
            • circlestar
              c언어의 포인터랑 비슷한개념이네요
            • supersonic
              새로운 시각을 갖게 해주신 생활코딩에 감사합니다.
            버전 관리
            egoing@gmail.com
            현재 버전
            선택 버전
            graphittie 자세히 보기